Validate aud and resource claims in returned ID-JAG JWT#5716
Open
jhrozek wants to merge 1 commit into
Open
Conversation
The XAA IdP exchange (RFC 8693) returned an ID-JAG JWT without validating its aud or resource claims against the values requested in the exchange. If the IdP minted an ID-JAG for a different audience or resource, ToolHive forwarded it to the target AS unchanged — a token- misbinding / confused-deputy risk. Add validateIDJAGClaims to check the aud claim (REQUIRED per draft §3.1, membership check per RFC 7519 §4.1.3) and the resource claim (OPTIONAL per §3.1, validated when one was requested) inside the returned ID-JAG JWT. A non-JWT assertion that fails to parse is now rejected rather than forwarded. The typ header check remains logged- only by design (the draft §3.1 MUST is on the issuer; ToolHive is the holder/presenter). Closes #5696
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #5716 +/- ##
=======================================
Coverage 70.66% 70.66%
=======================================
Files 682 682
Lines 68854 68887 +33
=======================================
+ Hits 48656 48682 +26
- Misses 16650 16656 +6
- Partials 3548 3549 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
JAORMX
approved these changes
Jul 4, 2026
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
audorresourceclaims against the values requested in the exchange. If the IdP minted an ID-JAG for a different audience or resource, ToolHive forwarded it to the target AS unchanged — a token-misbinding / confused-deputy risk.validateIDJAGClaimsto check theaudclaim (REQUIRED per ID-JAG draft §3.1, membership check per RFC 7519 §4.1.3) and theresourceclaim (OPTIONAL per §3.1, validated when one was requested) inside the returned ID-JAG JWT. A non-JWT assertion that fails to parse is now rejected rather than forwarded.typheader check remains logged-only by design (the draft §3.1 MUST is on the issuer; ToolHive is the holder/presenter).Fixes #5696
Type of change
Test plan
task test)task lint-fix)Does this introduce a user-facing change?
No. The XAA strategy now rejects malformed ID-JAG responses that would have failed at the target AS anyway. No configuration or API surface change.
Implementation plan
Approved implementation plan
Validate the
audandresourceclaims in the ID-JAG JWT returned by the IdP exchange in the XAA auth strategy (pkg/vmcp/auth/strategies/xaa.go).Per draft-ietf-oauth-identity-assertion-authz-grant §3.1:
audis REQUIRED — the issuer identifier of the Resource Authorization Server.resourceis OPTIONAL — "either a single URI or an array of URIs" (RFC 7519 string-or-array semantics).Step 1 —
validateIDJAGClaims+ wiring:validateIDJAGClaims(claims jwt.MapClaims, wantAudience, wantResource string) error.aud: membership check (requested audience must be among the aud values; IdP may include additional audiences). Handle string and[]interface{}array forms (RFC 7519 §4.1.3).resource: membership check only whenwantResource != ""(OPTIONAL in the exchange request per §4.3). Handle string and array forms.typheader check stays logged-only (draft §3.1 MUST is on the issuer).Step 2 — Tests:
buildJWTWithTypHeaderintobuildIDJAGJWT(t, typ, aud, resource).aud.Reviewed by MoE panel:
oauth-expert(ID-JAG spec compliance),code-reviewer(Go conventions),security-advisor(threat model). All approved.Special notes for reviewers
resourceclaim is validated only when the operator configured aTargetResource. Per the ID-JAG draft §3.1,resourceis OPTIONAL in both the exchange request (§4.3) and the ID-JAG itself. When configured, the check is stricter than the draft requires (rejecting a missingresourceeven though the IdP could legitimately omit it) — this is an intentional operator-driven binding choice for defence-in-depth.iss/sub/exp/jtiare not validated locally. This is acceptable deferral — the target AS performs authoritative JWT validation in the target grant (RFC 7523). Anexpcheck would be cheap defence-in-depth and is flagged as follow-up.oauth-expertreviewer noted the RFC citation for theresourcearray form: RFC 8707 §2 defines the request parameter, not the JWT claim. The comments cite "RFC 7519 string-or-array semantics" instead.Generated with Claude Code