The most contentious design question in draft-carleton-workload-authz-grant: does each agent instance get its own client_id and authenticate in the client-auth slot (left column, agent = client), or does the agent stay the subject of the platform-signed authorization grant with the client role left to the platform (right column, agent = subject, the current draft)? Three scenarios, worked both ways. Companion: request shapes. Status and full arguments: decision-log O4 in the repo.
The crux: Both sides agree the state that actually scales per agent (grants, jti replay, audit) grows identically under either design; the irreducible choice is which set of inventions the draft signs up for, because neither exists fully today. Agent==client inherits deployed, client_id-keyed management machinery but must invent per-instance client identity: extending the adopted family-granularity stack (CIMD/ATTEST/SPIFFE) to instance cardinality, with per-agent client_ids, family-vs-instance consent semantics, refresh custody for ephemeral instances, and IdPs that resolve per-agent identifiers -- invent new things for client management. Agent==subject inherits the registered delegation grammar (sub, act, actor_token) but must invent the management and binding plane on (iss, sub): per-agent quotas, kill switches, token listing, the consent-binding rule, and the act-in-ID-JAG composition, none of which any deployed AS enforces -- invent new things for representing delegations. There is no third stack; the tiebreakers are the adoption floor (jwt-bearer everywhere vs ATTEST/CIMD nearly nowhere) and which primary key -- client_id or (iss, sub) -- the ecosystem's per-agent state should accrete under.
Support Triage Agent 7f3d9a2e, hosted by Acme's tenancy on the platform, fetches tickets from its own store at api.saas.example. No user in the loop. The admin has one allowlist row at as.saas.example: client_id prefix https://acme.agents.platform.example/ vouched by attester issuer https://acme.agents.platform.example (keys via the same OIDC-discovery JWKS the current WAG draft already fetches).
POST /token HTTP/1.1
Host: as.saas.example
Content-Type: application/x-www-form-urlencoded
OAuth-Client-Attestation: eyJ0eXAiOiJvYXV0aC1jbGllbnQtYXR0ZXN0YXRpb24rand0IiwiYWxnIjoiRVMyNTYi...
OAuth-Client-Attestation-PoP: eyJ0eXAiOiJvYXV0aC1jbGllbnQtYXR0ZXN0YXRpb24tcG9wK2p3dCIsImFsZyI6IkVTMjU2In0...
grant_type=client_credentials
&client_id=https%3A%2F%2Facme.agents.platform.example%2Fagents%2F7f3d9a2e
&scope=tickets.read+tickets.write
&resource=https%3A%2F%2Fapi.saas.example%2F
OAuth-Client-Attestation JWT, signed by the tenancy issuer key (draft-ietf-oauth-attestation-based-client-auth-10 s4):
{"typ":"oauth-client-attestation+jwt","alg":"ES256","kid":"2026-07-14"}
.
{
"iss": "https://acme.agents.platform.example",
"sub": "https://acme.agents.platform.example/agents/7f3d9a2e",
"cnf": {"jwk": {"kty":"EC","crv":"P-256","x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU","y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0"}},
"iat": 1785271680,
"exp": 1785272280,
"name": "Support Triage Agent",
"namespace": "acme/support",
"groups": ["support-eng"],
"roles": ["responder"],
"ctx": "channel:C0123456789"
}
(signature: platform tenancy key)
OAuth-Client-Attestation-PoP JWT, signed by the instance key bound in cnf (s5.1):
{"typ":"oauth-client-attestation-pop+jwt","alg":"ES256"}
.
{
"aud": "https://as.saas.example",
"jti": "d0c17c0f-8a41-4f4a-9d3e-52b2e7a1c9b4",
"iat": 1785271680
}
(signature: instance key)
Issued access token (RFC 9068 shape):
{
"iss": "https://as.saas.example",
"sub": "https://acme.agents.platform.example/agents/7f3d9a2e",
"client_id": "https://acme.agents.platform.example/agents/7f3d9a2e",
"aud": "https://api.saas.example/",
"scope": "tickets.read tickets.write",
"exp": 1785272280,
"namespace": "acme/support",
"roles": ["responder"]
}
Same admin surface as WAG today: one allowlist row per tenancy, keys by reference. The AS validates the attestation signature from the cached tenancy JWKS, prefix-matches sub against the allowlist, verifies the PoP against cnf, checks jti replay (ATTEST s11.1), and never fetches anything per-agent. The agent Properties ride in the attestation and map to permissions exactly as the draft's Properties section intends; nothing about that design is lost. Note what this buys over the WAG bearer grant with zero extra admin work: a stolen request body is useless because issuance requires the instance-held cnf key, which is O1's hardened mode arrived at by construction rather than by a bolted-on binding rule.
Support Triage Agent 7f3d9a2e, hosted in Acme's tenancy on the platform, fetches tickets from its own store at api.saas.example. No user in the loop. Admin surface: one allowlist row at as.saas.example binding issuer https://acme.agents.platform.example to Acme's tenancy, keys by OIDC-discovery JWKS reference (the draft's existing Trust Establishment section, unchanged). Floor shape shown first; hardened shape (O1 row 3) adds two headers and one binding rule, with the agent still the SUBJECT.
POST /token HTTP/1.1
Host: as.saas.example
Content-Type: application/x-www-form-urlencoded
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
&assertion=eyJhbGciOiJFUzI1NiIsImtpZCI6IjIwMjYtMDctMTQifQ...
&resource=https%3A%2F%2Fapi.saas.example%2F
Assertion decoded (RFC 7523 s2.1 authorization grant, third-party issuer per RFC 7521 s3):
{"alg":"ES256","kid":"2026-07-14"}
.
{
"iss": "https://acme.agents.platform.example",
"sub": "wimse://acme.agents.platform.example/agent/7f3d9a2e",
"aud": ["https://as.saas.example", "https://as.saas.example/token"],
"exp": 1785271980,
"iat": 1785271680,
"jti": "7d0f5a2b-93c8-4f0e-9c33-1b6a0e6d5f10",
"name": "Support Triage Agent",
"namespace": "acme/support",
"groups": ["support-eng"],
"roles": ["responder"],
"ctx": "channel:C0123456789"
}
(signature: tenancy issuer key, resolved by iss from the allowlist, never from a client registration)
Hardened variant (O1 row 3): same request body, plus attestation-based client authentication in the CLIENT slot, agent unchanged in the GRANT slot:
OAuth-Client-Attestation: eyJ0eXAiOiJvYXV0aC1jbGllbnQtYXR0ZXN0YXRpb24rand0IiwiYWxnIjoiRVMyNTYiLCJraWQiOiIyMDI2LTA3LTE0In0...
{
"iss": "https://acme.agents.platform.example",
"sub": "https://acme.agents.platform.example/",
"cnf": {"jwk": {"kty":"EC","crv":"P-256","x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU","y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0"}},
"iat": 1785271680,
"exp": 1785272280
}
(sub = the TENANCY's client identity per ATTEST-10 s4, where attestation sub IS the client_id -- all instances share it, per-instance material is the cnf key; signature: tenancy attester key)
OAuth-Client-Attestation-PoP: eyJ0eXAiOiJvYXV0aC1jbGllbnQtYXR0ZXN0YXRpb24tcG9wK2p3dCIsImFsZyI6IkVTMjU2In0...
{
"aud": "https://as.saas.example",
"jti": "d0c17c0f-8a41-4f4a-9d3e-52b2e7a1c9b4",
"iat": 1785271680
}
(signature: instance-held cnf key)
Binding rule (WAG profile text, one sentence): assertions from allowlisted issuer I are accepted only on requests authenticated by I's attested client, and the AS MAY require the attestation to cover the presenting instance.
Issued access token (both variants):
{
"iss": "https://as.saas.example",
"sub": "wimse://acme.agents.platform.example/agent/7f3d9a2e",
"aud": "https://api.saas.example/",
"scope": "tickets.read tickets.write",
"exp": 1785272280,
"namespace": "acme/support",
"roles": ["responder"]
}
Floor variant is the one grant type every mainstream AS already ships (D3) -- a config change, not new client-auth code paths. Hardened variant delivers the advocate's headline security property (stolen request body useless without the instance-held cnf key) via the IDENTICAL ATTEST mechanics, proving slot choice and proof-of-possession are orthogonal: nothing about assertion-theft protection requires the agent to become the client. Note the attestation sub: per ATTEST-10 s4 it is the client_id, shared across instances -- the adopted draft itself refuses per-instance client_ids, which is the advocate's design, not the WG's. Root blast radius is identical either way (attester key is a platform-level durable key, per O1's sharpened analysis). Zero per-agent rows anywhere; the agent is attributed uniformly in sub.
The author's convenience scenario, worked end to end. Paul wants Support Triage Agent 7f3d9a2e, and only that agent, to read his mailbox at mailco. The agent's client_id is a CIMD URL under the platform tenancy origin. The same client_id continues doing Example 1's client_credentials against its own store with no user in the loop.
Step 1, authorization request (browser):
GET /authorize?response_type=code
&client_id=https%3A%2F%2Facme.agents.platform.example%2Fagents%2F7f3d9a2e
&redirect_uri=https%3A%2F%2Facme.agents.platform.example%2Fagents%2F7f3d9a2e%2Fcallback
&scope=email.read
&state=af0ifjsldkj
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256 HTTP/1.1
Host: as.mailco.example
Step 2, AS dereferences the client_id (CIMD-02 s5; validation per s4: client_id string-match, https, no redirects) and renders consent from:
{
"client_id": "https://acme.agents.platform.example/agents/7f3d9a2e",
"client_name": "Support Triage Agent",
"client_uri": "https://platform.example/a/acme/support-triage",
"redirect_uris": ["https://acme.agents.platform.example/agents/7f3d9a2e/callback"],
"token_endpoint_auth_method": "private_key_jwt",
"jwks_uri": "https://acme.agents.platform.example/agents/7f3d9a2e/jwks"
}
Consent screen: "Support Triage Agent (acme.agents.platform.example) wants to: read your email." paul@acme.example approves.
Step 3, token request:
POST /token HTTP/1.1
Host: as.mailco.example
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Facme.agents.platform.example%2Fagents%2F7f3d9a2e%2Fcallback
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
&client_id=https%3A%2F%2Facme.agents.platform.example%2Fagents%2F7f3d9a2e
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJFUzI1NiIsImtpZCI6ImFnZW50LTdmM2Q5YTJlLTAxIn0...
client_assertion decoded (RFC 7523 s2.2; key published at the agent's jwks_uri, mandatory once declared per CIMD s8.2):
{"alg":"ES256","kid":"agent-7f3d9a2e-01"}
.
{
"iss": "https://acme.agents.platform.example/agents/7f3d9a2e",
"sub": "https://acme.agents.platform.example/agents/7f3d9a2e",
"aud": "https://as.mailco.example",
"exp": 1785271980,
"jti": "3b1f0a77-52c1-4c4e-8d2a-9e5f6c7d8e9f"
}
(signature: agent instance key)
Step 4, token response:
{
"access_token": "eyJhbGciOiJFUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 600,
"refresh_token": "8xLOxBtZp8mYQx7CTO3G",
"scope": "email.read"
}
Access token claims:
{
"iss": "https://as.mailco.example",
"sub": "paul@acme.example",
"client_id": "https://acme.agents.platform.example/agents/7f3d9a2e",
"aud": "https://mail.mailco.example/",
"scope": "email.read",
"exp": 1785272280
}
Zero inventions: this is authorization_code plus PKCE plus CIMD, as deployed. The consent screen names the individual agent, which is more honest than consenting to a platform writ large; the grant is revocable at one row; the refresh token keeps access alive across the agent's weeks-long life and dies when the platform stops publishing the agent's key (CIMD s8.4.1 permits token revocation on key change), giving retirement (D10) an enforcement point. Under WAG as drafted this scenario has no wire form: an authorization request requires a client_id and the agent is not one, so 'grant MY email to THAT agent' cannot be expressed, which is the author's own convenience observation turned into a requirement.
The author's convenience scenario under agent-as-subject. Paul wants Support Triage Agent 7f3d9a2e, and only that agent, to read his mailbox at mailco. The OAuth client is the per-tenancy platform client (ONE CIMD document per tenancy, warm-cached at every AS -- not one per agent, so the cache actually amortizes). The specific agent is named in the authorization request via RFC 9396 authorization_details; the grant is recorded against (user, tenancy client, agent (iss,sub)); liveness requires a fresh platform-signed agent assertion at every token refresh.
Step 1, authorization request (browser):
GET /authorize?response_type=code
&client_id=https%3A%2F%2Facme.agents.platform.example%2F
&redirect_uri=https%3A%2F%2Facme.agents.platform.example%2Foauth%2Fcallback
&authorization_details=%5B%7B%22type%22%3A%22agent_delegation%22%2C%22agent%22%3A%22wimse%3A%2F%2Facme.agents.platform.example%2Fagent%2F7f3d9a2e%22%2C%22agent_name%22%3A%22Support%20Triage%20Agent%22%2C%22actions%22%3A%5B%22email.read%22%5D%7D%5D
&state=af0ifjsldkj
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256 HTTP/1.1
Host: as.mailco.example
authorization_details decoded (RFC 9396):
[{
"type": "agent_delegation",
"agent": "wimse://acme.agents.platform.example/agent/7f3d9a2e",
"agent_name": "Support Triage Agent",
"actions": ["email.read"]
}]
Consent screen (client metadata from the ONE tenancy CIMD document, agent from authorization_details):
"Support Triage Agent, running in Acme's workspace on Agents Platform, wants to: read your email."
paul@acme.example approves. AS records grant G: (paul@acme.example, client=https://acme.agents.platform.example/, agent=wimse://acme.agents.platform.example/agent/7f3d9a2e, email.read).
Step 2, code redemption; platform authenticates (RFC 7523 s2.2 private_key_jwt under the tenancy key, published at the jwks_uri the admin already trusts):
POST /token HTTP/1.1
Host: as.mailco.example
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Facme.agents.platform.example%2Foauth%2Fcallback
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
&client_id=https%3A%2F%2Facme.agents.platform.example%2F
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJFUzI1NiIsImtpZCI6InRlbmFuY3ktMjAyNi0wNyJ9...
client_assertion decoded:
{"alg":"ES256","kid":"tenancy-2026-07"}
.
{
"iss": "https://acme.agents.platform.example/",
"sub": "https://acme.agents.platform.example/",
"aud": "https://as.mailco.example",
"exp": 1785271980,
"jti": "3b1f0a77-52c1-4c4e-8d2a-9e5f6c7d8e9f"
}
(signature: tenancy key)
Response: {"access_token":"eyJhbGciOiJFUzI1NiIs...","token_type":"Bearer","expires_in":600,"refresh_token":"8xLOxBtZp8mYQx7CTO3G","scope":"email.read"} -- refresh token bound to grant G, held by the confidential tenancy client (not by an ephemeral per-agent client whose keys need escrow).
Step 3, every subsequent access -- token exchange with the agent in the ACTOR slot (RFC 8693 s2.1; refresh_token is a registered subject_token_type per RFC 8693 s3):
POST /token HTTP/1.1
Host: as.mailco.example
Content-Type: application/x-www-form-urlencoded
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange
&subject_token=8xLOxBtZp8mYQx7CTO3G
&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Arefresh_token
&actor_token=eyJhbGciOiJFUzI1NiIsImtpZCI6IjIwMjYtMDctMTQifQ...
&actor_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Ajwt
&client_id=https%3A%2F%2Facme.agents.platform.example%2F
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJFUzI1NiIsImtpZCI6InRlbmFuY3ktMjAyNi0wNyJ9...
actor_token = the agent's ordinary WAG assertion, byte-for-byte the shape from counter-example 1 (iss=tenancy issuer, sub=wimse://.../agent/7f3d9a2e, aud=https://as.mailco.example, fresh exp/iat/jti, Properties).
AS enforcement (the WAG grant-binding rule): actor_token verifies under the allowlisted issuer AND actor_token.sub == grant G's consented agent AND subject_token belongs to G. Any mismatch: invalid_grant.
Issued access token:
{
"iss": "https://as.mailco.example",
"sub": "paul@acme.example",
"client_id": "https://acme.agents.platform.example/",
"aud": "https://mail.mailco.example/",
"scope": "email.read",
"act": {
"iss": "https://acme.agents.platform.example",
"sub": "wimse://acme.agents.platform.example/agent/7f3d9a2e"
},
"exp": 1785272280
}
Consent names the individual agent (the advocate's 'no wire form at all' is false -- RFC 9396 is the wire form, an RFC deployed in FAPI 2 ecosystems, vs their stack of three pre-RFC drafts). Paul's security page lists ONE app with per-agent grant rows underneath -- structural family grouping, no prefix-parsing of client_id, no thousand-corpse client list. Revocation is one grant row. Retirement beats the advocate's: access dies deterministically at the next exchange when the platform stops signing the agent's assertions (WAG's cessation semantics), versus CIMD s8.4.1's discretionary MAY firing on an eventual metadata re-fetch. The refresh token lives with the durable confidential tenancy client, so no instance-key escrow problem. Honest cost, conceded: the actor_token-matches-consented-agent binding is WAG profile text still to be written, and token exchange with actor_token is not universal in deployed ASes -- but it is registered RFC 8693 grammar, and the advocate's alternative needs ATTEST+CIMD support that is strictly rarer.
Acme runs idp.acme.example. Paul asks the agent to file tickets as him at saas.example. The agent obtains an ID-JAG from the IdP by token exchange, then redeems it at the resource AS. The agent authenticates as itself (Example 1's attestation) at both endpoints; the grant slot carries only statements about Paul.
Step 1, token exchange at the enterprise IdP (IDJAG-04 s4.3, RFC 8693):
POST /token HTTP/1.1
Host: idp.acme.example
Content-Type: application/x-www-form-urlencoded
OAuth-Client-Attestation: <platform-signed attestation, sub = https://acme.agents.platform.example/agents/7f3d9a2e>
OAuth-Client-Attestation-PoP: <instance-signed, aud = https://idp.acme.example>
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange
&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aid-jag
&subject_token=eyJraWQiOiJpZHAtMjAyNi0wNiIs... (Paul's ID token, aud = the agent's client_id, per s4.3.3 audience check)
&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aid_token
&audience=https%3A%2F%2Fas.saas.example
&scope=tickets.write
Returned ID-JAG, decoded (IDJAG s3.1):
{"typ":"oauth-id-jag+jwt","alg":"ES256","kid":"idp-2026-06"}
.
{
"iss": "https://idp.acme.example",
"sub": "paul@acme.example",
"aud": "https://as.saas.example",
"client_id": "https://acme.agents.platform.example/agents/7f3d9a2e",
"scope": "tickets.write",
"jti": "9a8b7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d",
"iat": 1785271680,
"exp": 1785272280
}
(signature: enterprise IdP key)
Step 2, redemption at the resource AS (IDJAG s4.4, RFC 7523 s2.1):
POST /token HTTP/1.1
Host: as.saas.example
Content-Type: application/x-www-form-urlencoded
OAuth-Client-Attestation: <same platform-signed attestation>
OAuth-Client-Attestation-PoP: <fresh instance-signed PoP, aud = https://as.saas.example, new jti>
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
&assertion=<ID-JAG from step 1>
&resource=https%3A%2F%2Fapi.saas.example%2F
AS checks per IDJAG s4.4.1: ID-JAG signature against allowlisted idp.acme.example, and client_id claim equals the attestation-authenticated client. Issued access token:
{
"iss": "https://as.saas.example",
"sub": "paul@acme.example",
"client_id": "https://acme.agents.platform.example/agents/7f3d9a2e",
"aud": "https://api.saas.example/",
"scope": "tickets.write",
"exp": 1785272280
}
Two authorities, two artifacts, two slots: the IdP signs the statement about Paul, the platform signs the statement about the agent, and the AS composes them; neither party testifies outside its competence. The issued token carries who it is about (sub) and who acted (client_id) in fields every RS and SIEM already reads, with no act chain needed for the single-hop case; genuine multi-hop delegation still nests via RFC 8693 s4.1 on top. WAG as drafted cannot run this flow at all: s4.4.1's mandatory client_id match has nothing to match on an unauthenticated request, and the D4 fallback (platform authenticates as THE client) makes the ID-JAG name the platform, erasing the individual agent from the one flow where a human is on the hook. IdP-side client registration is handled by CIMD as a shared global client_id namespace, per IDJAG s5.7 verbatim.
Acme runs idp.acme.example and has registered ONE SSO relying party: the platform tenancy app (this is how enterprise SSO actually works -- admins register applications, not hours-old agent instances). Paul asks the agent to file tickets as him at saas.example. The platform obtains an ID-JAG by token exchange, presenting Paul's ID token as subject_token and the agent's WAG assertion as actor_token (RFC 8693 s2.1: 'a security token that represents the identity of the acting party'), then redeems it at the resource AS. Three authorities each sign only what they know: IdP about Paul and the delegation, platform about the agent, tenancy key about the caller.
Step 1, token exchange at the enterprise IdP (IDJAG-04 s4.3 over RFC 8693):
POST /token HTTP/1.1
Host: idp.acme.example
Content-Type: application/x-www-form-urlencoded
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange
&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aid-jag
&subject_token=eyJraWQiOiJpZHAtMjAyNi0wNiJ9...
&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aid_token
&actor_token=eyJhbGciOiJFUzI1NiIsImtpZCI6IjIwMjYtMDctMTQifQ...
&actor_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Ajwt
&audience=https%3A%2F%2Fas.saas.example
&scope=tickets.write
&client_id=https%3A%2F%2Facme.agents.platform.example%2F
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJFUzI1NiIsImtpZCI6InRlbmFuY3ktMjAyNi0wNyJ9...
subject_token decoded (Paul's ID token -- aud is the SSO app Acme's admin ACTUALLY registered, satisfying IDJAG s4.3.3's audience == authenticated client check with a token an enterprise IdP really issues):
{
"iss": "https://idp.acme.example",
"sub": "paul@acme.example",
"aud": "https://acme.agents.platform.example/",
"exp": 1785272580,
"iat": 1785271680
}
(signature: enterprise IdP key)
actor_token decoded (the agent's ordinary WAG assertion, unchanged shape, moved to the registered actor slot):
{
"iss": "https://acme.agents.platform.example",
"sub": "wimse://acme.agents.platform.example/agent/7f3d9a2e",
"aud": ["https://idp.acme.example"],
"exp": 1785271980,
"iat": 1785271680,
"jti": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
"name": "Support Triage Agent",
"namespace": "acme/support"
}
(signature: tenancy issuer key; the IdP trusts this issuer by the same one-row-per-tenancy allowlist WAG already defines)
Returned ID-JAG (IDJAG s3.1 claims; act emitted per RFC 8693 s4.1 semantics native to the exchange that minted it -- actor_token in, act out):
{"typ":"oauth-id-jag+jwt","alg":"ES256","kid":"idp-2026-06"}
.
{
"iss": "https://idp.acme.example",
"sub": "paul@acme.example",
"aud": "https://as.saas.example",
"client_id": "https://acme.agents.platform.example/",
"scope": "tickets.write",
"act": {
"iss": "https://acme.agents.platform.example",
"sub": "wimse://acme.agents.platform.example/agent/7f3d9a2e"
},
"jti": "9a8b7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d",
"iat": 1785271680,
"exp": 1785272280
}
(signature: enterprise IdP key)
Step 2, redemption at the resource AS (IDJAG s4.4, RFC 7523 s2.1 grant slot):
POST /token HTTP/1.1
Host: as.saas.example
Content-Type: application/x-www-form-urlencoded
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
&assertion=<ID-JAG from step 1>
&resource=https%3A%2F%2Fapi.saas.example%2F
&client_id=https%3A%2F%2Facme.agents.platform.example%2F
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJFUzI1NiIsImtpZCI6InRlbmFuY3ktMjAyNi0wNyJ9...
AS checks per IDJAG s4.4.1: ID-JAG signature against allowlisted idp.acme.example, and the client_id claim (https://acme.agents.platform.example/) equals the authenticated client. MATCH -- the check the advocate said WAG cannot satisfy is satisfied, with the agent intact in act.
Issued access token:
{
"iss": "https://as.saas.example",
"sub": "paul@acme.example",
"client_id": "https://acme.agents.platform.example/",
"aud": "https://api.saas.example/",
"scope": "tickets.write",
"act": {
"iss": "https://acme.agents.platform.example",
"sub": "wimse://acme.agents.platform.example/agent/7f3d9a2e"
},
"exp": 1785272280
}
This is the McGuinness ai-agent-instance Appendix B token shape verbatim ('alice@example.com via agent instance sess-9f2c'): user in sub, agent in act.sub, client_id = the application -- each field keeping its RFC-defined meaning, with the audit question answered by the two fields RFC 8693 registered for exactly principal-vs-actor. Contrast the advocate's version, which requires Paul's ID token to carry aud = an hours-old per-agent URL: no enterprise IdP issues that, so their flow needs either per-agent SSO registration (violating WAG's Instantiation MUST NOT, and DCR-at-the-IdP) or blanket origin trust (the same tenancy row WAG already has, gaining nothing). Multi-hop chaining stays clean: agent A handing to agent B nests act per RFC 8693 s4.1, with no bogus first link, because the caller (platform client) was never in the chain. Conceded gap: IDJAG-04 does not yet specify act in the ID-JAG, so this needs a one-line interop note or IDJAG extension -- smaller and better-precedented than the advocate's dependence on unshipped ATTEST transports plus IdPs resolving CIMD client_ids (their own weakest_points #7).