package jwt import ( "context" "errors" "time" "go.b0esche.cloud/backend/internal/database" "github.com/golang-jwt/jwt/v4" "github.com/google/uuid" ) type Claims struct { UserID string `json:"user_id"` OrgIDs []string `json:"org_ids"` SessionID string `json:"session_id"` jwt.RegisteredClaims } type Manager struct { secret []byte } func NewManager(secret string) *Manager { return &Manager{secret: []byte(secret)} } func (m *Manager) Generate(userID string, orgIDs []string, sessionID string) (string, error) { return m.GenerateWithDuration(userID, orgIDs, sessionID, 15*time.Minute) } func (m *Manager) GenerateWithDuration(userID string, orgIDs []string, sessionID string, duration time.Duration) (string, error) { claims := Claims{ UserID: userID, OrgIDs: orgIDs, SessionID: sessionID, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(duration)), IssuedAt: jwt.NewNumericDate(time.Now()), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString(m.secret) } func (m *Manager) Validate(tokenString string) (*Claims, error) { token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, errors.New("unexpected signing method") } return m.secret, nil }) if err != nil { return nil, err } if claims, ok := token.Claims.(*Claims); ok && token.Valid { return claims, nil } return nil, errors.New("invalid token") } func (m *Manager) ValidateWithSession(ctx context.Context, tokenString string, db *database.DB) (*Claims, *database.Session, error) { claims, err := m.Validate(tokenString) if err != nil { return nil, nil, err } sessionID, err := uuid.Parse(claims.SessionID) if err != nil { return nil, nil, errors.New("invalid session ID in token") } session, err := db.GetSession(ctx, sessionID) if err != nil { return nil, nil, err } if session.RevokedAt != nil || time.Now().After(session.ExpiresAt) { return nil, nil, errors.New("session invalid") } return claims, session, nil }