From fb7e9bdca40a7156ae4cc3d0b45cf08102c1485e Mon Sep 17 00:00:00 2001 From: Nathan Coad Date: Tue, 21 Apr 2026 14:54:19 +1000 Subject: [PATCH] dont include groups in JWT --- internal/auth/jwt.go | 7 ++++--- internal/auth/jwt_test.go | 15 +++++++++++++++ server/handler/auth.go | 2 +- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/internal/auth/jwt.go b/internal/auth/jwt.go index dba9bb3..815b11d 100644 --- a/internal/auth/jwt.go +++ b/internal/auth/jwt.go @@ -102,9 +102,10 @@ func (s *JWTService) IssueToken(subject string, roles []string, groups []string) now := s.now().UTC() claims := Claims{ - Subject: subject, - Roles: compactTrimmedStrings(roles), - Groups: compactTrimmedStrings(groups), + Subject: subject, + Roles: compactTrimmedStrings(roles), + // Intentionally omit LDAP groups from JWTs; role claims are sufficient for authorization. + Groups: nil, Issuer: s.issuer, Audience: s.audience, IssuedAt: now.Unix(), diff --git a/internal/auth/jwt_test.go b/internal/auth/jwt_test.go index 6bfa906..2ac382c 100644 --- a/internal/auth/jwt_test.go +++ b/internal/auth/jwt_test.go @@ -57,6 +57,21 @@ func TestIssueAndVerifyTokenRoundTrip(t *testing.T) { if issuedClaims.ID == "" { t.Fatal("expected jti to be populated") } + if len(issuedClaims.Groups) != 0 { + t.Fatalf("expected groups to be omitted from issued claims, got %#v", issuedClaims.Groups) + } + + parts := strings.Split(token, ".") + if len(parts) != 3 { + t.Fatalf("expected jwt to have 3 parts, got %d", len(parts)) + } + payloadJSON, err := base64.RawURLEncoding.DecodeString(parts[1]) + if err != nil { + t.Fatalf("failed to decode jwt payload: %v", err) + } + if strings.Contains(string(payloadJSON), `"groups"`) { + t.Fatalf("expected jwt payload to omit groups claim, got payload: %s", string(payloadJSON)) + } verifiedClaims, err := svc.VerifyToken(token) if err != nil { diff --git a/server/handler/auth.go b/server/handler/auth.go index 08e9d2a..ae7c00e 100644 --- a/server/handler/auth.go +++ b/server/handler/auth.go @@ -217,7 +217,7 @@ func (h *Handler) AuthLogin(w http.ResponseWriter, r *http.Request) { if subject == "" { subject = username } - token, claims, err := jwtSvc.IssueToken(subject, roles, identity.Groups) + token, claims, err := jwtSvc.IssueToken(subject, roles, nil) if err != nil { h.Logger.Error("failed to issue auth token", "username", username, "error", err) audit.LogAuthEvent(h.Logger, r, "login", "error", "reason", "token_issue_failed", "username", username, "error", err)