Skip to content

xaa strategy: validate returned ID-JAG aud/resource claims before Step B (fail-fast config-hygiene check) #5696

Description

@jhrozek

Follow-up from review of RFC-0079 (stacklok/toolhive-rfcs#79) and merged PR #5684.

Context: XAAStrategy.performStepA (pkg/vmcp/auth/strategies/xaa.go) already unverified-parses the returned ID-JAG JWT to check the typ header (lines 260-269), as defense-in-depth alongside the OAuth-level issued_token_type/token_type=N_A checks (lines 233-245). It does not currently also check the JWT's aud/resource claims against the configured TargetAudience/TargetResource before handing the assertion to Step B.

Why this is worth adding: it is not a security boundary — an attacker controlling a malicious/compromised IdP response could fabricate matching unsigned claims just as easily, so this does not substitute for the Resource AS's own signature/aud/resource validation at Step B, which remains the actual trust boundary (per RFC-0079 §4.3). Its value is as a fail-fast config-correctness check: catching IdP misconfiguration or an accidental cross-wiring between resources before an opaque Step B failure (or, in a lax-AS scenario, a silent success against the wrong resource).

Scope: in performStepA, extend the existing parser.ParseUnverified(assertion, jwt.MapClaims{}) call (already at line 260-261) to also read aud and resource from the parsed claims and compare against config.targetAudience/config.targetResource. Treat a mismatch as a hard error (unlike the existing typ check, which is a soft warning) — this one should block Step B rather than just log, since proceeding to Step B with a known-wrong audience serves no purpose. Log clearly that this is a config-hygiene check, not a signature-verified assertion.

Related: #5695 (XAA follow-ups from #5691) tracks other deferred non-blocking XAA items but doesn't cover this one.

cc @tgrunnagle — this was your finding (F2) on RFC-0079's review.

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageIssue needs initial triage by a maintainer

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions