POST /v1/oauth/token
POST /v1/oauth/token
OAuth 2.1 token endpoint. Dispatches on grant_type:
client_credentials: confidential client exchanges itsclient_id+client_secretfor an access + refresh JWT pair. Existing API keys use this path.authorization_code: exchange a consent-approved code + PKCE verifier for an access JWT + an opaque refresh token. Public DCR clients use this.refresh_token: exchange a refresh for a new pair. Accepts both opaque (public clients; rotating with family revoke-on-reuse) and JWT (confidential clients; stateless).
Error responses follow RFC 6749 §5.2 — {"error": "...", "error_description": "..."} with appropriate HTTP status — so MCP clients
like Claude Code can parse them with standard OAuth validators. Form
rejections (malformed body, wrong content-type, …) flow through
OAuthForm so they emit the same envelope rather than axum’s default
422 + free-form text body.
Request Body required
Section titled “Request Body required ”Wire-level body for /oauth/token. grant_type is intentionally a free-
form string — RFC 6749 §1.3 describes it as a URI extension point, so
rejecting unknown values at deserialization would conflate “malformed
request” (RFC invalid_request) with “we don’t support that grant type”
(RFC unsupported_grant_type). The handler dispatches manually and emits
the spec-correct error code for each case.
object
Authorization code (from the redirect after consent).
PKCE verifier — plaintext string whose S256 hash must match the code_challenge stored at authorize time.
One of client_credentials, authorization_code, refresh_token.
Other values are rejected with RFC 6749 §5.2 unsupported_grant_type.
Redirect URI presented at authorize time. Must match exactly per RFC 6749 §4.1.3.
RFC 8707 resource indicator. For client_credentials, lets a machine
client bind its token to a specific MCP resource (e.g. <origin>/mcp)
so it satisfies that resource’s audience check at use time. The
authorization_code path takes the resource from the authorize request
via the stored code row instead, so this field is read only for
client_credentials.
Responses
Section titled “ Responses ”Token pair generated successfully
object
Invalid request
Invalid credentials