mentionable.dev

DID-backed OAuth Federation for A2A — Mentionable v0.1

Status: Draft v0.1; implemented by @mentionable/connector-kit and vicoop-bridge.

Profile / Agent Card extension URI: https://mentionable.dev/ns/oauth-federation/v0.1

Last updated: 2026-09-03

This document defines an OAuth 2.0 Token Exchange profile through which a Mentionable Connector turns a short-lived, DID-signed assertion about a platform principal into a bearer access token for one A2A agent. It is a profile of RFC 8693, RFC 7523, RFC 8414, and RFC 8707; it does not define a new OAuth grant.

The exact compact JWTs and form fields in fixtures/oauth-federation/v0.1/ are the executable conformance vectors for this document. The string constants in packages/connector-kit/src/oauth-federation.ts are the byte-level registry. If prose and a committed fixture disagree, that is a specification defect and MUST be resolved before claiming conformance.


0. Conformance language

The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, NOT RECOMMENDED, MAY, and OPTIONAL in this document are to be interpreted as described in BCP 14 (RFC 2119 and RFC 8174) when, and only when, they appear in all capitals.

1. Scope and final v0.1 topology

v0.1 supports only direct delegation by a relay-verified Connector:

platform principal S
        |
        v
Connector C
  assertion issuer = C
  OAuth client      = C
  A2A actor         = C
        |
        v
Authorization Server / STS ---- access token ----> A2A Resource Server
                                                       |
                                                       v
                                                  target agent

For every accepted exchange:

subject_token.iss == authenticated client_id == derived actor
principal         == verified subject_token.sub
purpose           == delegation

The Authorization Server (AS), Security Token Service (STS), A2A Resource Server, and target agent are separate logical roles. A deployment MAY co-locate them, but MUST preserve their trust boundaries.

The profile deliberately separates:

Client authentication alone MUST NOT authorize a platform principal. DID signature verification alone MUST NOT authorize one either. Authorization requires an exact receiver-owned caller-policy match (§8).

This topology is the final result of the two v0.1 amendments on issue #618. Amendment 2 supersedes the earlier bare Connector self-assertion rule: task-only renewal uses a task-continuation assertion that preserves the originating platform principal and names one task.

2. Identifiers and canonicalization

2.1 Connector identity

The Connector issuer and OAuth client_id MUST be the same exact DID string. The reference implementation uses did:web. v0.1 comparisons are exact string comparisons; implementations MUST NOT case-fold, trim, redirect, or otherwise rewrite a presented issuer, client ID, verification-method ID, resource, subject, method, or task ID during an authorization decision.

2.2 Platform subject and method

sub identifies the platform principal in the canonical namespace of the authentication method. For Slack workspace members the form is:

slack:<workspace-or-team-id>/<user-id>

For example, the fixtures use slack:T0123456/U0456789. The method is a non-empty URI identifying how the Connector authenticated the subject; the fixtures use:

urn:mentionable:auth:slack-workspace-member:v0.1

Neither string is globally authoritative by itself. The collision-safe authorization identity is the exact tuple:

(issuer DID, method URI, platform subject)

Receivers MUST key policy on all three components and MUST use an unambiguous tuple representation, such as structured columns or length-prefixed components. Delimiter concatenation without escaping is not collision-safe.

2.3 Agent aliases and canonical resource

An @agent@domain address, its acct: URI, and an advertised agent DID are discovery aliases, not OAuth resources. A client resolves the alias to an Agent Card and obtains the canonical resource from §3. The canonical resource MUST be the agent’s advertised, version-independent A2A endpoint URL and MUST be sent verbatim as the single RFC 8707 resource value.

A task ID, A2A context ID, credential ID, assertion jti, address, or DID MUST NOT be treated as an authorization credential or substituted for the canonical resource.

3. Discovery

3.1 Agent Card extension

An A2A server advertises this profile with an exact a2a.capabilities.extensions[] entry:

{
  "uri": "https://mentionable.dev/ns/oauth-federation/v0.1",
  "description": "DID-backed OAuth 2.0 Token Exchange for exact Mentionable federated callers.",
  "required": true,
  "params": {
    "authorization_server": "https://bridge.example/.well-known/oauth-authorization-server",
    "resource": "https://bridge.example/agents/AGENT_ID"
  }
}

