package auth import ( "crypto/rand" "crypto/sha256" "encoding/base64" "time" "github.com/golang-jwt/jwt/v5" "github.com/google/uuid" ) func NewRefreshToken() (plain string, hashed string, err error) { raw := make([]byte, 32) if _, err = rand.Read(raw); err != nil { return "", "", err } plain = base64.RawURLEncoding.EncodeToString(raw) sum := sha256.Sum256([]byte(plain)) hashed = base64.RawURLEncoding.EncodeToString(sum[:]) return plain, hashed, nil } func SignAccessToken(secret, issuer string, ttl time.Duration, claims Claims) (string, error) { token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "iss": issuer, "sub": claims.UserID.String(), "username": claims.Username, "role": claims.Role, "session_id": claims.Session.String(), "exp": time.Now().Add(ttl).Unix(), "iat": time.Now().Unix(), }) return token.SignedString([]byte(secret)) } func ParseAccessToken(secret string, tokenString string) (Claims, error) { claims := Claims{} token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) { return []byte(secret), nil }) if err != nil || !token.Valid { return claims, err } mapClaims, ok := token.Claims.(jwt.MapClaims) if !ok { return claims, jwt.ErrTokenMalformed } subject, ok := mapClaims["sub"].(string) if !ok { return claims, jwt.ErrTokenMalformed } sessionValue, ok := mapClaims["session_id"].(string) if !ok { return claims, jwt.ErrTokenMalformed } userID, err := uuid.Parse(subject) if err != nil { return claims, err } sessionID, err := uuid.Parse(sessionValue) if err != nil { return claims, err } claims.UserID = userID claims.Session = sessionID claims.Username, _ = mapClaims["username"].(string) claims.Role, _ = mapClaims["role"].(string) return claims, nil }