package auth import ( "fmt" "time" "github.com/golang-jwt/jwt/v5" ) const ( defaultAccessTTL = 7200 * time.Second refreshTTL = 30 * 24 * time.Hour ) type JWTManager struct { secret []byte refreshSecret []byte accessTTL time.Duration refreshTTL time.Duration } func NewJWTManager(secret, refreshSecret string) *JWTManager { return &JWTManager{ secret: []byte(secret), refreshSecret: []byte(refreshSecret), accessTTL: defaultAccessTTL, refreshTTL: refreshTTL, } } func (m *JWTManager) GenerateAccessToken(claims Claims) (string, error) { now := time.Now() claims.RegisteredClaims = jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(now.Add(m.accessTTL)), IssuedAt: jwt.NewNumericDate(now), Subject: claims.UserID, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString(m.secret) } func (m *JWTManager) GenerateRefreshToken(userID string) (string, error) { now := time.Now() claims := jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(now.Add(m.refreshTTL)), IssuedAt: jwt.NewNumericDate(now), Subject: userID, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString(m.refreshSecret) } func (m *JWTManager) ParseAccessToken(tokenStr string) (*Claims, error) { token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(t *jwt.Token) (interface{}, error) { if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"]) } return m.secret, nil }) if err != nil { return nil, fmt.Errorf("parse access token: %w", err) } claims, ok := token.Claims.(*Claims) if !ok || !token.Valid { return nil, fmt.Errorf("invalid access token") } return claims, nil } func (m *JWTManager) ParseRefreshToken(tokenStr string) (string, error) { token, err := jwt.ParseWithClaims(tokenStr, &jwt.RegisteredClaims{}, func(t *jwt.Token) (interface{}, error) { if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"]) } return m.refreshSecret, nil }) if err != nil { return "", fmt.Errorf("parse refresh token: %w", err) } claims, ok := token.Claims.(*jwt.RegisteredClaims) if !ok || !token.Valid { return "", fmt.Errorf("invalid refresh token") } return claims.Subject, nil }