The uri match MUST be exact. Scheme changes, case changes, a trailing slash, or another version are non-matches. params.authorization_server and params.resource are REQUIRED non-empty strings. The former is the URL of the RFC 8414 metadata document itself; clients MUST NOT derive another well-known path from it. The latter is the canonical resource from §2.3.

Before exchanging or delivering a token, a Connector MUST verify that the extension resource exactly equals the A2A endpoint to which it will send the request. A mismatch MUST fail closed.

3.2 Authorization Server metadata

The URL above MUST return an RFC 8414 JSON object. At minimum, a v0.1 server MUST advertise:

{
  "issuer": "https://bridge.example",
  "token_endpoint": "https://bridge.example/oauth/token",
  "grant_types_supported": ["urn:ietf:params:oauth:grant-type:token-exchange"],
  "token_endpoint_auth_methods_supported": ["private_key_jwt"],
  "token_endpoint_auth_signing_alg_values_supported": ["EdDSA"],
  "scopes_supported": [
    "a2a:message.send",
    "a2a:message.stream",
    "a2a:task.read",
    "a2a:task.cancel",
    "a2a:task.resubscribe",
    "a2a:task.push-config"
  ],
  "subject_token_types_supported": ["urn:ietf:params:oauth:token-type:jwt"]
}

subject_token_types_supported is a profile metadata extension. A server MAY advertise other grants, scopes, or authentication methods for unrelated profiles and MAY publish additional profile-selection metadata. A client MUST verify the token-exchange grant and MUST verify private_key_jwt, EdDSA, the JWT subject-token type, and every scope it intends to request before the exchange. The Agent Card extension remains the v0.1 profile-selection signal. An absent grant_types_supported does not imply token-exchange support.

Production token endpoints MUST use HTTPS. Fetchers MUST apply normal SSRF defenses to both Agent Card and metadata retrieval, including blocking loopback, link-local, and private-network targets unless explicitly operating in a local-development mode.

4. JWT and DID verification rules

4.1 Common JOSE header

Every v0.1 assertion is a compact JWS using this protected-header shape:

{
  "alg": "EdDSA",
  "typ": "<one exact type from §5>",
  "kid": "did:web:connector.example#oauth-key-2026-08"
}

