package auth import ( "context" "errors" "time" "github.com/golang-jwt/jwt/v5" "github.com/google/uuid" ) type Claims struct { UserID uuid.UUID `json:"uid"` jwt.RegisteredClaims } type Tokens struct { Access string Refresh string } type Service struct { secret []byte accessTTL time.Duration refreshTTL time.Duration } func NewService(secret string, accessTTL, refreshTTL time.Duration) *Service { return &Service{ secret: []byte(secret), accessTTL: accessTTL, refreshTTL: refreshTTL, } } func (s *Service) Generate(userID uuid.UUID) (*Tokens, error) { now := time.Now() access := jwt.NewWithClaims(jwt.SigningMethodHS256, Claims{ UserID: userID, RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(now), ExpiresAt: jwt.NewNumericDate(now.Add(s.accessTTL)), Subject: userID.String(), }, }) accessStr, err := access.SignedString(s.secret) if err != nil { return nil, err } refresh := jwt.NewWithClaims(jwt.SigningMethodHS256, Claims{ UserID: userID, RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(now), ExpiresAt: jwt.NewNumericDate(now.Add(s.refreshTTL)), Subject: userID.String(), }, }) refreshStr, err := refresh.SignedString(s.secret) if err != nil { return nil, err } return &Tokens{Access: accessStr, Refresh: refreshStr}, nil } func (s *Service) Parse(ctx context.Context, tokenStr string) (*Claims, error) { claims := &Claims{} tok, 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 s.secret, nil }) if err != nil || !tok.Valid { return nil, errors.New("invalid token") } return claims, nil }