Agent door
Authentication
The door authenticates an agent with an identity assertion from a trusted issuer, mints an org-scoped session whose lifetime the grant sets (one hour by default, no refresh), and — optionally — binds that session to a proof-of-possession key.
The door does not invent its own auth. It uses standards-based OAuth discovery, the
ID-JAG identity-assertion authorization grant, and
optional DPoP proof-of-possession. The base host is
https://api.yellowhead.digital.
Discovery
Two anonymously-fetchable metadata documents describe the door:
| Document | Endpoint |
|---|---|
| Protected-resource metadata (RFC 9728) | GET /.well-known/oauth-protected-resource |
| Authorization-server metadata (RFC 8414) | GET /.well-known/oauth-authorization-server |
The authorization-server document advertises the grant type, the identity-assertion profile, and the DPoP signing algorithms the token endpoint accepts:
- Grant type:
urn:ietf:params:oauth:grant-type:jwt-bearer - Identity-assertion profile:
urn:ietf:params:oauth:grant-profile:id-jag - Assertion type:
urn:ietf:params:oauth:token-type:id-jag - Token endpoint:
POST /mcp/oauth/token - Token-endpoint client auth:
none(the assertion, and optionally a DPoP proof, are the credential) - DPoP signing algorithms:
RS256,PS256,ES256,ES384,EdDSA
ID-JAG: the identity assertion
An agent authenticates by presenting an ID-JAG — a signed JWT issued by an identity provider your workspace has registered as a trusted issuer. The token endpoint verifies the assertion end to end: its type, signing algorithm, signature (against the issuer's published keys), audience, expiry, and required claims, and that the asserted subject and client are permitted by a live grant in your workspace.
A single assertion mints a single session — its unique identifier is consumed on use, so an
assertion cannot be replayed. A new session needs a fresh assertion. Where more than one
grant could match an assertion, the client selects one by passing a grant_id.
Request
POST /mcp/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&assertion=<the ID-JAG>
&grant_id=<optional grant selector> Response
{
"access_token": "yh_mcp_...",
"token_type": "Bearer", // "DPoP" when the session is key-bound
"expires_in": 3600,
"scope": "diagnostic.read report.read ..." // the grant's capabilities
}
The access token is an opaque session token (prefix yh_mcp_),
not a JWT. Every request re-checks the session row and the grant behind it, so revoking a
grant or a session takes effect within about a minute — see below.
Calling the door
Present the session token as a bearer credential on every MCP transport request:
POST /mcp
Authorization: Bearer yh_mcp_...
Content-Type: application/json
MCP-Protocol-Version: 2025-06-18
{ "jsonrpc": "2.0", "method": "tools/list", "id": 1 } POST /mcp carries the JSON-RPC calls (initialize,
tools/list, tools/call). DELETE /mcp ends a protocol
session. Tool results return as JSON in the POST response body; the door does not open a
server-sent event stream.
DPoP: sender-constraining (optional, sometimes required)
A bearer session token is, on its own, a bearer credential: whoever holds it can use it.
DPoP (RFC 9449) closes that gap by binding the session to a key the client
proves it holds. When a client presents a valid DPoP proof at the token endpoint, the
session is bound to that key's thumbprint (a binding that can never be changed afterward)
and the response returns token_type: DPoP. A key-bound session must then
present a fresh proof — signed by the same key, over this exact request and this exact
token — on every transport request.
DPoP is optional by default and required per grant: a workspace can mark a grant so that only an issuer-confirmed, key-bound session is admitted — which is the posture for every external-customer grant. DPoP is also the prerequisite for the direct-apply write path (see Write approvals): without a proven key binding, writes are always held for a human.
Sessions, revocation, and expiry
- Lifetime. A session lives until
expires_inelapses (the grant's configured TTL, default one hour) or it is terminated early. - No refresh. There is no refresh token. A new session needs a fresh identity assertion.
- Revocation lands within ~60 seconds. Revoking the grant, revoking the trusted issuer (which kills every session under it), or narrowing the grant's allowed subjects terminates a live session on its next revalidation. Deleting the session row is immediate.
- Redeploys. A backend redeploy clears in-memory protocol sessions; the
bearer session survives in the database, so the client re-runs
initializewith the same token and continues.