package jwt import ( "errors" "os" "time" "github.com/golang-jwt/jwt/v5" ) type Claims struct { UserID uint `json:"user_id"` Email string `json:"email"` UserName string `json:"username,omitempty"` jwt.RegisteredClaims } // GenerateAccessToken creates a short-lived access token (15 minutes). func GenerateAccessToken(userID uint, email, userName string) (string, error) { secret := os.Getenv("JWT_SECRET") claims := Claims{ UserID: userID, Email: email, UserName: userName, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * time.Minute)), IssuedAt: jwt.NewNumericDate(time.Now()), }, } return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(secret)) } // GenerateRefreshToken creates a long-lived refresh token (7 days). func GenerateRefreshToken(userID uint, email, userName string) (string, error) { secret := os.Getenv("JWT_REFRESH_SECRET") claims := Claims{ UserID: userID, Email: email, UserName: userName, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(7 * 24 * time.Hour)), IssuedAt: jwt.NewNumericDate(time.Now()), }, } return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(secret)) } // ValidateToken parses and validates a token string using the provided secret. func ValidateToken(tokenStr, secret string) (*Claims, error) { token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(t *jwt.Token) (interface{}, error) { if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, errors.New("unexpected signing method") } return []byte(secret), nil }) if err != nil { return nil, err } claims, ok := token.Claims.(*Claims) if !ok || !token.Valid { return nil, errors.New("invalid token") } return claims, nil }