# OAuth for MCP clients

> Connect Claude.ai, ChatGPT or another MCP client with OAuth 2.1 and PKCE, with the endpoints, client registration, consent screen and revoking access.

AI assistants such as the Claude.ai and ChatGPT connectors cannot take a pasted key. They connect to a project's MCP server with OAuth 2.1: the person signs in to Spatibase, sees a consent screen and chooses what the assistant may do. This page is for developers building or debugging an MCP client. People who just want to connect an assistant should read [Connect an AI assistant](https://spatibase.com/docs/ai/connect-an-assistant/).

On this page, `$PROJECT_ID` is the project's UUID, and the MCP URL is `https://mcp.spatibase.com/projects/$PROJECT_ID` (copy the exact address from the project's **AI agents** page, in **Connect your assistant**).

## Which standards does Spatibase follow?

Spatibase is its own authorization server and follows the MCP authorization specification, revision 2026-07-28. That includes OAuth 2.1 with PKCE, Protected Resource Metadata (RFC 9728), Authorization Server Metadata (RFC 8414), Resource Indicators (RFC 8707), the `iss` authorization response parameter (RFC 9207), Client ID Metadata Documents, Dynamic Client Registration (RFC 7591) for older clients, and Token Revocation (RFC 7009).

Clients that can send a header (Claude Code, Cursor, your own agent) can use an API key instead. See [MCP server](https://spatibase.com/docs/ai/mcp-server/).

## How does a client connect?

A client goes through these steps:

1. **Call the MCP URL without a token.** On a private project the answer is `401` with `WWW-Authenticate: Bearer resource_metadata="…"`. On a public project an anonymous call succeeds with read access, so a client that wants more must start the flow itself.
2. **Read the protected resource metadata** at `GET https://mcp.spatibase.com/.well-known/oauth-protected-resource/projects/$PROJECT_ID`. It returns `resource` (the project's canonical MCP URL), `authorization_servers`, `scopes_supported` and `bearer_methods_supported: ["header"]`.
3. **Read the authorization server metadata** at `GET https://api.spatibase.com/.well-known/oauth-authorization-server`. It lists the endpoints, `code_challenge_methods_supported: ["S256"]`, `client_id_metadata_document_supported: true` and `authorization_response_iss_parameter_supported: true`.
4. **Get a client id**, in one of two ways (see the next section).
5. **Send the person's browser to the authorization endpoint:**

   ```http
   GET /oauth/authorize?response_type=code&client_id=…&redirect_uri=…&code_challenge=…&code_challenge_method=S256&state=…&scope=read%20edit&resource=<MCP URL>
   ```

   Spatibase asks the person to sign in if needed, then shows the consent screen. The request stays valid for 30 minutes, long enough to sign in by email link.
6. **The person selects Allow or Cancel.** The browser returns to `redirect_uri` with `code`, `state` and `iss`, or with `error=access_denied`.
7. **Exchange the code** with a form-encoded `POST /oauth/token`:

   ```http
   POST /oauth/token
   Content-Type: application/x-www-form-urlencoded

   grant_type=authorization_code&code=…&code_verifier=…&redirect_uri=…&client_id=…&resource=…
   ```

   The answer is `{access_token: "sb_oat_…", token_type: "Bearer", expires_in: 3600, refresh_token: "sb_ort_…", scope}`.
8. **Call the MCP URL** with `Authorization: Bearer sb_oat_…`. Before the hour is up, refresh with `grant_type=refresh_token&refresh_token=…&client_id=…`.

## How does a client get a client id?

Spatibase accepts two kinds of client id:

- **Client ID Metadata Document (preferred).** The client id is an `https://` URL with a path, on the default port, that serves JSON with `client_id` (exactly that URL), `client_name`, `redirect_uris` and `token_endpoint_auth_method: "none"`. Spatibase fetches it when a person starts connecting: public addresses only, no redirects, one 5-second deadline, at most 10 KB. It is cached according to its `Cache-Control`, between 5 minutes and 24 hours.
- **Dynamic client registration.** `POST /oauth/register` with `{client_name, redirect_uris, token_endpoint_auth_method?}` answers `201` with `{client_id: "sb_client_…", …}`. The default `none` creates a public client. `client_secret_post` or `client_secret_basic` also returns a `client_secret` (`sb_ocs_…`), once.

## What does the person see on the consent screen?

The consent screen, **Connect an app to Spatibase**, shows the app's name, the project and **What may it do?**. For a client with a metadata document, it shows the domain the app is published from; otherwise it says Spatibase cannot confirm who made the app, and a client registered through `/oauth/register` carries an **unverified** badge. If the redirect goes to the person's own computer, it warns them to allow it only if they started connecting themselves.

| Scope | Label on the consent screen | Pre-selected |
|---|---|---|
| `read` | **Read the project** | Always included |
| `edit` | **Edit drafts** | Yes |
| `publish` | **Publish** | No |
| `delete` | **Delete** | No |
| `model` | **Change the content model** | No |

**The person's role caps the scope.** An owner may allow all five, an editor `read edit publish delete`, a developer `read` only. The screen lists only the scopes the app asked for, and greys out those the role does not allow ("not available: your role is …"). Approving more than the role allows answers `422`. Each token is also cut to the person's current role on every request, so changing someone's role narrows their assistants at once. What each scope unlocks is in the [tool list](https://spatibase.com/docs/ai/mcp-server/#tools).

## Rules worth knowing

| Topic | Rule |
|---|---|
| Redirect URIs | Matched exactly against the registered ones. `https`, or `http` only on `localhost`, `127.0.0.1` and `[::1]`. No fragments. |
| Errors before a redirect | An unknown client, an unregistered redirect URI, a wrong `response_type` or missing PKCE show an error page and never redirect. `invalid_target` and `invalid_scope` go back to the app only once someone has connected it; before that they are an error page too. |
| PKCE | Required, `S256` only. |
| `resource` | Required on `/oauth/authorize` and must be a project's MCP URL. Optional on token and refresh requests, but must match when sent. |
| Scopes | `read edit publish delete model`. No `scope` asks for all of them. Unknown values (for example `offline_access`) are ignored. `read` is implied by any scope and always granted; the person may leave out the others. A refresh may narrow the scope, never widen it. |
| Authorization codes | `sb_oac_…`, valid 10 minutes, single use. Using a code twice fails and revokes the tokens the first use produced. |
| Access tokens | `sb_oat_…`, valid 1 hour, for one project's MCP URL only, and only while the person is still a member of the project. |
| Refresh tokens | `sb_ort_…`, valid 30 days, rotated on every use. Presenting a used refresh token again closes the whole connection. |
| Revocation | `POST /oauth/revoke` with `token=…&client_id=…` always answers `200`. Revoking a refresh token also revokes its access tokens. |
| Error format | `/oauth/*` endpoints answer `{error, error_description}`. Rate limits per IP answer `429 {error: "rate_limited"}` with `Retry-After`; on `/oauth/authorize` a 429 page. |
| Storage | Spatibase stores only SHA-256 hashes of codes, tokens and client secrets. |

## Connected apps

Every approval creates a connection for one person, one app and one project. Approving the same app for the same project again updates that connection.

People see their connections in the account menu under **Connected apps**, with the project, the permissions, when it was connected and when it was last used. **Disconnect** removes access straight away. Connecting, changing and disconnecting are recorded in the project's **Activity**, and changes the assistant makes appear there as "person via app".

## Related

- [MCP server reference](https://spatibase.com/docs/ai/mcp-server/)
- [Connect an AI assistant](https://spatibase.com/docs/ai/connect-an-assistant/)
- [What assistants can do](https://spatibase.com/docs/ai/what-assistants-can-do/)
- [Authentication](https://spatibase.com/docs/reference/authentication/)
