diff --git a/draft-hardt-aauth-r3.md b/draft-hardt-aauth-r3.md index 556a80b..a7cbb1d 100644 --- a/draft-hardt-aauth-r3.md +++ b/draft-hardt-aauth-r3.md @@ -481,12 +481,16 @@ Base claims (from AAuth Protocol): - `dwk`: `aauth-resource.json` - `aud`: Auth server URL - `jti`: Unique token identifier -- `agent`: Agent identifier +- `ps`: The `iss` of the person token the resource verified +- `sub`: The `sub` of that person token, identifying the person this authorization is for +- `person_token_jti`: The `jti` of that person token, binding this resource token to it - `agent_jkt`: JWK Thumbprint of the agent's signing key - `iat`: Issued at timestamp - `exp`: Expiration timestamp - `scope`: Requested scopes (optional) +A resource token carries no agent identifier; the recipient learns the agent's identity from the agent token that signs the token request. + R3 extension claims: - **`r3_uri`** (REQUIRED for R3): The URI where the AS can fetch the R3 document. The AS authenticates itself using an HTTP Message Signature. - **`r3_s256`** (REQUIRED for R3): The SHA-256 hash of the R3 document at `r3_uri`, base64url-encoded without padding. @@ -505,7 +509,9 @@ R3 extension claims: "dwk": "aauth-resource.json", "aud": "https://as.example.com", "jti": "rt-8f3a2b", - "agent": "assistant@agent.example", + "ps": "https://ps.example", + "sub": "8f14e45fceea167a5a36dedd4bea2543", + "person_token_jti": "pt-3ab910", "agent_jkt": "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs", "r3_uri": "https://calendar.example.com/r3/a1b2c3d4", "r3_s256": "aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789abcd", @@ -532,10 +538,14 @@ When the AS receives a resource token containing `r3_uri` and `r3_s256`, it MUST 1. Validate the resource token signature per AAuth Protocol ([@!I-D.hardt-oauth-aauth-protocol]). 2. Fetch the R3 document at `r3_uri`. The AS MAY use a cached copy if the cache entry was stored with the same `r3_s256` value. 3. Compute the SHA-256 hash of the bytes received and compare it to `r3_s256`. If the hashes do not match, the AS MUST reject the resource token. -4. Record `r3_uri` and `r3_s256` in its audit log alongside the token issuance event, the agent identifier, and the timestamp. +4. Record `r3_uri` and `r3_s256` in its audit log alongside the token issuance event, the timestamp, `ps` and `sub` from the resource token, `agent_jkt` from the resource token, and the agent identifier. 5. Use the `operations` section for policy evaluation. 6. Include `r3_uri`, `r3_s256`, `r3_granted`, and (if applicable) `r3_per_call` in the issued auth token. +The agent identifier in step 4 does not come from the resource token. No token a resource issues carries one ([@!I-D.hardt-oauth-aauth-protocol]): the resource token binds to the agent's key through `agent_jkt` and names the person through `ps` and `sub`. The AS takes the agent identifier from the `sub` of the `agent_token`, which the PS is REQUIRED to send alongside the resource token on the PS-to-AS token request. Where the PS also sends a `subagent_token`, that token's `sub` is the agent the auth token is bound to and is the identifier the AS records; the `agent_token`'s `sub` is its parent, and an AS that distinguishes them SHOULD record both. + +An AS reached any other way than a PS-to-AS token request has no agent token and therefore no agent identifier. It can still record `agent_jkt`, which is what a later presentation of the auth token is checked against, but it MUST NOT infer an agent identity it was not given. + ## Caching The AS is not required to retain R3 documents beyond their immediate use in token issuance. Its audit log records `r3_uri` and `r3_s256`, which is enough to re-fetch and verify the document later. @@ -549,12 +559,15 @@ Base claims (from AAuth Protocol): - `dwk`: `aauth-access.json` (issued by an AS) or `aauth-person.json` (issued by a PS) - `aud`: Resource URL - `jti`: Unique token identifier -- `agent`: Agent identifier +- `ps`: The person server the person is represented by. Equal to `iss` when a PS issued the token +- `sub`: Directed user identifier (REQUIRED), copied from the resource token. An opaque string, unique within `iss`, that the PS SHOULD derive pairwise per resource - `cnf`: Confirmation claim with `jwk` containing the agent's public key - `iat`: Issued at timestamp - `exp`: Expiration timestamp -- `sub`: User identifier (conditional) -- `scope`: Authorized scopes (conditional) +- `scope`: Authorized scopes (optional) +- `mission_s256`: SHA-256 hash of the approved mission JSON (optional), present when the auth token was issued in the context of a mission + +An auth token carries no agent identifier; `cnf` binds it to one key, and the resource enforces against `sub` and the R3 claims below. R3 extension claims: - **`r3_uri`** (REQUIRED for R3): The URI of the R3 document that was in effect at approval time. @@ -576,8 +589,8 @@ R3 extension claims: "dwk": "aauth-access.json", "aud": "https://calendar.example.com", "jti": "at-9d4c1e", - "agent": "assistant@agent.example", - "sub": "user:alice@example.com", + "ps": "https://ps.example", + "sub": "8f14e45fceea167a5a36dedd4bea2543", "cnf": { "jwk": { "kty": "OKP", "crv": "Ed25519", "x": "NzbLsXh8uDCcd...", "alg": "Ed25519" } }, "r3_uri": "https://calendar.example.com/r3/a1b2c3d4", @@ -676,7 +689,7 @@ Whether the AS or PS additionally *machine-evaluates* `parameters` (for example, A party fetching `r3_uri` MUST authenticate itself with an HTTP Message Signature as defined in the AAuth Protocol ([@!I-D.hardt-oauth-aauth-protocol]). The resource MUST reject any request that is not signed by a party entitled to that document. Two parties are: - the AS named in the `aud` of a resource token carrying that `r3_uri`; and -- the PS of the agent that resource token was issued to, which the resource knows from the `ps` claim of the agent token the agent presented. +- the PS that issued the person token the resource verified before issuing that resource token, which is the `ps` claim of the resource token itself ([@!I-D.hardt-oauth-aauth-protocol]). In three-party access these are the same party — `aud` is the PS. In four-party access both fetch, and for different reasons: the AS reads `operations` to evaluate policy, the PS reads `display` to render consent (#r3-processing). Any other signer MUST be rejected. @@ -764,6 +777,9 @@ There are currently no known implementations. *Note: This section is to be removed before publishing as an RFC.* - draft-hardt-aauth-r3-02 + - Brought the base-claim recitals in Resource Token Extensions and Auth Token Extensions up to AAuth Protocol -11. The `agent` claim is gone from both tokens: a resource token now carries `ps`, `sub`, and `person_token_jti`, and an auth token carries `ps` and a REQUIRED directed `sub`, with `mission_s256` OPTIONAL. The examples were updated to match, including the auth token's `sub`, which showed an email address where the value is an opaque directed identifier. + - AS Processing required the AS to log "the agent identifier" against a resource token that no longer carries one. The step now names identifiers the AS actually holds — `ps`, `sub`, and `agent_jkt` from the resource token — and says where the agent identifier does come from: the `sub` of the `agent_token` the PS is REQUIRED to send on the PS-to-AS token request, or of the `subagent_token` where one is present. An AS reached any other way has no agent token and MUST NOT infer an agent identity. + - R3 Document Access Restriction identified the entitled PS by the `ps` claim of the agent token. Under -11 an agent presents a person token in place of its agent token at the authorization endpoint, so the resource may never see an agent token, and that claim is OPTIONAL in any case. The entitled PS is now the issuer of the person token the resource verified, which is the REQUIRED `ps` claim of the resource token the resource itself issued. - Added operation access annotations: a resource states, on the operation in its own vocabulary, which credential the operation requires and whether it consumes budget. The vocabulary is where they go because it is the only description of the resource's operations an agent can read — R3 documents are PS- and AS-only. Annotations are sparse against the resource-wide `access_mode`, replace it rather than intersect with it, and stay advisory: the runtime `AAuth-Requirement` remains authoritative. Encodings defined for MCP, OpenAPI, AsyncAPI, and OData; none for gRPC, GraphQL, or WSDL, whose discovery mechanisms do not carry annotations to a generic caller. - Added the `access_mode` value `per-call`, for a resource or an operation that authorizes each invocation individually against its parameters, and registered it in the AAuth Access Mode Value Registry. - Renamed the auth token claim `r3_conditional` to `r3_per_call`, matching the `per-call` access mode. "Conditional" did not say what the condition was. diff --git a/draft-hardt-oauth-aauth-protocol.md b/draft-hardt-oauth-aauth-protocol.md index e334e4d..e5b2081 100644 --- a/draft-hardt-oauth-aauth-protocol.md +++ b/draft-hardt-oauth-aauth-protocol.md @@ -352,7 +352,7 @@ A resource that needs more than identity for a particular operation challenges f ### PS Authorization Access (Three-Party) -The resource has no separate access server — it accepts identity claims from whichever PS the agent declares, and applies its own policy on the resulting claims. The resource discovers the agent's PS from the `ps` claim in the agent token and issues a resource token (#resource-tokens) with `aud` = PS URL. The agent obtains the resource token either by calling the resource's `authorization_endpoint` (if published in resource metadata) or by receiving a `401` challenge with `requirement=auth-token` when calling the resource directly (#requirement-auth-token). The agent sends the resource token to the PS's token endpoint (#ps-token-endpoint), and the PS returns an auth token (#auth-tokens) asserting identity claims about the user (`sub`, optionally `email`, `tenant`, `groups`, `roles`) and confirming user consent for the scope the resource requested. The resource applies its own access policy on the resulting claims. Any agent's PS can assert identity claims to any resource without bilateral setup; the resource namespaces those claims by the asserting PS — the same `sub` value from a different PS is a different subject. As in many OIDC deployments, registration and login share a single flow (see (#trust-posture-in-ps-asserted-access) for how the resource matches or creates a user record from `(iss, sub)`). +The resource has no separate access server — it accepts identity claims from whichever PS the agent declares, and applies its own policy on the resulting claims. The `ps` claim in the agent token tells the resource that the agent has a person server and which one, which is how the resource knows to challenge for a person token at all. The PS the resource then acts on is the `iss` of the person token it verifies (#person-tokens); the resource copies that value into the `ps` claim of the resource token (#resource-tokens) it issues with `aud` = PS URL. The agent obtains the resource token either by calling the resource's `authorization_endpoint` (if published in resource metadata) or by receiving a `401` challenge with `requirement=auth-token` when calling the resource directly (#requirement-auth-token). The agent sends the resource token to the PS's token endpoint (#ps-token-endpoint), and the PS returns an auth token (#auth-tokens) asserting identity claims about the user (`sub`, optionally `email`, `tenant`, `groups`, `roles`) and confirming user consent for the scope the resource requested. The resource applies its own access policy on the resulting claims. Any agent's PS can assert identity claims to any resource without bilateral setup; the resource namespaces those claims by the asserting PS — the same `sub` value from a different PS is a different subject. As in many OIDC deployments, registration and login share a single flow (see (#trust-posture-in-ps-asserted-access) for how the resource matches or creates a user record from `(iss, sub)`). ~~~ ascii-art Agent Resource PS @@ -551,7 +551,7 @@ Acquiring the agent token — the AP-side enrollment ceremony, including per-pla - The PS maintains the association between an agent and its person. This association is typically established when the person first authorizes the agent at the PS via the interaction flow. An organization administrator may also pre-authorize agents for the organization. - The PS MAY establish a direct communication channel with the user (e.g., email, push notification, or messaging) to support out-of-band authorization, approval notifications, and revocation alerts. - Person servers publish metadata at `/.well-known/aauth-person.json` (#ps-metadata). -- The resource discovers the agent's PS from the `ps` claim in the agent token and issues resource tokens with `aud` = PS URL. +- The `ps` claim in the agent token tells the resource the agent has a person server, before the resource has anything else to go on. The PS a resource acts on is the `iss` of the person token it verifies (#person-tokens); the resource copies that value into the resource token's `ps` claim and issues with `aud` = PS URL. **Federated authorization access (four-party):** @@ -614,7 +614,7 @@ Required payload claims: - `exp`: Expiration timestamp. Agent tokens SHOULD NOT have a lifetime exceeding 24 hours. Optional payload claims: -- `ps`: The HTTPS URL of the agent's person server. Configured per agent instance. When present, resources can discover the agent's PS from the agent token. This claim is distinct from `iss` (which identifies the agent provider that issued the token). +- `ps`: The HTTPS URL of the agent's person server. Configured per agent instance. When present, it tells a resource that the agent has a person server and which one, before the resource has verified a person token — enough to decide whether to challenge for one. The PS of an issued authorization is the `iss` of the person token the resource verified (#person-token-structure), not this claim. This claim is distinct from `iss` (which identifies the agent provider that issued the token). - `parent_agent`: Sub-agent marker (#sub-agents). When present, the agent is a sub-agent and the value is the identifier of its parent agent. A sub-agent MUST NOT request authorization directly; its parent obtains auth tokens on its behalf (#sub-agents). Agent providers MAY include additional claims in the agent token. Companion specifications may define additional claims for use by PSes or ASes in policy evaluation — for example, software attestation, platform integrity, secure enclave status, workload identity assertions, or software publisher identity. PSes and ASes MUST ignore unrecognized claims. @@ -3295,12 +3295,13 @@ This specification registers the following claims in the IANA "JSON Web Token Cl | Claim Name | Claim Description | Change Controller | Reference | |---|---|---|---| | `dwk` | Discovery Well-Known document name | IETF | This document | -| `ps` | Person Server URL | IETF | This document | -| `agent` | Agent identifier | IETF | This document | -| `agent_jkt` | JWK Thumbprint of the agent's signing key | IETF | This document | +| `ps` | Person server URL — the agent's person server in an agent token, and the person server whose namespace `sub` belongs to in a resource or auth token | IETF | This document | +| `agent_jkt` | JWK Thumbprint of the agent's signing key, in a resource token | IETF | This document | | `parent_agent` | Parent agent identifier in a sub-agent's agent token | IETF | This document | +| `person_token_jti` | The `jti` of the person token a resource token is bound to | IETF | This document | | `mission_s256` | SHA-256 hash of the approved mission JSON, in person, resource, and auth tokens | IETF | This document | -| `ps` | Person server URL in an auth token | IETF | This document | +| `account` | Account the authorization is for, in resource and auth tokens | IETF | This document | +| `interaction` | Resource interaction step required before authorization, an object with `url` and `code`, in a resource token | IETF | This document | ## AAuth Requirement Value Registry @@ -3395,6 +3396,8 @@ The following implementations are known: *Note: This section is to be removed before publishing as an RFC.* - draft-hardt-oauth-aauth-protocol-11 + - Three places still said a resource discovers the agent's PS from the `ps` claim in the agent token — the three-party access mode, the bootstrapping requirements, and the claim's own definition — which the Design Rationale already contradicted. The agent token's `ps` is the advance signal that the agent has a person server, which is what lets a resource decide to challenge for a person token. The PS of an issued authorization is the `iss` of the person token the resource verified, which the resource copies into the resource token's `ps`. + - Corrected the JWT Claims Registrations table. `ps` was registered twice; the two rows are collapsed into one covering agent, resource, and auth tokens. `agent` is no longer a claim in any token and its row is removed — it survives only as a member of the mission blob, which is not a JWT. Added `person_token_jti`, `account`, and `interaction`, none of which were registered. - Established the AAuth Access Mode Value Registry, seeded with `agent-token`, `person-token`, `session-token`, and `auth-token`. The `access_mode` field was described as a closed list of four, which left no room for the `per-call` value R3 defines; the registry is how the other extensible AAuth value spaces are already handled. - Pointed `access_mode` at R3 operation access annotations. Two places said a resource MAY apply different modes to different endpoints without naming a mechanism for saying which. - Added the person token (`aa-person+jwt`), issued by a PS to identify the person to one resource. Presented via `Signature-Key` in place of the agent token. A resource MUST verify one before issuing a resource token. Lifetime capped at 1 hour, as for auth tokens.