feat(oauth): JWT-Bearer client auth + grant (RFC 7523) + token-exchange polish - #65
Merged
Merged
Conversation
Contributor
Benchmark regression reportThreshold: 25% Generated by the bench workflow. A |
…ge polish Adds RFC 7523 section 2.2 (private_key_jwt client authentication via signed JWT assertion) and section 2.1 (jwt-bearer grant type for externally-issued JWTs from GCP SA, k8s OIDC, AWS IAM Roles Anywhere). Token-exchange gains MaxActorChainDepth config knob and audience whitelist validation. 10 new tests; all pass -race -count=3. New public surface: - JWTBearerConfig, TrustedJWTIssuer, SubjectMapper interface - SubMapper (sub=ULID passthrough), EmailMapper (email claim lookup) - JWTBearerStorage interface + memory.Store implementation (InsertJTI, SweepExpiredJTIs) with in-process sync.Map fallback - JWTBearerGrant forwarder on *TheAuth - AS metadata: private_key_jwt, client_secret_jwt, token_endpoint_auth_signing_alg_values_supported, urn:ietf:params:oauth:grant-type:jwt-bearer in grant_types_supported
thegdsks
force-pushed
the
feat/jwt-bearer-grant-2026-06-22
branch
from
June 22, 2026 20:34
4a766cd to
2cf1c72
Compare
5 tasks
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
private_key_jwtandclient_secret_jwtclient authentication. Clients present a signed JWTclient_assertioninstead of a shared secret; the AS verifies against the client's registeredjwks_urior inlinejwks. Supports ES256, ES384, RS256, PS256, EdDSA (stdlib-only, no new deps).urn:ietf:params:oauth:grant-type:jwt-bearergrant. Exchanges an externally-issued JWT (GCP SA, k8s OIDC, AWS IAM Roles Anywhere) for an AS-issued access token via configuredTrustedJWTIssuers. IncludesSubjectMapperinterface with built-inSubMapper(sub=ULID) andEmailMapper(email claim).JWTBearerConfig.MaxActorChainDepthoverridesAgentPolicy.MaxChainDepthwhen set;ExchangeTokenvalidates theaudienceparameter against the configured resource catalog.sync.Mapfallback + optional durableJWTBearerStorageinterface (InsertJTI,SweepExpiredJTIs) withmemory.Storeimplementation.private_key_jwt,client_secret_jwt,token_endpoint_auth_signing_alg_values_supported, and the jwt-bearer grant URN.-race -count=3):TestPrivateKeyJWTHappyPath,TestPrivateKeyJWTReplayRejected,TestPrivateKeyJWTExpiredAssertion,TestPrivateKeyJWTWrongAudience,TestPrivateKeyJWTAlgorithmDowngradeRejected,TestJWTBearerGrantHappyPath,TestJWTBearerGrantUntrustedIssuer,TestJWTBearerGrantSubjectMapperMiss,TestTokenExchangeMaxChainDepth,TestTokenExchangeAudienceWhitelist.Test plan
go test -race -count=3 ./...passes (verified locally)gofmt -l .returns no output (verified)go vet ./...returns no output (verified)git diff --name-only)private_key_jwtclient authenticates with ES256 assertion and receives access tokenjtitwice returns an error on the second attemptexpin the past returns an erroraudnot matching the token endpoint URL returns an errorprivate_key_jwtclientsissis rejectedaudienceparameter rejected with invalid_target