Skip to content
Inbot
Esc
↑↓navigate↵open⌘Jpreview
On this page

OAuth for MCP clients

How MCP clients connect with owner-approved OAuth grants instead of API keys, with every endpoint, lifetime and rule.

MCP clients that support OAuth can connect to Inbot without an API key. Inbot is its own authorization server: the client registers itself, sends the owner to a consent screen in the Inbot web app, and receives short-lived, audience-bound tokens. The owner chooses the grant, using the same inbox scope, permissions and send policy as for API keys.

The flow

  1. The client calls /mcp without a credential and receives 401 with WWW-Authenticate: Bearer resource_metadata="<origin>/.well-known/oauth-protected-resource/mcp", scope="inbot".
  2. It reads the protected resource and authorization server metadata, and registers itself at /oauth/register.
  3. It opens /oauth/authorize in the owner’s browser. If the owner is signed out, Inbot redirects to /login, then returns them to the consent request after sign-in.
  4. The owner sees Allow access to Inbot? with the client’s name and redirect host, both as declared by the client and unverified, chooses a grant, and approves or denies.
  5. The client exchanges the code at /oauth/token with its PKCE verifier and receives an access token and a refresh token.

New consent grants default to Full access with Approval, with no inboxes selected. Select the intended inbox scope before approving; the preset includes reading, drafting and sending, while every send still needs owner approval.

Consent requires the web app, so the service must run with the bundled app or --web-root. Without it, /oauth/authorize returns not_supported.

Endpoints

Path Purpose
GET /.well-known/oauth-protected-resource, .../mcp, .../v0 Resource metadata: resource, authorization_servers, scopes_supported: ["inbot"]
GET /.well-known/oauth-authorization-server Authorization server metadata
POST /oauth/register Dynamic client registration. Public clients only (token_endpoint_auth_method: none), 1 to 10 redirect_uris. Returns 201.
GET /oauth/authorize Opens the consent screen in the browser
POST /oauth/token authorization_code or refresh_token grants. JSON or form-encoded, at most 16 KiB.
POST /oauth/revoke Revokes a token and its whole family. Always answers 200.

Discovery, registration, token and revocation responses allow any origin (Access-Control-Allow-Origin: *, without credentials). Errors from these endpoints use the OAuth format, { error, error_description }.

Rules

Rule Value
Grant types authorization_code and refresh_token
PKCE Required, S256 only
Scope inbot, optional. It grants nothing by itself; permissions come from the grant the owner chooses.
Resource Required at authorization and token exchange: exactly <origin>/mcp or <origin>/v0
Redirect URIs HTTPS, HTTP on loopback only, or a private-use custom scheme. Loopback ports may change between registration and use.
Authorization code Five minutes, single use
Access token Five minutes (inbot_oauth_...)
Refresh tokens Rotate on every use. The family ends 30 days after consent; rotation does not extend it.

Reusing a refresh token that was already used revokes the whole family, including its access tokens. Reusing an authorization code revokes the tokens it issued.

Tokens are bound to the origin and resource they were issued for. A token for /mcp does not work on /v0, a token issued on the loopback URL does not work through the tunnel, and changing the remote access hostname invalidates tokens issued for the old one. WebSocket events and attachment metadata routes belong to /v0. MCP get_attachment can issue a signed download capability for an /mcp token; the URL needs no bearer, retains that token’s audience, and rechecks its activity and grant. Never share the URL.

Limits

Registration is limited to 20 per hour, and token and revocation requests to 120 per minute each, with separate counters for loopback and tunnel traffic. Over the limit, Inbot returns 429 with Retry-After: 60. At most 1,000 client registrations are kept; registrations that never receive a grant expire after 24 hours.

Through remote access

OAuth works through the tunnel. Discovery, registration, token and revocation are reachable, and the consent screen works once the owner signs in to the web app over the tunnel.

Manage grants

The owner sees every grant under API keys > Authorized OAuth clients, or with inbot oauth clients.

  • inbot oauth revoke <grant_id> revokes a grant and every token it issued.
  • inbot oauth purge deletes client registrations that have no grant. Existing grants are untouched.

OAuth management is owner-only and blocked over the tunnel. The oauth.authorized, oauth.revoked and oauth.tokens_revoked events are visible only to the owner.