feat(jwksauth): add UID server-attested claim#29
Merged
Conversation
- Add UID field to Claims, populated from the <prefix>_uid JWT key - Thread UID through claimKeys, newClaimKeys, and newTokenInfo alongside Domain, Project, and ServiceAccount, preserving lazy Extras allocation - Document that UID is the username for user-bearing flows and is empty for Client Credentials tokens - Cover happy path, custom-prefix hard cutover, bare-key ignore, and Client Credentials shape with new tests - Refresh doc.go and README to describe four server-attested claims Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds support in jwksauth for a fourth server-attested private claim, UID, surfaced as Claims.UID and sourced from the <prefix>_uid JWT payload key (default extra_uid). This extends the existing server-attested claim plumbing (prefix resolution + Extras exclusion) alongside Domain/Project/ServiceAccount and updates tests/docs accordingly.
Changes:
- Extend
jwksauth.Claimsand internalclaimKeys/decoding to read<prefix>_uidintoClaims.UIDand exclude it fromExtras. - Add/extend unit tests covering default/custom prefix behavior, no-fallback semantics, and “client credentials = empty UID”.
- Update package docs and README to document the new claim and its semantics.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| jwksauth/claims.go | Adds Claims.UID, threads <prefix>_uid through claimKeys and decoding, and excludes it from Extras. |
| jwksauth/claims_prefix_test.go | Extends prefix/Extras tests to cover UID, including hard-cutover and bare uid behavior. |
| jwksauth/middleware_test.go | Adds verifier/middleware test coverage for UID and client-credentials shape. |
| jwksauth/doc.go | Updates package-level documentation to include UID as server-attested under the prefix. |
| jwksauth/README.md | Updates README to describe extra_uid / Claims.UID and related semantics. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Rephrase the wrong-prefix sentence to make explicit that fail-closed only applies to the three AccessRule-covered dimensions, and that UID is identity not authorization Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment on lines
30
to
37
| type Claims struct { | ||
| ClientID string | ||
| Scope string | ||
| Domain string | ||
| ServiceAccount string | ||
| Project string | ||
| UID string | ||
|
|
Comment on lines
+37
to
+45
| // AuthGate emits four private claims — Domain, Project, ServiceAccount, | ||
| // and UID — under a configurable prefix (default "extra"), so the JWT | ||
| // payload keys are "extra_domain", "extra_project", "extra_service_account", | ||
| // and "extra_uid". The SDK reads them out of the box; if your AuthGate | ||
| // deployment has overridden JWT_PRIVATE_CLAIM_PREFIX, pass the same value | ||
| // via [WithPrivateClaimPrefix]. UID carries the username for tokens | ||
| // issued by user-bearing flows (Authorization Code + PKCE, Device | ||
| // Authorization Grant); the Client Credentials flow has no user, so UID | ||
| // is the empty string for those tokens. |
Comment on lines
76
to
+80
| t.Run("acme_prefix_hits", func(t *testing.T) { | ||
| tok := fi.Sign(t, "api://x", time.Minute, map[string]any{"acme_domain": "oa"}) | ||
| tok := fi.Sign(t, "api://x", time.Minute, map[string]any{ | ||
| "acme_domain": "oa", | ||
| "acme_uid": "alice", | ||
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a
UID stringfield tojwksauth.Claimsthat carries the username from AuthGate-issued tokens under the<prefix>_uidJWT key. UID is populated by user-bearing flows (Authorization Code + PKCE, Device Authorization Grant); Client Credentials tokens have no user, soClaims.UIDis the empty string. Threaded through the existingclaimKeys/newClaimKeys/newTokenInfoplumbing alongsideDomain/Project/ServiceAccountwith the same hard-cutover prefix semantics and lazyExtrasallocation.AI Authorship
jwksauth/claims.go,jwksauth/claims_prefix_test.go,jwksauth/middleware_test.go,jwksauth/doc.go,jwksauth/README.mdChange classification
Claimsis a public SDK type consumed by every caller ofjwksauth.Verifier/Middleware. The change is purely additive and backwards-compatible (new field defaults to""), but reviewers should still confirm: (a) wire-key string"_uid"matches what upstream AuthGate emits, (b) Extras-exclusion logic correctly suppresses the<prefix>_uidkey, (c)staticReservedClaimKeysis intentionally untouched (theuser_idUUID concept is separate fromUIDusername and must not be disturbed).Plan reference
plan.mdin the repo root (not committed; planning artifact). Goal: surface the username claim from user-bearing AuthGate flows onClaims.UID, keepstaticReservedClaimKeys.user_id(the UUID concept) untouched, and use the same hard-cutover prefix semantics as the other three server-attested claims.Verification
Verifier.Verifyand throughMiddleware)Test inventory:
TestPrefixedClaims_DefaultPrefix_HappyPath— extended: token carriesextra_uid: "alice"; assertsClaims.UID == "alice"(user-bearing happy path).TestVerifier_HappyPath— extended: same as above, asserted on the verifier directly.TestVerifier_ClientCredentialsShape_UIDEmpty— new: token withoutextra_uidsucceeds,Claims.UID == "", middleware returns 200 underAccessRule{}(Client Credentials shape).TestPrefixedClaims_CustomPrefix/acme_prefix_hits— extended:acme_uidcorrectly populatesClaims.UID.TestPrefixedClaims_CustomPrefix/default_prefix_no_fallback— extended: underacmeprefix,extra_uiddoes NOT fall back, lands inExtrasinstead,Claims.UID == ""(hard cutover).TestPrefixedClaims_BareUIDIgnored— new: bareuidkey (no<prefix>_) is not promoted toClaims.UID; surfaces viaExtras["uid"]instead.make lintclean (0 issues).make testgreen across all packages.Manual verification: none required (pure library change, no runtime behavior to inspect outside the test suite).
Verifiability check
Claims,claimKeys,newClaimKeys;doc.goandREADME.mddescribe the four server-attested claims and the Client-Credentials = empty UID invariant)info.Claims.UIDafter this change indicates wire-key driftSecurity check
alice,oa,p1are not credentials)AccessRuledeliberately does not gain aUIDsfield; UID is identity, not authorization. Callers gate on UID in their own handler if needed.Risk & rollback
<prefix>_uid(e.g.<prefix>_username),Claims.UIDwill silently always be empty. Cost: dead field, no incorrect behavior.extra_uidas a custom Extras key will see that key disappear fromExtrasand surface onClaims.UIDinstead. This is the documented<prefix>_*namespace contract (same for Domain/Project/ServiceAccount) and was already a "do not use" zone for callers.Reviewer guide
jwksauth/claims.go— confirm wire key"_uid"(line 125), confirmkeys.uidis included in the Extras-exclusion check (line 150), confirmstaticReservedClaimKeysis untouched (theuser_identry there is the separate UUID concept and must not be disturbed).jwksauth/claims_prefix_test.go:184—TestPrefixedClaims_BareUIDIgnoredand the new assertions in thedefault_prefix_no_fallbacksubtest pin the hard-cutover semantics.jwksauth/middleware_test.go— additions mirror existing patterns (extra_domain/extra_projectstyle).jwksauth/doc.go,jwksauth/README.md— text-only updates aligned with the code change.🤖 Generated with Claude Code