---
url: https://flat.io/developers/docs/mcp/authorization.md
description: >-
  How MCP clients authorize against Flat - OAuth 2.1 with PKCE, Client ID
  Metadata Documents, Dynamic Client Registration, scopes, resource indicators
  and token lifetimes.
---

# Authorization

The Flat MCP server follows the
[MCP authorization specification](https://modelcontextprotocol.io/specification/latest/basic/authorization):
OAuth 2.1, authorization code grant with PKCE, discovered from metadata. A client that implements
the spec needs no Flat-specific configuration.

## Discovery

An unauthenticated request to `https://mcp.flat.io/mcp` answers `401` with a `WWW-Authenticate`
challenge pointing to the protected resource metadata ([RFC 9728](https://www.rfc-editor.org/rfc/rfc9728)):

```
GET https://mcp.flat.io/.well-known/oauth-protected-resource
```

```json
{
  "resource": "https://mcp.flat.io/mcp",
  "authorization_servers": ["https://api.flat.io"],
  "bearer_methods_supported": ["header"],
  "scopes_supported": ["mcp"],
  "resource_name": "Flat",
  "resource_documentation": "https://flat.io/developers/docs/mcp"
}
```

The authorization server metadata ([RFC 8414](https://www.rfc-editor.org/rfc/rfc8414)) is at
`https://api.flat.io/.well-known/oauth-authorization-server` (also served as
`/.well-known/openid-configuration`):

| Field | Value |
|---|---|
| `issuer` | `https://api.flat.io` |
| `authorization_endpoint` | `https://flat.io/auth/oauth/mcp` |
| `token_endpoint` | `https://api.flat.io/oauth/access_token` |
| `revocation_endpoint` | `https://api.flat.io/oauth/invalidate_token` |
| `registration_endpoint` | `https://api.flat.io/oauth/mcp/register` |
| `grant_types_supported` | `authorization_code`, `refresh_token` |
| `code_challenge_methods_supported` | `S256` |
| `client_id_metadata_document_supported` | `true` |
| `scopes_supported` | `mcp` |

## Client registration

### Client ID Metadata Documents (preferred)

Use an `https://` URL as your `client_id`. Flat fetches it at authorization time and reads your
client metadata from it ([CIMD](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/)).

* The URL must be `https`, have a path (not just a host) and no fragment.
* The document's `client_id` must equal the URL exactly.
* `client_name` and a non-empty `redirect_uris` are required.
* Redirect URIs must be `https`, or `http` on `localhost` / `127.0.0.1`, without a fragment.
* Flat follows at most 3 redirects, reads at most 64 KB and waits at most 5 seconds. It caches the
  document according to `Cache-Control: max-age`, between 5 minutes and 24 hours.

There is nothing to register with Flat beforehand.

### Dynamic Client Registration

For clients that do not support CIMD, Flat accepts [RFC 7591](https://www.rfc-editor.org/rfc/rfc7591)
registration:

```http
POST https://api.flat.io/oauth/mcp/register
Content-Type: application/json

{
  "client_name": "My MCP client",
  "redirect_uris": ["http://127.0.0.1:33418/callback"]
}
```

The response (`201`) contains a `client_id`, with `token_endpoint_auth_method: "none"` and
`scope: "mcp"`. No client secret is issued: every MCP client is a public client. Registration is
limited to 10 clients per IP address per hour (`429`, `Retry-After: 3600`).

Clients registered this way cannot prove who they are, so the consent screen marks them as
unverified (see below). Prefer CIMD.

## Authorization request

```
https://flat.io/auth/oauth/mcp
  ?response_type=code
  &client_id=https://example.com/oauth/client.json
  &redirect_uri=https://example.com/oauth/callback
  &code_challenge=...
  &code_challenge_method=S256
  &scope=mcp
  &resource=https://mcp.flat.io/mcp
  &state=...
```

* `response_type` must be `code`.
* **PKCE is required**, with `S256` only. The verifier is 43 to 128 characters.
* `redirect_uri` must match a registered URI exactly. For `http` loopback URIs (`localhost`,
  `127.0.0.1`), any port matches ([RFC 8252](https://www.rfc-editor.org/rfc/rfc8252#section-7.3));
  scheme, host, path and query must still match. A mismatch is shown on Flat's page and never
  redirected.
* `scope` defaults to `mcp`; a request without `mcp` fails with `invalid_scope`.
* `resource` ([RFC 8707](https://www.rfc-editor.org/rfc/rfc8707)) defaults to
  `https://mcp.flat.io/mcp`. Unknown values fail with `invalid_target`.

The redirect back carries `code`, `state` and `iss` ([RFC 9207](https://www.rfc-editor.org/rfc/rfc9207)).
The code is valid for 10 minutes and can be redeemed once.

The `mcp` scope is only granted through this flow. It cannot be added to a regular Flat developer
app or a personal access token.

## Tokens

| Token | Lifetime |
|---|---|
| Access token | 24 hours |
| Refresh token | 30 days, renewed on every refresh |

Every refresh returns a new refresh token with a new 30-day lifetime and invalidates the previous
one. A client that does not refresh for 30 days must send the customer through consent again.

Send the access token as a bearer token on every MCP request:

```http
Authorization: Bearer <access_token>
```

The MCP server only accepts tokens issued for its resource (`https://mcp.flat.io/mcp`).

## Consent screen

The customer approves every client on Flat's consent screen, which lists the single permission
requested: *Read and change the content of your Flat account*. How the client is presented depends
on what Flat could verify:

| Client | Shown as |
|---|---|
| Recognized (ChatGPT, Claude, Visual Studio Code) | Product name and logo, "recognized by Flat" |
| CIMD client | The domain of its `client_id` as a proven origin, with the name it declares |
| DCR client | "This app hasn't proven who it is", and the button reads **Allow anyway** |

When the redirect URI is a loopback address, the screen also says the access code goes to a
program running on the customer's computer.

## Errors

| Situation | Result |
|---|---|
| Missing, expired or revoked token | `401`, `invalid_token` |
| Token issued for another resource | `401`, `invalid_token` |
| Account without Flat Power | `403`, `MCP_UPGRADE_REQUIRED` |
| Flat for Education account | `403`, `MCP_EDUCATION_NOT_AVAILABLE` |
| Disabled account | `403`, `MCP_ACCOUNT_DISABLED` |
| Client disabled by Flat | `MCP_CLIENT_DISABLED` |

Eligibility is checked at consent and on every request. The error messages are localized to the
customer's language and meant to be shown to them as is.

## Revocation

* **By the customer:** removing the client from **Link external accounts and apps** in their Flat
  account settings deletes all its tokens for that customer.
* **By the client:** `POST https://api.flat.io/oauth/invalidate_token`
  ([RFC 7009](https://www.rfc-editor.org/rfc/rfc7009)) revokes the token you send.
