Files
rideaware-api/internal/config/jwt.go
2025-11-22 23:06:42 -06:00

125 lines
2.8 KiB
Go

package config
import (
"fmt"
"os"
"time"
"github.com/golang-jwt/jwt/v5"
)
type JWTConfig struct {
SecretKey string
AccessTokenDuration time.Duration
RefreshTokenDuration time.Duration
ResetTokenDuration time.Duration
}
var JWT *JWTConfig
func InitJWT() {
JWT = &JWTConfig{
SecretKey: os.Getenv("JWT_SECRET_KEY"),
AccessTokenDuration: 15 * time.Minute,
RefreshTokenDuration: 7 * 24 * time.Hour,
ResetTokenDuration: 1 * time.Hour,
}
if JWT.SecretKey == "" {
panic("JWT_SECRET_KEY not set in environment")
}
}
type CustomClaims struct {
UserID uint `json:"user_id"`
Email string `json:"email"`
Username string `json:"username"`
TokenType string `json:"token_type"`
jwt.RegisteredClaims
}
func GenerateAccessToken(userID uint, email, username string) (string, error) {
claims := CustomClaims{
UserID: userID,
Email: email,
Username: username,
TokenType: "access",
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(JWT.AccessTokenDuration)),
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: "rideaware",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(JWT.SecretKey))
}
func GenerateRefreshToken(userID uint, email, username string) (string, error) {
claims := CustomClaims{
UserID: userID,
Email: email,
Username: username,
TokenType: "refresh",
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(JWT.RefreshTokenDuration)),
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: "rideaware",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(JWT.SecretKey))
}
func VerifyToken(tokenString string) (*CustomClaims, error) {
claims := &CustomClaims{}
token, err := jwt.ParseWithClaims(
tokenString,
claims,
func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(JWT.SecretKey), nil
},
)
if err != nil {
return nil, err
}
if !token.Valid {
return nil, fmt.Errorf("invalid token")
}
return claims, nil
}
func VerifyRefreshToken(tokenString string) (*CustomClaims, error) {
claims, err := VerifyToken(tokenString)
if err != nil {
return nil, err
}
// Verify it's actually a refresh token
if claims.TokenType != "refresh" {
return nil, fmt.Errorf("token is not a refresh token")
}
return claims, nil
}
func VerifyAccessToken(tokenString string) (*CustomClaims, error) {
claims, err := VerifyToken(tokenString)
if err != nil {
return nil, err
}
// Verify it's actually an access token
if claims.TokenType != "access" {
return nil, fmt.Errorf("token is not an access token")
}
return claims, nil
}