MCP OAuth token endpoint proxy (authorization_code + refresh_token)
const url = 'https://example.com/api/oauth/token';const options = { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: new URLSearchParams({ grant_type: 'refresh_token', refresh_token: 'example', client_id: 'example', resource: 'https://example.com' })};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/api/oauth/token \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data grant_type=refresh_token \ --data refresh_token=example \ --data client_id=example \ --data resource=https://example.comForwards authorization_code and refresh_token grants to Supabase
/auth/v1/oauth/token. Discovered as token_endpoint from
/.well-known/oauth-authorization-server for MCP clients.
Accepts both application/x-www-form-urlencoded (OAuth 2.1 standard) and
application/json request bodies.
RFC 8707 (Resource Indicators): clients MAY include an optional
resource parameter. The canonical value is the production MCP endpoint
(https://api.apphandoff.com/mcp). During the compatibility transition,
the API base URL and legacy /api/mcp-bot endpoint remain accepted as
exact aliases; all other values return invalid_target (HTTP 400). The
value is forwarded upstream so RFC 8707-aware issuers can populate the
JWT aud claim.
Request Body required
Section titled “Request Body required ”object
Supabase OAuth client UUID or an HTTPS Client ID Metadata Document URL.
RFC 8707 resource indicator. Optional; the canonical value is the
/mcp endpoint. The API base URL and legacy /api/mcp-bot endpoint
remain accepted as exact transition aliases. Every other value
returns invalid_target.
object
Supabase OAuth client UUID or an HTTPS Client ID Metadata Document URL.
Must be https://, http://localhost/127.0.0.1/[::1], or a
registered custom app scheme. javascript:, data:, file:,
vbscript: are denylisted.
PKCE verifier; the AS metadata advertises S256-only.
RFC 8707 resource indicator (see refresh_token grant).
object
Supabase OAuth client UUID or an HTTPS Client ID Metadata Document URL.
RFC 8707 resource indicator. Optional; the canonical value is the
/mcp endpoint. The API base URL and legacy /api/mcp-bot endpoint
remain accepted as exact transition aliases. Every other value
returns invalid_target.
object
Supabase OAuth client UUID or an HTTPS Client ID Metadata Document URL.
Must be https://, http://localhost/127.0.0.1/[::1], or a
registered custom app scheme. javascript:, data:, file:,
vbscript: are denylisted.
PKCE verifier; the AS metadata advertises S256-only.
RFC 8707 resource indicator (see refresh_token grant).
Responses
Section titled “ Responses ”Token payload from Supabase
object
Invalid request. Local validation failures use the Error envelope
(INVALID_BODY — schema validation failure; INVALID_RESOURCE —
resource does not match the canonical API URL). Failures from the
grant exchange itself use OAuthTokenError; in particular a dead or
unknown refresh token returns invalid_grant, which means
re-authorize, not retry.
RFC 6749 §5.2 token-endpoint error. error is ALWAYS a registered code,
never prose: clients switch on it, and invalid_grant in particular is
the signal to discard the grant and re-run the authorization flow rather
than retry. Upstream Supabase GoTrue does not emit this shape, so the
translation happens here (lib/oauth-token-error.ts); the raw upstream
code is preserved in error_code and the prose in error_description.
object
Human-readable detail. Never load-bearing for client control flow.
Upstream GoTrue error code, verbatim (e.g. refresh_token_not_found).
Legacy AppHandoff envelope key, retained for existing consumers.
object
Human-readable error message
Stable machine-readable error code for client branching
First validation message per field path
object
Structured validation issues (Zod)
object
Seconds until rate limit resets (429 responses)
Rate limited
object
Human-readable error message
Stable machine-readable error code for client branching
First validation message per field path
object
Structured validation issues (Zod)
object
Seconds until rate limit resets (429 responses)
Seconds until the rate limit resets
Headers
Section titled “Headers ”Server error
object
Human-readable error message
Stable machine-readable error code for client branching
First validation message per field path
object
Structured validation issues (Zod)
object
Seconds until rate limit resets (429 responses)
Upstream Supabase token exchange error, including a network failure
reaching it. Reported as server_error — retryable, unlike a token
fault.
RFC 6749 §5.2 token-endpoint error. error is ALWAYS a registered code,
never prose: clients switch on it, and invalid_grant in particular is
the signal to discard the grant and re-run the authorization flow rather
than retry. Upstream Supabase GoTrue does not emit this shape, so the
translation happens here (lib/oauth-token-error.ts); the raw upstream
code is preserved in error_code and the prose in error_description.
object
Human-readable detail. Never load-bearing for client control flow.
Upstream GoTrue error code, verbatim (e.g. refresh_token_not_found).
Legacy AppHandoff envelope key, retained for existing consumers.
CIMD mapping lookup failed (temporarily_unavailable).