Documentation
¶
Overview ¶
Package idjag holds the ToolHive rules for ID-JAG, the assertion used by Cross App Access (XAA). It follows draft-ietf-oauth-identity-assertion-authz-grant-06.
An ID-JAG is a short-lived signed JWT whose "typ" header is oauth-id-jag+jwt (draft 3.1, 4.4.1; RFC 8725 Section 3.11). Three roles take part. The IdP authorization server issues it. The client obtains it and presents it. The resource authorization server redeems it.
Step 1, the exchange. The client sends an RFC 8693 token exchange to the IdP with requested_token_type set to the ID-JAG token type, exactly one "audience" (draft 4.3, 4.3.3) and a "resource" (draft 4.3), which is RECOMMENDED and must be included when the client found the resource through RFC 9728 metadata. The response carries issued_token_type set to the ID-JAG token type, token_type N_A, and the ID-JAG in the access_token member (draft 4.3.4).
Step 2, the redemption. The client sends the ID-JAG to the resource authorization server as an RFC 7523 JWT-bearer grant (draft 4.4). The server validates it as draft 4.4.1 describes: all of RFC 7521 Section 5.2 plus the ID-JAG rules. An ID-JAG is a JWT-bearer assertion profile that is obtained through a token exchange. The redemption is not itself a token exchange.
This package holds three functions:
IsIDJAG is the typ test. Every access-token validator that shares a signing key with an ID-JAG issuer must call it, so an ID-JAG is never accepted as an access token.
FromExchange is the client's sanity check on the IdP response from step 1. It checks that the IdP honoured the request: token types, a parsable JWT, the requested audience and the requested resource. The rule that aud is a single issuer identifier belongs to the resource server (draft 4.4.1). The check here requires aud to be exactly the requested value. Draft 4.3 lets an IdP resolve a URN audience to the issuer identifier, and this check refuses that: ToolHive supports only issuer-identifier audiences here.
ResourceClaim reads the resource claim as a list of strings and reports whether the claim is well formed under draft 3.
The server-side jti and client_id binding checks live inline in pkg/authserver/server/tokenexchange/id_jag_handler.go. Signature checks, issuer trust, replay protection and client authentication are product concerns. This package never does them.
The wiring lives in pkg/vmcp/auth/strategies/xaa.go (the client) and in pkg/authserver/server/tokenexchange (the authorization server).
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrNoToken = errors.New("no token") ErrIssuedTokenType = errors.New("issued_token_type is not the ID-JAG type") ErrTokenType = errors.New("token_type is not N_A") ErrEmptyAssertion = errors.New("empty ID-JAG assertion") ErrNotJWT = errors.New("ID-JAG assertion is not a valid JWT") ErrAudienceMismatch = errors.New("ID-JAG aud does not match the requested audience") ErrResourceMismatch = errors.New("ID-JAG resource does not contain the requested resource") )
Sentinel errors returned by FromExchange. Use errors.Is to tell them apart.
Functions ¶
func FromExchange ¶
FromExchange checks the token an RFC 8693 token exchange at the IdP returned for an ID-JAG request (draft 4.3.4) and returns the ID-JAG assertion. The token is the one a pkg/oauthproto/tokenexchange token source returns.
The checks run in this order: issued_token_type is the ID-JAG type, token_type is N_A, the assertion is not empty, the assertion parses as a JWT, aud is exactly the requested audience, and, when resource is not empty, the resource claim contains it. A typ header that is not the ID-JAG type is logged and accepted. The JWT is parsed without verifying its signature: this is a check that the IdP honoured the request, not a trust decision.
Error messages hold only the checked value and the wanted value, never the assertion.
func IsIDJAG ¶
IsIDJAG reports whether a JOSE "typ" header value names the ID-JAG type. It is true for every RFC 7515 Section 4.1.9 spelling: the comparison ignores case and the "application/" prefix is optional.
This is the deny-side predicate. Every access-token validator that shares a signing key with an ID-JAG issuer must refuse a token for which it is true.
func ResourceClaim ¶
ResourceClaim reads the "resource" claim of an ID-JAG.
values holds the claim's strings: a string gives a one-element slice, an array gives its string elements (other elements are skipped), and any other value gives nil.
ok is true only when the claim is well formed under draft-ietf-oauth- identity-assertion-authz-grant-06 section 3: a non-empty string, or a non-empty array whose elements are all non-empty strings with no duplicates.
ResourceClaim cannot tell an absent claim from a malformed one. A caller that needs that distinction checks the claims map for the key itself (the comma-ok form). A JSON null counts as present but malformed.
Types ¶
This section is empty.