The verifier MUST pin alg to EdDSA and MUST match typ to the presentation slot before signature verification. Algorithm inference from the key or acceptance of none is forbidden. The kid MUST be an absolute fragment DID URL whose base is exactly iss (<iss>#<non-empty-fragment>), and that exact value MUST occur in the issuer DID document’s assertionMethod relationship. The complete kid MUST satisfy the URI lexical rules in §5.1; in particular its fragment delimiter MUST occur exactly once and raw whitespace, controls, backslashes, invalid percent escapes, and unmatched or non-authority brackets are forbidden.

4.2 Common claims and time profile

All three assertion types MUST contain well-typed iss, sub, aud, iat, exp, and jti claims.

ClaimRequirement
issExact Connector DID.
subType-specific subject from §5.
audThe exact token-endpoint URL. A string or a one-element array containing only that string is accepted.
iatNumericDate at issuance.
expNumericDate; exp - iat MUST be positive and MUST NOT exceed 600 seconds.
jtiNon-empty, freshly generated replay identifier.
nbfOPTIONAL; when present, the receiver MUST enforce it.

The default assertion lifetime is 300 seconds, the maximum is 600 seconds, and receiver clock tolerance is 60 seconds. A receiver MUST reject assertions that are expired beyond tolerance, issued in the future beyond tolerance, or whose nbf is in the future beyond tolerance.

The STS MUST consume each (iss, jti) once across all instances and retain the replay record through at least exp + 60 seconds. Subject and client assertions in one request therefore require distinct fresh jti values. Production replay storage MUST provide cross-instance atomic registration; the reference cache adapter accepts either a synchronous boolean or an asynchronous boolean result so database and Redis implementations can perform that registration durably. The connector-kit combined verifyTokenExchange entry point requires a replay cache and fails fast when it is absent; it MUST NOT return ok: true without registering both assertions. The lower-level single-assertion verifier keeps cache-less operation only for isolated cryptographic component testing, not for an STS authorization decision.

4.3 DID document and key authorization

An issuer signature is eligible only after the receiver has matched a local trust/policy entry (§8.1). Only then may it resolve the issuer DID document. The document id MUST equal iss. The exact kid MUST occur in assertionMethod, either as a string/id-only reference to a top-level verificationMethod or as a full verification method embedded directly in the relationship. It MUST resolve to a verification method controlled by that issuer and MUST expose an Ed25519 public JWK. Embedded and referenced methods receive identical checks. The method type MUST be exactly JsonWebKey, controller MUST equal iss, and publicKeyJwk MUST have kty: "OKP", crv: "Ed25519", and a non-empty string x; it MUST NOT contain private d material:

{
  "@context": ["https://www.w3.org/ns/did/v1", "https://w3id.org/security/jwk/v1"],
  "id": "did:web:connector.oauth-fixtures.mentionable.dev",
  "verificationMethod": [
    {
      "id": "did:web:connector.oauth-fixtures.mentionable.dev#oauth-fixture-2026-08",
      "type": "JsonWebKey",
      "controller": "did:web:connector.oauth-fixtures.mentionable.dev",
      "publicKeyJwk": {
        "crv": "Ed25519",
        "x": "UGfideXk63XnzqYdisiTAZ64iAHklpZox4h4kECItXU",
        "kty": "OKP"
      }
    }
  ],
  "assertionMethod": ["did:web:connector.oauth-fixtures.mentionable.dev#oauth-fixture-2026-08"]
}

A key merely present in verificationMethod, or present under a different verification relationship, is unauthorized. A verification method MUST NOT combine publicKeyJwk and publicKeyMultibase. When one Ed25519 key also serves PlatformIdentityCredential v0.2, publishers MUST use distinct method IDs; the Slack reference Connector uses #<kid> for its Multikey and #<kid>-oauth-jwk for the OAuth JWK.

Connectors MUST sign only with their active key. During rotation, issuers MAY keep the prior public method in assertionMethod long enough for assertions already issued within the maximum lifetime and clock tolerance. DID resolvers SHOULD cache documents for no more than five minutes, honor shorter HTTP cache directives, and retry resolution once on an unknown kid or signature failure before final rejection. Removing a compromised method is an emergency revocation signal; receivers MUST NOT keep accepting it indefinitely from a stale cache.

5. Assertion profiles

The decoded JSON below uses the exact values in the committed fixtures. The fixture files contain the normative compact-JWT bytes and signatures.

5.1 Platform-subject assertion

This assertion is REQUIRED whenever any message scope is requested.

// protected header
{
  "alg": "EdDSA",
  "typ": "mentionable-subject-assertion+jwt",
  "kid": "did:web:connector.oauth-fixtures.mentionable.dev#oauth-fixture-2026-08"
}
// payload
{
  "mentionable_method": "urn:mentionable:auth:slack-workspace-member:v0.1",
  "iss": "did:web:connector.oauth-fixtures.mentionable.dev",
  "sub": "slack:T0123456/U0456789",
  "aud": "https://sts.oauth-fixtures.mentionable.dev/oauth/token",
  "iat": 1787097600,
  "exp": 1787097900,
  "jti": "b1a2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
}

mentionable_method MUST be in the profile’s conservative RFC 3986 absolute URI lexical subset: it has a valid scheme and non-empty scheme-specific part, uses only RFC 3986 ASCII URI characters and valid %HH escapes, and contains at most one fragment delimiter. Raw whitespace, controls, and backslashes are forbidden. Brackets are valid only as a balanced IPv6 host in a URI authority, and a URI using // MUST have a non-empty authority host. Both opaque URNs and hierarchical absolute URLs are valid. An authority permits at most one raw @; its userinfo and host MUST use their RFC 3986 component characters, and an explicit port MUST contain one or more decimal digits whose numeric value is between 0 and 65535 inclusive. A relative reference is not valid. sub MUST NOT equal iss. The assertion represents a fresh, Connector-verified platform event. A Connector MUST NOT mint it merely because it previously observed the same subject.

5.2 Task-continuation assertion

This assertion MAY be used only for an exchange whose requested scopes are all task scopes. It is the only task-only renewal subject in v0.1.

// protected header
{
  "alg": "EdDSA",
  "typ": "mentionable-task-continuation-assertion+jwt",
  "kid": "did:web:connector.oauth-fixtures.mentionable.dev#oauth-fixture-2026-08"
}
// payload
{
  "mentionable_task_id": "3f2504e0-4f89-41d3-9a0c-0305e82c3301",
  "iss": "did:web:connector.oauth-fixtures.mentionable.dev",
  "sub": "slack:T0123456/U0456789",
  "aud": "https://sts.oauth-fixtures.mentionable.dev/oauth/token",
  "iat": 1787097600,
  "exp": 1787097900,
  "jti": "c2b3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e"
}

sub is the originating platform principal captured when the task was created, not the Connector. mentionable_task_id MUST be a non-empty exact task ID. The Connector MAY re-mint a continuation assertion at each renewal, but MUST derive it from a persisted binding established by the fresh origin event and MUST use a new jti.

A continuation assertion requesting a2a:message.send or a2a:message.stream MUST be rejected with invalid_request. Bare Connector self-assertions are not accepted.

5.3 Client assertion (private_key_jwt)

The client authenticates under RFC 7523 with:

// protected header
{
  "alg": "EdDSA",
  "typ": "mentionable-client-assertion+jwt",
  "kid": "did:web:connector.oauth-fixtures.mentionable.dev#oauth-fixture-2026-08"
}
// payload
{
  "iss": "did:web:connector.oauth-fixtures.mentionable.dev",
  "sub": "did:web:connector.oauth-fixtures.mentionable.dev",
  "aud": "https://sts.oauth-fixtures.mentionable.dev/oauth/token",
  "iat": 1787097600,
  "exp": 1787097900,
  "jti": "e4d5f6a7-b8c9-4d0e-9f2a-3b4c5d6e7f8a"
}

iss, sub, and the form client_id MUST be identical. The explicit typ is profile-specific and intentionally is not the bare JWT used by some generic RFC 7523 servers; this provides cross-JWT confusion resistance under RFC 8725. Unauthenticated exchange is forbidden.

5.4 Prohibited assertion types

v0.1 defines no actor assertion, Connector self-assertion, VC presentation, or arbitrary platform token type. actor_token and actor_token_type MUST be absent. A PlatformIdentityCredential v0.2 MUST NOT be reinterpreted as any assertion above.

6. Token exchange

6.1 Request

The Connector sends an HTTPS POST with media type application/x-www-form-urlencoded. Every parameter below is single-valued; a duplicate occurrence MUST be rejected with invalid_request.

Parameterv0.1 value
grant_typeREQUIRED; urn:ietf:params:oauth:grant-type:token-exchange.
subject_tokenREQUIRED; compact JWT from §5.1 or §5.2.
subject_token_typeREQUIRED; urn:ietf:params:oauth:token-type:jwt.
resourceREQUIRED exactly once; canonical URL from the Agent Card.
scopeREQUIRED non-empty, ASCII-space-delimited set from §7.
requested_token_typeREQUIRED; urn:ietf:params:oauth:token-type:access_token.
client_idREQUIRED; Connector DID, equal to both assertion issuers.
client_assertion_typeREQUIRED; urn:ietf:params:oauth:client-assertion-type:jwt-bearer.
client_assertionREQUIRED; compact JWT from §5.3.

The valid fixture request is equivalent to:

POST /oauth/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&
subject_token=<valid/subject-assertion.json.jwt>&
subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Ajwt&
resource=https%3A%2F%2Fagents.oauth-fixtures.mentionable.dev%2Ffixture-agent%2Fa2a&
scope=a2a%3Amessage.send+a2a%3Atask.read&
requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&
client_id=did%3Aweb%3Aconnector.oauth-fixtures.mentionable.dev&
client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&
client_assertion=<valid/client-assertion.json.jwt>

The angle-bracket references denote the exact compact values embedded in valid/exchange-request-delegation.json, not literal wire strings.

The STS MUST authenticate the client assertion and verify the subject assertion independently, then re-check subject_token.iss == client_id on the verified claims. Before either assertion can cause DID resolution, it MUST invoke the receiver-owned policy lookup described in §8.1. Decoding unverified JOSE fields is used only to construct that lookup candidate and MUST NOT produce an authorization decision.

6.2 Success response

On success the STS returns the RFC 8693/RFC 6749 response below. The access token string is opaque to the client.

{
  "access_token": "<opaque access token>",
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
  "token_type": "Bearer",
  "expires_in": 300,
  "scope": "a2a:message.send a2a:task.read"
}

access_token, issued_token_type, and token_type are REQUIRED. issued_token_type MUST be exactly urn:ietf:params:oauth:token-type:access_token, and token_type MUST be exactly Bearer in v0.1. When present, expires_in MUST be a positive integer and scope MUST be a non-empty ASCII-space-delimited set from §7. A client MUST fail closed on malformed values and MUST NOT use the token if a returned scope omits any scope required for the requested operation. The STS SHOULD include expires_in and the granted scope; clients MUST honor both when present. A generic response parser MAY return a valid granted subset; the operation-specific caller MUST enforce its required scope before use. Access tokens MUST be short-lived; 300 seconds is RECOMMENDED and implemented by the first receiver. A response MUST NOT contain a refresh token.

The response and every error response MUST carry Cache-Control: no-store and Pragma: no-cache.

6.3 Error mapping and retry

Errors use the RFC 6749 JSON shape, with invalid_target from RFC 8693:

{
  "error": "invalid_grant",
  "error_description": "subject assertion verification failed"
}
ConditionError
Wrong or unsupported grant_typeunsupported_grant_type
Missing/duplicate/malformed parameter, missing/wrong requested_token_type, unexpected actor token, issuer/client mismatch, continuation with a message scopeinvalid_request
Missing/unsupported client auth, invalid client assertion, client identity mismatchinvalid_client
Client is not permitted to use token exchangeunauthorized_client
Missing, unknown, or unsupported scopeinvalid_scope
Missing, unknown, non-canonical, duplicate, or substituted resourceinvalid_target
Untrusted/disallowed platform tuple; cryptographic or assertion-profile failure (including expired/replayed/wrong-audience/unauthorized-kid/bad signature); task-binding mismatchinvalid_grant
Internal persistence, replay-store, or verifier failureserver_error

An STS SHOULD return HTTP 401 for invalid_client and HTTP 400 for the other client errors. It MAY return an opaque correlation identifier, but MUST NOT echo assertions, tokens, or sensitive claims in the response.

A Connector MUST NOT retry the same request bytes after any recognized OAuth client error. It may correct the request or mint fresh assertions. Transport failure, an unparseable 5xx response, or a 5xx server_error MAY be retried with backoff and fresh assertions. A 5xx server_error is retryable metadata for the caller; the reference client performs no automatic retry.

The boundary is intentional: failure to decode the JOSE compact serialization or obtain the minimum request-routing shape is a malformed request and maps to invalid_request. Once the JWT is structurally decoded, any cryptographic or assertion-profile failure maps to invalid_grant (except client-assertion failures, which map to invalid_client).

7. Resource and scope registry

Scopes are case-sensitive and separated only by ASCII space (%x20). A server MUST reject a token outside this closed v0.1 registry; it MUST NOT split on generic whitespace after profile validation.

ScopeAuthorized A2A operationClass
a2a:message.sendSend Message (message/send, SendMessage, or binding-equivalent operation).Message
a2a:message.streamSend Streaming Message (message/stream, SendStreamingMessage, or equivalent).Message
a2a:task.readGet one named task (tasks/get, GetTask, or equivalent).Task
a2a:task.cancelCancel one named task (tasks/cancel, CancelTask, or equivalent).Task
a2a:task.resubscribeResubscribe/subscribe to one named task (tasks/resubscribe, SubscribeToTask, or equivalent).Task
a2a:task.push-configCreate, get, list, or delete push-notification configuration for one named task. OPTIONAL per deployment.Task

An implementation maps these semantic operations consistently across its A2A JSON-RPC, gRPC, and HTTP+JSON bindings. a2a:task.push-config MUST appear in scopes_supported only when implemented.

A2A task-listing is not authorized by a2a:task.read in v0.1 because no request-scoped actor filter is standardized by this profile. A receiver MUST either reject federated listing or define a later profile version before offering it.

A platform-subject assertion MAY request message and task scopes together. A task-continuation assertion MUST request only task scopes. A message operation that names an existing task is also subject to the task ownership checks in §8.3, even though its required operation scope is a message scope.

8. Receiver policy and authorization context

8.1 Trust before network fetch

For a platform-subject exchange, the receiver first decodes only enough unverified data to construct (iss, mentionable_method, sub). It MUST check that exact tuple together with the exact resource and requested scope set against the target agent’s local allowed-caller policy before resolving a DID or performing any issuer-controlled network request. Missing or invalid mentionable_method MUST fail before policy lookup. A policy miss MUST fail as invalid_grant.

For a continuation exchange, which intentionally carries no method, the receiver first looks up the named task locally. The task’s stored profile, principal, actor, authorization key, resource, and permitted scope set MUST match the unverified (iss, sub, mentionable_task_id, resource, scopes) candidate before DID resolution. A missing task ID MUST fail before policy lookup. Cryptographic verification and verified-claim binding remain REQUIRED after this prefilter.

The connector-kit reference path exposes this boundary as the REQUIRED, async-capable authorizeCandidateBeforeFetch callback. It supplies a discriminated platform or continuation candidate containing the exact raw strings above. false maps to invalid_grant; a thrown policy-store error may propagate for the STS to map to server_error. Callback approval is not final authorization and MUST NOT be used without the subsequent verified result.

8.2 Derived context and access-token binding

After complete verification, the STS derives:

{
  "purpose": "delegation",
  "principal": "slack:T0123456/U0456789",
  "actor": "did:web:connector.oauth-fixtures.mentionable.dev"
}

A continuation exchange additionally derives the verified task binding:

{
  "purpose": "delegation",
  "principal": "slack:T0123456/U0456789",
  "actor": "did:web:connector.oauth-fixtures.mentionable.dev",
  "task_id": "3f2504e0-4f89-41d3-9a0c-0305e82c3301"
}

These objects match the expected.authorization values in the two valid exchange fixtures. act is a semantic output: the Resource Server MUST retain the Connector as actor. The access token may be self-contained or opaque, but the AS and Resource Server MUST securely associate it with:

Token bytes and assertion jti values MUST NOT be task ownership keys. The component implementing the policy callback MUST preserve the exact local authorization key it matched and bind that key to the issued token and any created task; the reference verifier’s boolean callback result does not carry or reconstruct that receiver-owned key.

8.3 A2A operation authorization and task continuity

The Resource Server MUST authenticate the bearer token on every protected A2A operation and enforce the token’s resource and required scope.

When a message creates a task, the server MUST atomically bind the task to the verified (principal, actor, profile, authorization key). For every later operation on a named task, including a message that continues it, the server MUST require all of:

  1. the token is valid for the same agent resource and carries the operation’s required scope;
  2. token principal equals the task’s bound principal;
  3. token actor equals the task’s bound actor;
  4. token authorization key equals the task’s bound authorization key and that receiver-owned policy remains active; and
  5. if the token has a task_id, it exactly equals the operated task.

The principal check applies even to a platform-subject token that carries task scopes but no token-level task binding. Actor-only matching would allow one user behind a multi-user Connector to access another user’s task and is non-conformant.

Knowledge or possession of a task ID or context ID never relaxes these checks. Access-token renewal does not break continuity when the newly issued token has the same verified principal, actor, resource, authorization key, and sufficient scopes.

9. Lifecycle, renewal, and revocation

There are no refresh tokens. Renewal is a new RFC 8693 exchange with a fresh client assertion and either:

The Connector MUST cache access tokens no longer than the returned expires_in and SHOULD keep a safety margin. It MUST NOT cache or replay assertions.

Removing or narrowing the exact receiver authorization MUST make outstanding access tokens issued under that authorization unusable and MUST prevent further operations on tasks bound to it. This must be consistent across Resource Server instances. Re-adding the same tuple authorizes new exchanges but MUST NOT reactivate a previously revoked token or historical task binding.

Key rotation and policy revocation are independent. A still-valid signature under a published key cannot override removed receiver policy, and removing a key does not itself create or expand caller policy.

10. PlatformIdentityCredential v0.2 compatibility

PlatformIdentityCredential v0.2 and this profile may use the same Connector DID and Ed25519 key material, but they have different purposes and wire bindings:

PropertyPlatformIdentityCredential v0.2OAuth federation v0.1
PurposeContext and attribution onlyAuthorization for one agent resource and scope set
Carriermessage.metadata.mentionable.verifiable_credentialsRFC 8693 token endpoint, then bearer header
BindingOuter messageId challenge and recipient domainToken-endpoint aud, canonical resource, scopes, and task binding
Verification methodMultikey / publicKeyMultibaseJsonWebKey / publicKeyJwk
Authorization effectNone by itselfOnly after receiver policy and token exchange

A receiver MUST NOT silently promote a credential into an OAuth assertion, accept it as subject_token, or infer authorization from its presence. Conversely, raw OAuth assertions and bearer tokens MUST NOT be inserted into the VC carrier, A2A message metadata, task history, or backend prompts.

A Resource Server MAY expose a normalized attestation derived from the verified tuple to its agent runtime for audit/context, but it MUST omit the raw JWT, signature, access token, private claims, and replay identifiers.

11. Security and privacy considerations

DPoP under RFC 9449 is the named future sender-constrained hardening path. It is not part of v0.1 conformance; v0.1 access tokens are bearer tokens.

12. Conformance

An issuer/client implementation conforms when it:

  1. recognizes the extension URI exactly and validates both params;
  2. discovers and validates the metadata capabilities;
  3. mints the exact JWT types and claims in §5 under an authorized JWK method;
  4. sends the exact form profile in §6 without actor tokens;
  5. uses a fresh event for message assertions and a stored origin binding for continuation assertions; and
  6. treats OAuth client errors as non-retryable without changing the request.

An STS/Resource Server implementation conforms when it:

  1. passes every applicable committed conformance fixture;
  2. applies exact caller policy through a required pre-fetch gate before any issuer-controlled resolution and preserves the matched local authorization key for token/task binding;
  3. authenticates the client and subject assertion independently;
  4. enforces verified issuer/client equality, replay, audience, resource, scope, and type-specific claims;
  5. derives exactly the principal/actor/task context in §8.2;
  6. binds issued authority and tasks as required by §§8–9; and
  7. keeps raw assertions, proofs, and tokens out of task and agent-visible data.

The fixture manifest includes accepted platform, continuation, client, and exchange cases plus expired, not-yet-valid, zero/negative/excessive-TTL, wrong audience, wrong type/algorithm, invalid method URI, issuer-external kid, malformed/private/mixed DID verification methods, unauthorized key, tampered signature, replayed jti, issuer/client mismatch, undecodable/missing issuer, duplicate/substituted resource, continuation/message-scope confusion, missing task ID, missing/empty scope, wrong/missing requested token type, and unknown scope failures. A cache-less single-assertion verifier may use the fixtures as a cryptographic component test but MUST NOT claim STS conformance or replay safety. The combined verifyTokenExchange reference path requires a replay cache at both its type and runtime boundaries and cannot produce a successful exchange without it.

Reference implementations:

13. Deferred items

The following are explicitly outside v0.1 and require a later version:

This profile reverses the archived draft2-policy-and-mandates.md decision to reuse only the RFC 8693 act claim shape while rejecting its token endpoint. That archived MandatePart design remains non-normative; this profile adopts RFC 8693 specifically for the authorization lifecycle.

14. References

Normative:

Informative: