package jwt import ( "fmt" "time" "github.com/golang-jwt/jwt/v5" "github.com/google/uuid" ) const ( TokenTypeAuth = "auth" TokenTypeRefresh = "refresh" ) type Claims struct { UserID string `json:"userId"` Username string `json:"username"` TokenType string `json:"tokenType"` jwt.RegisteredClaims } type JWTManager struct { secret []byte authTTL time.Duration refreshTTL time.Duration } func NewJWTManager(secret string, authTTL, refreshTTL time.Duration) *JWTManager { return &JWTManager{ secret: []byte(secret), authTTL: authTTL, refreshTTL: refreshTTL, } } func (m *JWTManager) GenerateAuthToken(userID string, username string) (string, error) { return m.generate(userID, username, TokenTypeAuth, m.authTTL) } func (m *JWTManager) GenerateRefreshToken(userID string, username string) (string, error) { return m.generate(userID, username, TokenTypeRefresh, m.refreshTTL) } func (m *JWTManager) generate(userID string, username, tokenType string, ttl time.Duration) (string, error) { now := time.Now() claims := Claims{ UserID: userID, Username: username, TokenType: tokenType, RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(now), ExpiresAt: jwt.NewNumericDate(now.Add(ttl)), Subject: userID, ID: uuid.NewString(), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString(m.secret) } func (m *JWTManager) ParseAuthToken(tokenStr string) (*Claims, error) { claims, err := m.parse(tokenStr) if err != nil { return nil, err } if claims.TokenType != TokenTypeAuth { return nil, fmt.Errorf("invalid token type") } return claims, nil } func (m *JWTManager) ParseRefreshToken(tokenStr string) (*Claims, error) { claims, err := m.parse(tokenStr) if err != nil { return nil, err } if claims.TokenType != TokenTypeRefresh { return nil, fmt.Errorf("invalid token type") } return claims, nil } func (m *JWTManager) parse(tokenStr string) (*Claims, error) { token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(token *jwt.Token) (any, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method") } return m.secret, nil }) if err != nil { return nil, err } claims, ok := token.Claims.(*Claims) if !ok || !token.Valid { return nil, fmt.Errorf("invalid token") } return claims, nil }