> ## Documentation Index
> Fetch the complete documentation index at: https://developers.apob.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP authentication

> Discover APOB's OAuth endpoints, authorize public clients with PKCE, and manage MCP connections.

MCP requests use an OAuth access token issued for the configured MCP resource.
Use `Authorization: Bearer <MCP_ACCESS_TOKEN>` on every `/mcp` request. The
GraphQL API key and Firebase ID token flows have separate credential boundaries.

## Discover endpoints

An unauthenticated MCP request returns a Bearer `WWW-Authenticate` challenge
with `resource_metadata` and the required `mcp:access` scope. Fetch that metadata
URL to obtain the exact resource URI and authorization-server issuer.

| Endpoint | Purpose |
| - | - |
| `GET /.well-known/oauth-protected-resource/mcp` | Protected resource metadata; the root `/.well-known/oauth-protected-resource` alias returns the same document. |
| `GET /.well-known/oauth-authorization-server` | Authorization-server metadata, including issuer, endpoints, scopes, grants, and PKCE methods. |
| `GET /oauth/authorize` | Start browser authorization with the parameters below. |
| `POST /oauth/token` | Exchange an authorization code for an access token using form-encoded parameters. |
| `GET /oauth/jwks` | Public signing keys. |

Use the endpoint URLs returned by discovery rather than constructing them from
the GraphQL URL. These paths are available only in an MCP-enabled environment.
Discovery follows the
[MCP authorization specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization).

## Supported client and token policy

| Setting | APOB contract |
| - | - |
| Client type | Public client; token endpoint authentication method `none`. |
| Grant | `authorization_code` only. |
| PKCE | Required; `S256` only. |
| Scope | `mcp:access`. |
| Resource | One exact configured MCP resource URI at authorization and code exchange. |
| Access token | Bearer JWT, signed with RS256; seven-day lifetime. |
| Refresh | No refresh token or refresh-token grant. Authorize again after expiration. |
| Client identity | A configured client ID, or a validated HTTPS Client ID Metadata Document (CIMD) URL. |

CIMD support is advertised in authorization-server metadata. A CIMD document
must identify the same client URL and define permitted redirect URIs. Deployment
policy can restrict which CIMD clients are accepted. There is no dynamic client
registration endpoint; do not assume an arbitrary opaque client ID is registered.

## Authorization code flow

<Steps>
  <Step title="Create the authorization request">
    Generate a verifier and its S256 challenge, plus a state value bound to your
    client interaction. Open the discovered authorization endpoint in a browser
    with the parameters below.
  </Step>

  <Step title="Sign in and consent">
    Follow the APOB sign-in and consent pages. Verify the account and client.
    Keep the browser's authorization session through the redirect flow.
  </Step>

  <Step title="Validate the callback">
    Check the returned state against the original interaction. On approval,
    exchange the returned code once using the original redirect URI and verifier.
  </Step>

  <Step title="Use the access token">
    Store the returned token securely and send it in the Bearer header. Follow
    the [HTTP and JSON-RPC reference](/mcp/protocol) to initialize and discover tools.
  </Step>
</Steps>

Authorization parameters:

| Parameter | Value |
| - | - |
| `response_type` | `code`. |
| `client_id` | Configured public client ID or accepted CIMD URL. |
| `redirect_uri` | Registered callback URI. |
| `scope` | `mcp:access`. |
| `resource` | Exact resource from protected-resource metadata. |
| `code_challenge` | Base64url SHA-256 of the verifier, without padding. |
| `code_challenge_method` | `S256`. |
| `state` | Client-generated state to check on callback. |

Redirect matching is constrained to registered callbacks. For an explicitly
registered portless HTTP `localhost` callback, a canonical port from 1 to 65535
is accepted while the scheme, host, path, and raw query remain exact. Fixed-port
`localhost` and HTTPS callbacks match exactly. Token exchange must use the
original authorization request's redirect URI, including its port.

Exchange example, with values obtained from discovery and the callback:

```bash theme={null}
curl "$APOB_TOKEN_URL" \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=authorization_code' \
  --data-urlencode "client_id=$MCP_CLIENT_ID" \
  --data-urlencode "code=$MCP_AUTHORIZATION_CODE" \
  --data-urlencode "redirect_uri=$MCP_REDIRECT_URI" \
  --data-urlencode "code_verifier=$MCP_CODE_VERIFIER" \
  --data-urlencode "resource=$APOB_MCP_URL"
```

Check the token endpoint's status and body; a code is single-use. Invalid codes,
verifiers, redirect URIs, or resource bindings do not produce access tokens.
Bearer access checks the resource/audience, issuer, signature, expiration,
scope, and stored token state. Tool discovery and calls also recheck the active
account. Revoked or inactive access fails even if the JWT has not yet expired.

## Browser sign-in and consent helpers

These endpoints support the APOB authorization frontend. An ordinary MCP client
follows the browser redirects; it does not log in by sending a Firebase token
to `/mcp`.

| Endpoint | Request and result |
| - | - |
| `POST /auth/session` | JSON containing exactly `id_token`, a Firebase ID token. Requires the existing pending anonymous authorization session and permitted login Origin. Success rotates the session and returns `204`. |
| `GET /auth/session` | Requires the authenticated browser session. Returns `authenticated: true` and `csrf` containing `headerName` and `token`; unauthenticated access returns `401`. |
| `DELETE /auth/session` | Requires the browser session, allowed Origin, and CSRF token. Invalidates that session and returns `204`. |
| `GET /oauth/consent/context` | Requires the browser session and single `client_id`, `state`, and `scope=mcp:access` values matching its pending consent request. Returns `client`, `permissions`, and `form`. |
| `POST /oauth/authorize` | Submit the returned consent form fields and CSRF token using the same browser session. |

After successful login, resume with a query-free `GET /oauth/authorize` using
the same session. The consent form supplies `action`, `method`, `clientId`,
`state`, `approveScope`, `csrfParameterName`, and `csrfToken`. Use those values
instead of reconstructing the pending authorization from URL parameters.

Browser sessions have a 30-minute inactivity timeout; pending authorization
expires after 10 minutes. These session/context responses use `Cache-Control:
no-store`. Session cookies authenticate the browser flow and do not authenticate
MCP tool requests.

## Inspect or revoke a connection

The [GraphQL connected-app API](/api-reference/mcp-connections) provides:

* `listMcpClientInstructions` for setup metadata.
* `listPersonalMcpConnections` for the authenticated account's connected clients.
* `revokeMcpConnection` to remove that account's consent and invalidate the
  client's stored access tokens and authorization codes.

Use GraphQL authentication for these operations. Logging out of the browser
session does not revoke an MCP access token. After revocation or expiration,
the client must authorize again. MCP does not expose OAuth token introspection
or token revocation endpoints.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.