Token Vault
ReferenceOAuth

OAuth Endpoint Reference

Every OAuth 2.1 endpoint with request/response shapes and curl examples — discovery, registration, authorize, token, refresh, revoke.

All endpoints live on the API domain. Well-behaved clients never hard-code these paths — they read them from the discovery documents. Errors follow RFC 6749 §5.2: a top-level {"error", "error_description"} body. Every endpoint is rate limited; 429 responses carry a Retry-After header.

MethodPathPurpose
GET/.well-known/oauth-protected-resourceWhich AS governs the MCP endpoint
GET/.well-known/oauth-authorization-serverAS capabilities + endpoint URLs
POST/api/mcp-oauth/registerDynamic client registration
—tokenvault.uk/oauth/authorizeBrowser consent page (authorization endpoint)
POST/api/mcp-oauth/tokenCode exchange + refresh
POST/api/mcp-oauth/revokeToken revocation
POST/api/mcp-oauth/introspectToken introspection — see Introspection

Discovery

curl https://api.tokenvault.uk/.well-known/oauth-protected-resource
{
  "resource": "https://api.tokenvault.uk/api/agents/mcp",
  "authorization_servers": ["https://api.tokenvault.uk"]
}
curl https://api.tokenvault.uk/.well-known/oauth-authorization-server
{
  "issuer": "https://api.tokenvault.uk",
  "authorization_endpoint": "https://tokenvault.uk/oauth/authorize",
  "registration_endpoint": "https://api.tokenvault.uk/api/mcp-oauth/register",
  "token_endpoint": "https://api.tokenvault.uk/api/mcp-oauth/token",
  "revocation_endpoint": "https://api.tokenvault.uk/api/mcp-oauth/revoke",
  "code_challenge_methods_supported": ["S256"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "response_types_supported": ["code"],
  "token_endpoint_auth_methods_supported": ["none"]
}

Note token_endpoint_auth_methods_supported: ["none"] — Token Vault only issues public clients. There is no client_secret, ever. PKCE S256 is mandatory.

Register (RFC 7591)

curl -X POST https://api.tokenvault.uk/api/mcp-oauth/register \
  -H 'Content-Type: application/json' \
  -d '{
    "client_name": "my-agent-runtime",
    "redirect_uris": ["http://127.0.0.1:43117/callback"]
  }'
201 Created
{
  "client_id": "kX3…16-byte-urlsafe…",
  "redirect_uris": ["http://127.0.0.1:43117/callback"],
  "token_endpoint_auth_method": "none"
}

Redirect URIs must be loopback (127.0.0.1, ::1, localhost — any port), https to a public hostname, or a custom scheme (myapp://callback, cursor://callback, com.example.app:/callback). Plain http to any other host is rejected with invalid_redirect_uri — as is a custom scheme that is itself a browser/OS launcher (microsoft-edge:…, googlechrome:…, intent://…, etc.) or that smuggles a second absolute URL right after its own scheme (microsoft-edge:https://evil.tld/cb), since a launcher relaunching that inner URL in a different handler would hand your authorization code to whatever host it points at.

Inputs are bounded: client_name up to 200 characters, at most 10 redirect_uris, each up to 2000 characters. Over-long input is rejected with 422.

A registration expires 7 days after it is created unless it completes an authorize, at which point it is extended to 180 days and re-extended on each subsequent authorize. A client that has been idle past that window simply registers again — the flow is designed for it.

Authorize (browser)

Open the authorization endpoint in a browser with standard OAuth 2.1 query params:

https://tokenvault.uk/oauth/authorize
  ?client_id=<from register>
  &redirect_uri=http://127.0.0.1:43117/callback
  &response_type=code
  &code_challenge=<BASE64URL(SHA256(verifier))>
  &code_challenge_method=S256
  &state=<random>

The user signs in, picks an agent, and consents. The browser lands on your redirect_uri with ?code=…&state=….

Authorization codes are single-use and expire after 60 seconds. Exchange immediately. A second exchange of the same code fails with invalid_grant.

Token — code exchange

Accepts application/x-www-form-urlencoded (spec) or application/json:

curl -X POST https://api.tokenvault.uk/api/mcp-oauth/token \
  -d grant_type=authorization_code \
  -d code=<code> \
  -d code_verifier=<verifier> \
  -d client_id=<client_id> \
  -d redirect_uri=http://127.0.0.1:43117/callback
200 OK
{
  "access_token": "tvsess_…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "tvrefresh_…",
  "scope": ""
}

client_id, redirect_uri, and the PKCE verifier must all match the values the code was minted against, or the exchange fails with invalid_grant.

Token — refresh

curl -X POST https://api.tokenvault.uk/api/mcp-oauth/token \
  -d grant_type=refresh_token \
  -d refresh_token=tvrefresh_…
200 OK
{
  "access_token": "tvsess_…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": ""
}

Every refresh re-checks the agent before minting: a suspended agent returns 403 invalid_grant "Agent is suspended", a deleted agent 403 "Agent not found", and an agent with MCP access switched off 403 "MCP not enabled". This is the kill-switch re-check — revoking an agent in the console stops token issuance immediately.

Revoke (RFC 7009)

curl -X POST https://api.tokenvault.uk/api/mcp-oauth/revoke \
  -H 'Content-Type: application/json' \
  -d '{"token": "tvsess_…", "token_type_hint": "access_token"}'

Always returns 200, whether or not the token existed — callers can't probe token validity through revocation. token_type_hint (access_token | refresh_token) only optimizes lookup order; both stores are tried.

Rate limited at 60 per hour per IP. A 429 is returned before any lookup happens, so it still tells the caller nothing about the token.

Using the session token

A tvsess_ works anywhere a tvagent_ key works on the MCP surface:

curl -X POST https://api.tokenvault.uk/api/agents/mcp \
  -H 'Authorization: Bearer tvsess_…' \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

The agent's grants and ABAC policies apply identically. get_credential returns a signed URL and the client fetches the credential directly from your webhook — Token Vault never touches it.

Error reference

HTTPerrorWhen
400invalid_requestMissing/malformed params, non-S256 PKCE
400invalid_grantBad/expired/reused code, PKCE failure, unknown refresh token, binding mismatch
400invalid_clientUnknown client_id
400invalid_redirect_uriUnregistered or disallowed redirect URI
400unsupported_grant_typeAnything but authorization_code / refresh_token
401invalid_tokenBad Firebase session on the consent page
403access_denied / invalid_grantAgent suspended, deleted, or MCP-disabled
429rate_limitedSlow down; honor Retry-After

Ready to try it?

Sign up free with Google — your credentials stay on your own webhook, and the quickstart gets an agent fetching its first credential in about ten minutes.

On this page