package jwt import ( "errors" "time" "github.com/golang-jwt/jwt/v5" ) type Claims struct { Username string `json:"username"` jwt.RegisteredClaims } type Service struct { secret string expiry time.Duration signingMethod jwt.SigningMethod } func NewService(secret string, expirySeconds int) *Service { if secret == "" { secret = "dev-secret-change-me" } if expirySeconds <= 0 { expirySeconds = 3600 } return &Service{ secret: secret, expiry: time.Duration(expirySeconds) * time.Second, signingMethod: jwt.SigningMethodHS256, } } func (s *Service) IssueToken(username string) (string, error) { if username == "" { return "", errors.New("missing username") } now := time.Now() claims := Claims{ Username: username, RegisteredClaims: jwt.RegisteredClaims{ Subject: username, IssuedAt: jwt.NewNumericDate(now), ExpiresAt: jwt.NewNumericDate(now.Add(s.expiry)), }, } t := jwt.NewWithClaims(s.signingMethod, claims) return t.SignedString([]byte(s.secret)) } func (s *Service) ParseToken(tokenString string) (*Claims, error) { if tokenString == "" { return nil, errors.New("missing token") } claims := &Claims{} parsed, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { if token.Method == nil || token.Method.Alg() != s.signingMethod.Alg() { return nil, errors.New("unexpected signing method") } return []byte(s.secret), nil }) if err != nil { return nil, err } if parsed == nil || !parsed.Valid { return nil, errors.New("invalid token") } return claims, nil }