# OAuth sign-in

> The endpoints, client registration, scopes, token lifetimes, and refusals of Bonnard's OAuth sign-in for MCP clients.

Updated: 2026-11-09

AI clients sign in to Bonnard with OAuth: a person approves access in the browser, and the client gets tokens for one
connection URL. A person signs in with a workspace or customer account. No admin step comes first.

Verified 9 Nov 2026.

## Endpoints

Each path is on `https://app.bonnard.ai`.

Table: OAuth endpoints

| Endpoint | Path | Purpose |
| --- | --- | --- |
| Connection URL | `/mcp` | Serves the MCP tools |
| Protected resource metadata | `/.well-known/oauth-protected-resource/mcp` | Names the authorization server and scopes |
| Authorization server metadata | `/.well-known/oauth-authorization-server` | Gives the URL of each endpoint |
| Client registration | `/reg` | Registers a client |
| Authorization | `/auth` | Starts the sign-in |
| Token | `/token` | Issues and refreshes tokens |
| Public keys | `/jwks` | Gives the keys that sign access tokens |
| Revocation | `revocation_endpoint` in the metadata | Ends a token (RFC 7009) |
| UserInfo | `/me` | Returns the signed-in person's claims |
| Pushed authorization | `/request` | Takes an authorization request before the redirect |
| Client management | `/reg/<client id>` | Reads, updates, or deletes a registration |
| OpenID configuration | `/.well-known/openid-configuration` | Gives the URL of each endpoint |

## Connection URLs

Table: Connection URLs

| URL | Connects to |
| --- | --- |
| `https://app.bonnard.ai/mcp` | The workspace or company the person selects at sign-in |
| `https://app.bonnard.ai/mcp/o/<org id>` | One workspace, named by its id |
| `https://app.bonnard.ai/mcp/<slug>` | One older app address |

Each URL needs its own sign-in, because Bonnard binds a token to the URL it was issued for.

## Discovery

A request with no token gets HTTP 401 and this header:

```http
WWW-Authenticate: Bearer resource_metadata="https://app.bonnard.ai/.well-known/oauth-protected-resource/mcp", scope="openid"
```

The document at that URL gives `resource`, `authorization_servers`, `bearer_methods_supported`, and `scopes_supported`.
For `/mcp/o/<org id>`, the URL of that document ends in `/mcp/o/<org id>`. The client then reads
`/.well-known/oauth-authorization-server` for the endpoints.

## Client registration

A client registers in one of two ways:

- Dynamic Client Registration: `POST` to `/reg` with no access token. The limit is 10 requests a minute for each IP
  address, and more return `rate_limited` (HTTP 429).
- Client ID Metadata Document: use an HTTPS URL as `client_id`. Bonnard refuses an IP address or a local name as its host.

A redirect URI with a custom scheme or a loopback address registers as a native client. Bonnard deletes a registered client after 7 days if it has no grant.

## Authorization

- Flow: authorization code. Each request must send PKCE.
- `resource`: send one value, the connection URL. Bonnard refuses a workspace URL beside another resource with `invalid_target`.
- Scopes: `openid`, `profile`, `email`, and `offline_access`.

## Tokens

Table: Token lifetimes

| Token | Lifetime | Behaviour |
| --- | --- | --- |
| Access | 15 minutes | A JWT bound to the connection URL |
| Refresh | 30 days | Replaced at each use. A used token works for 60 seconds more, so a client can retry a lost response |
| Grant | 30 days | Refresh does not extend it, so the person signs in again after 30 days |

A client gets a refresh token if it registered the refresh grant, with or without `offline_access`.

## Refusals

These are the common refusals. [Errors](https://bonnard.ai/docs/reference/errors.md) has all codes.

Table: Refusal codes

| Code | HTTP status | When |
| --- | --- | --- |
| [`invalid_token`](https://bonnard.ai/docs/reference/errors.md) | 401 | The token or API key is not valid |
| [`access_forbidden`](https://bonnard.ai/docs/reference/errors.md) | 403 | The person has no access to the data source at an older `/mcp/<slug>` address |
| [`workspace_forbidden`](https://bonnard.ai/docs/reference/errors.md) | 403 | The person is not in this workspace, or the token is for another workspace |

## Protocol errors

The authorization and token endpoints answer with these OAuth codes.

Table: OAuth protocol errors

| Code | Endpoint | When |
| --- | --- | --- |
| `invalid_target` | `/auth`, `/token` | `resource` names an unknown URL, or a workspace URL beside another resource |
| `invalid_request` | `/token` | HTTP 400. Bonnard cannot read the body, for example a body over 56 KB |
| `invalid_grant` | `/token` | The refresh token or the grant ended. The person signs in again |
