Agent door

Authentication

The door admits an agent two ways: a person signs in from an AI assistant and approves a consent screen, or the agent presents an identity assertion from a trusted issuer. Either way it gets an org-scoped, capability-bound session.

The door does not invent its own auth. It uses standards-based OAuth discovery and two grants: the OAuth authorization-code flow, for a person connecting an AI assistant by signing in, and the ID-JAG identity-assertion grant, for provisioned access. Provisioned sessions can add DPoP proof-of-possession. The base host is https://api.yellowhead.digital.

Discovery

Anonymously-fetchable metadata documents describe the door:

DocumentEndpoint
Protected-resource metadata (RFC 9728) GET /.well-known/oauth-protected-resource
Protected-resource metadata for the /mcp resource (RFC 9728, path-suffixed form) GET /.well-known/oauth-protected-resource/mcp
Authorization-server metadata (RFC 8414) GET /.well-known/oauth-authorization-server

The two protected-resource documents are the same document. The authorization-server document advertises both grants. For provisioned access, it lists the identity-assertion grant type and 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

Sign-in: the authorization-code flow

A person connects an AI assistant by adding https://api.yellowhead.digital/mcp as a custom connector in an MCP client that supports OAuth sign-in, such as Claude or ChatGPT. The client finds the endpoints from the discovery documents above, sends the person to app.yellowhead.digital to sign in, and shows a consent screen that lists what the connection will be able to do. When the person approves, the client exchanges the returned code at the token endpoint for an access token and a refresh token. It works on every plan, Free included. The sign-in can wait until it is needed: a request with no credential may run initialize, tools/list and the one public tool, describe_yellowhead, and a call to any other tool gets HTTP 401 with a WWW-Authenticate challenge (error="invalid_token" and the resource_metadata URL), which is the client's cue to sign the person in. For step-by-step setup in a specific assistant, see Connect Claude and Connect ChatGPT.

For this flow, the authorization-server document also advertises:

  • Authorization endpoint: https://app.yellowhead.digital/oauth/authorize
  • Grant types: authorization_code and refresh_token
  • Response type: code, returned in the query, with an iss parameter on every authorization response (RFC 9207)
  • PKCE: S256 only
  • Client registration: a client ID metadata document (the client's ID is a URL), or dynamic registration at POST /mcp/oauth/register
  • Client auth: none (every client is public)
  • Scopes: read and offline_access
  • Revocation endpoint: POST /mcp/oauth/revoke

Scopes never widen what a connection can do. Its capabilities come from the account's plan: the read capabilities that plan can use, and diagnostic.start, which starts a Diagnostic: the first call returns a preview, and only a second call with the preview's single-use confirmation token starts the run, using only the account's included monthly Diagnostics. Depending on the plan, it can also file the run in a workspace or, for an owner or admin of an Agency team account, create a new prospect or client workspace (see Capabilities). A sign-in connection holds no action-item write capability. It can do only what the person can do in the account, owners and admins of a team account can turn members' connections off, and the person sees and removes their connections in Settings › AI assistants.

The access token is the same opaque session token the provisioned path issues (prefix yh_mcp_). Each refresh token is used once: every refresh returns a new one, and presenting an old one again ends the connection, so the person connects again.

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 provisioned session lives until expires_in elapses (the grant's configured TTL, default one hour) or it is terminated early.
  • No refresh on provisioned access. The ID-JAG grant issues no refresh token; a new session needs a fresh identity assertion. A sign-in connection refreshes with its refresh token, as described above.
  • 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 initialize with the same token and continues.