Files
Primora/apps/backend/internal/auth/jwt.go
T
2026-04-10 12:03:31 +02:00

73 lines
1.9 KiB
Go

package auth
import (
"context"
"fmt"
"time"
"github.com/MicahParks/keyfunc/v3"
"github.com/golang-jwt/jwt/v5"
)
type Claims struct {
SessionID string `json:"sid"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
Name string `json:"name,omitempty"`
jwt.RegisteredClaims
}
func ParseToken(rawToken, secret, issuer, audience string) (*Claims, error) {
token, err := jwt.ParseWithClaims(rawToken, &Claims{}, func(token *jwt.Token) (interface{}, error) {
if token.Method.Alg() != jwt.SigningMethodHS256.Alg() {
return nil, fmt.Errorf("unexpected signing method %s", token.Method.Alg())
}
return []byte(secret), nil
}, jwt.WithIssuer(issuer), jwt.WithAudience(audience), jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}))
if err != nil {
return nil, err
}
claims, ok := token.Claims.(*Claims)
if !ok || !token.Valid {
return nil, fmt.Errorf("invalid token claims")
}
if claims.ExpiresAt == nil || claims.ExpiresAt.Time.Before(time.Now()) {
return nil, fmt.Errorf("token expired")
}
return claims, nil
}
type Verifier struct {
keyfunc jwt.Keyfunc
issuer string
audience string
}
func NewVerifier(ctx context.Context, jwksURL, issuer, audience string) (*Verifier, error) {
jwks, err := keyfunc.NewDefaultCtx(ctx, []string{jwksURL})
if err != nil {
return nil, fmt.Errorf("create jwks verifier: %w", err)
}
return &Verifier{
keyfunc: jwks.Keyfunc,
issuer: issuer,
audience: audience,
}, nil
}
func (v *Verifier) ParseToken(rawToken string) (*Claims, error) {
token, err := jwt.ParseWithClaims(rawToken, &Claims{}, v.keyfunc, jwt.WithIssuer(v.issuer), jwt.WithAudience(v.audience))
if err != nil {
return nil, err
}
claims, ok := token.Claims.(*Claims)
if !ok || !token.Valid {
return nil, fmt.Errorf("invalid token claims")
}
if claims.ExpiresAt == nil || claims.ExpiresAt.Time.Before(time.Now()) {
return nil, fmt.Errorf("token expired")
}
return claims, nil
}