File size: 4,293 Bytes
d705bb5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
---
title: "OAuth 2.1 Server"
description: "Dynamic client registration, authorization, and token exchange endpoints that back the World Monitor MCP server's OAuth 2.1 authentication flow."
---

WorldMonitor runs a minimal OAuth 2.1 authorization server whose only client-facing purpose today is **granting access to the MCP server** at `/api/mcp`. It implements:

- [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) β€” Dynamic Client Registration
- [RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636) β€” PKCE (required, S256 only)
- [RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414) β€” Authorization Server Metadata
- [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728) β€” Protected Resource Metadata

## Discovery

| URL | Purpose |
|-----|---------|
| `/.well-known/oauth-authorization-server` | AS metadata (endpoints, supported grants, PKCE methods) |
| `/.well-known/oauth-protected-resource` | Resource metadata (authorization servers, scopes) |

`/.well-known/oauth-protected-resource` currently advertises the public resource scope `mcp`. Pro authorization-code grants return the internal scope value `mcp_pro`; legacy API-key grants and `client_credentials` return `mcp`.

## Endpoints

### `POST /api/oauth/register`

Dynamic Client Registration. Returns a `client_id` (public clients, no secret).

**Request**:
```json
{
  "redirect_uris": ["https://claude.ai/api/mcp/auth_callback"],
  "client_name": "Claude Desktop",
  "token_endpoint_auth_method": "none"
}
```

**Response**:
```json
{
  "client_id": "7c3b08f0-0c1f-4a9c-8a52-69e13d2a5d5e",
  "client_name": "Claude Desktop",
  "redirect_uris": ["https://claude.ai/api/mcp/auth_callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none"
}
```

**Redirect URI allowlist**: only these prefixes are accepted:

- `https://claude.ai/api/mcp/auth_callback`
- `https://claude.com/api/mcp/auth_callback`
- `http://localhost:<port>` / `http://127.0.0.1:<port>` β€” any port

**Rate limit**: 5 registrations / 60 s / IP.

**Client TTL**: 90 days sliding (every successful token exchange refreshes).

### `GET /api/oauth/authorize`

Starts the OAuth flow. Renders a consent page that redirects to Clerk for sign-in, then issues an authorization code bound to the caller's PRO entitlement.

**Required query params**:

- `response_type=code`
- `client_id` β€” from DCR
- `redirect_uri` β€” must match the one registered
- `code_challenge` β€” PKCE S256
- `code_challenge_method=S256`
- `state` β€” opaque
- `scope` (optional)

**Code TTL**: 10 minutes. Single-use (atomic `GETDEL` on exchange).

### `POST /api/oauth/token`

Exchanges an authorization code for an access token, or refreshes an existing token.

**Grant type: `authorization_code`**:
```
grant_type=authorization_code
code=<from /authorize>
code_verifier=<PKCE>
client_id=<from DCR>
redirect_uri=<same as /authorize>
```

**Response**:
```json
{
  "access_token": "6f13d8fa-89b6-4a02-a527-7f6f61a2df55",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "6ba38313-9a4d-4797-9186-3d2c3c1cfe02",
  "scope": "mcp_pro"
}
```

**Grant type: `refresh_token`**:
```
grant_type=refresh_token
refresh_token=<from previous exchange>
client_id=<from DCR>
```

**Rate limit**: 10 token requests / minute. The limiter is keyed by `client_secret` hash for `client_credentials`, by `client_id` when present (`authorization_code` and `refresh_token`), and falls back to caller IP only when neither identifier is available.

**Token TTLs**:

- Access token: 1 hour
- Refresh token: 7 days

Access and refresh tokens are opaque UUIDs. All token-endpoint responses include `Cache-Control: no-store, Pragma: no-cache`.

## Using tokens

Pass the access token on every MCP request:

```
Authorization: Bearer 6f13d8fa-89b6-4a02-a527-7f6f61a2df55
```

Tokens are bound to the user's account and re-check entitlement on every call β€” a downgrade revokes access on the next request.

## Error responses

Per [RFC 6749 Β§5.2](https://datatracker.ietf.org/doc/html/rfc6749#section-5.2):

```json
{ "error": "invalid_grant", "error_description": "..." }
```

Common errors: `invalid_request`, `invalid_client`, `invalid_grant`, `unsupported_grant_type`, `invalid_scope`.