Added JWT Refresh token

This commit is contained in:
PB
2025-10-20 20:59:00 +02:00
parent 50b2127bd7
commit b58b07f0ac
4 changed files with 53 additions and 23 deletions

View File

@@ -12,5 +12,8 @@ API_CACHE_PASSWORD=12345678
API_MONGODB_URL=mongodb://mongodb:12345678@mongo-db:27017 API_MONGODB_URL=mongodb://mongodb:12345678@mongo-db:27017
# EVENTBUS_URL=amqp://guest:guest@api-eventbus:5672 # EVENTBUS_URL=amqp://guest:guest@api-eventbus:5672
JWT_SECRET_KEY=SomeFancySecretAndRandomString JWT_ACCESS_TOKEN_SECRET_KEY=SomeFancySecretAndRandomString
JWT_TOKEN_EXPIRE_TIME=1 # hours JWT_ACCESS_TOKEN_EXPIRE_TIME=1 # hours
JWT_REFRESH_TOKEN_SECRET_KEY=SomeFancySecretAndRandomStringAgain
JWT_REFRESH_TOKEN_EXPIRE_TIME=7 # days

View File

@@ -1,8 +1,7 @@
package server package server
import ( import (
"os" baseCnf "git.ego.freeddns.org/egommerce/go-api-pkg/config"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
jwt "github.com/gofiber/jwt/v2" jwt "github.com/gofiber/jwt/v2"
) )
@@ -12,7 +11,7 @@ import (
func JWTProtected() func(*fiber.Ctx) error { func JWTProtected() func(*fiber.Ctx) error {
// Create config for JWT authentication middleware. // Create config for JWT authentication middleware.
config := jwt.Config{ config := jwt.Config{
SigningKey: []byte(os.Getenv("JWT_SECRET_KEY")), SigningKey: []byte(baseCnf.GetEnv("JWT_ACCESS_TOKEN_SECRET_KEY", "FallbackAccessTokenSecret")),
ContextKey: "jwt", // used in private routes ContextKey: "jwt", // used in private routes
ErrorHandler: jwtError, ErrorHandler: jwtError,
} }

View File

@@ -46,12 +46,13 @@ func (a *Auth) Login(login, passwd string) (string, error) {
return "", ErrLoginIncorrect return "", ErrLoginIncorrect
} }
token, _ := jwtSrv.CreateToken(id) accessToken, _ := jwtSrv.CreateAccessToken(id)
if err = a.saveTokenToCache(token, id); err != nil { refreshToken, _ := jwtSrv.CreateRefreshToken(id)
if err = a.saveTokensToCache(accessToken, refreshToken, id); err != nil {
return "", ErrUnableToCacheToken return "", ErrUnableToCacheToken
} }
return token, nil return accessToken, nil
} }
func (a *Auth) Register(email, login, passwd string) (string, error) { func (a *Auth) Register(email, login, passwd string) (string, error) {
@@ -70,10 +71,15 @@ func (a *Auth) Register(email, login, passwd string) (string, error) {
return id, nil return id, nil
} }
func (a *Auth) saveTokenToCache(token, id string) error { func (a *Auth) saveTokensToCache(accessToken, refreshToken, id string) error {
res := a.cache.Set(context.Background(), "auth:token:"+id, token, tokenExpireTime) res := a.cache.Set(context.Background(), "auth:access_token:"+id, accessToken, accessTokenExpireTime)
if err := res.Err(); err != nil { if err := res.Err(); err != nil {
fmt.Println("failed to save token in redis: ", err.Error()) fmt.Println("failed to save access token in redis: ", err.Error())
}
res = a.cache.Set(context.Background(), "auth:refresh_token:"+id, refreshToken, refreshTokenExpireTime)
if err := res.Err(); err != nil {
fmt.Println("failed to save refresh token in redis: ", err.Error())
} }
return nil return nil

View File

@@ -10,33 +10,55 @@ import (
) )
var ( var (
tokenExpireTime time.Duration accessTokenExpireTime time.Duration
refreshTokenExpireTime time.Duration
) )
func init() { func init() {
min, _ := strconv.Atoi(baseCnf.GetEnv("JWT_TOKEN_EXPIRE_TIME", "5")) expAccessTokenTime, _ := strconv.Atoi(baseCnf.GetEnv("JWT_ACCESS_TOKEN_EXPIRE_TIME", "5"))
tokenExpireTime = time.Duration(int(time.Hour) * min) accessTokenExpireTime = time.Duration(int(time.Hour) * expAccessTokenTime) // hours
expRefreshTokenTime, _ := strconv.Atoi(baseCnf.GetEnv("JWT_REFRESH_TOKEN_EXPIRE_TIME", "7"))
refreshTokenExpireTime = time.Duration(int(time.Hour*24) * expRefreshTokenTime) // days
jwtSrv = &JWT{ jwtSrv = &JWT{
tokenExpireTime, accessTokenExpireTime,
[]byte(baseCnf.GetEnv("JWT_SECRET_KEY", "B413IlIv9nKQfsMCXTE0Cteo4yHgUEfqaLfjg73sNlh")), []byte(baseCnf.GetEnv("JWT_ACCESS_TOKEN_SECRET_KEY", "FallbackAccessTokenSecret")),
refreshTokenExpireTime,
[]byte(baseCnf.GetEnv("JWT_REFRESH_TOKEN_SECRET_KEY", "FallbackRefreshTokenSecret")),
} }
} }
type JWT struct { type JWT struct {
tokenExpireTime time.Duration accessTokenExpireTime time.Duration
tokenSecret []byte accessTokenSecret []byte
refreshTokenExpireTime time.Duration
refreshTokenSecret []byte
} }
func (s *JWT) CreateToken(id string) (string, error) { func (s *JWT) CreateAccessToken(id string) (string, error) {
claims := &jwt.StandardClaims{ claims := &jwt.StandardClaims{
Id: id, Subject: id,
ExpiresAt: time.Now().Add(time.Duration(s.tokenExpireTime) * time.Minute).Unix(), IssuedAt: time.Now().Unix(),
ExpiresAt: time.Now().Add(s.accessTokenExpireTime).Unix(),
} }
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(s.tokenSecret) return token.SignedString(s.accessTokenSecret)
}
func (s *JWT) CreateRefreshToken(id string) (string, error) {
claims := &jwt.StandardClaims{
Subject: id,
IssuedAt: time.Now().Unix(),
ExpiresAt: time.Now().Add(s.refreshTokenExpireTime).Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(s.accessTokenSecret)
} }
func (s *JWT) ValidateToken(tokenStr string) error { func (s *JWT) ValidateToken(tokenStr string) error {
@@ -46,7 +68,7 @@ func (s *JWT) ValidateToken(tokenStr string) error {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
} }
return s.tokenSecret, nil return s.accessTokenSecret, nil
}) })
if _, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { if _, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {