Skip to content

Commit 233c1d9

Browse files
travisbreaksclaude
andcommitted
feat(examples): add OAuth without DCR example
Adds an example MCP server demonstrating how to authenticate users via an upstream OAuth provider (GitHub, Google, etc.) that does not support RFC 7591 Dynamic Client Registration. The pattern: the MCP server runs a lightweight OAuth Authorization Server proxy that accepts DCR from MCP clients, then proxies the actual authentication to the upstream provider using pre-registered credentials from environment variables. Includes: - OAuth proxy server with /register, /authorize, /callback, /token - Protected resource metadata (RFC 9728) on the MCP server - Bearer auth middleware for MCP endpoints - README with setup instructions and security considerations - Configuration for any OAuth 2.0 provider via environment variables Closes #707 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ccb78f2 commit 233c1d9

3 files changed

Lines changed: 750 additions & 0 deletions

File tree

examples/server/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pnpm tsx src/simpleStreamableHttp.ts
3838
| Task interactive server | Task-based execution with interactive server→client requests. | [`src/simpleTaskInteractive.ts`](src/simpleTaskInteractive.ts) |
3939
| Hono Streamable HTTP server | Streamable HTTP server built with Hono instead of Express. | [`src/honoWebStandardStreamableHttp.ts`](src/honoWebStandardStreamableHttp.ts) |
4040
| SSE polling demo server | Legacy SSE server intended for polling demos. | [`src/ssePollingExample.ts`](src/ssePollingExample.ts) |
41+
| OAuth without DCR (proxy pattern) | OAuth proxy for upstream providers without Dynamic Client Registration. | [`src/oauthWithoutDcr.ts`](src/oauthWithoutDcr.ts) |
4142

4243
## OAuth demo flags (Streamable HTTP server)
4344

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# OAuth Without Dynamic Client Registration (DCR) Example
2+
3+
Many OAuth providers (GitHub, Google, Azure AD, etc.) do not support [RFC 7591 Dynamic Client Registration](https://datatracker.ietf.org/doc/html/rfc7591). This example demonstrates how to build an MCP server that authenticates users through such a provider using a **proxy
4+
pattern**.
5+
6+
## The problem
7+
8+
The MCP specification's OAuth flow expects an Authorization Server that supports DCR. When connecting to a provider that only accepts pre-registered OAuth apps, MCP clients cannot complete the standard registration step.
9+
10+
## The solution: OAuth proxy
11+
12+
The MCP server runs a lightweight OAuth Authorization Server that sits between the MCP client and the upstream provider:
13+
14+
```
15+
MCP Client <--> OAuth Proxy (this server) <--> Upstream Provider (GitHub)
16+
- Accepts DCR from clients - No DCR support
17+
- Proxies auth to upstream - Pre-registered OAuth app
18+
- Issues its own tokens - Issues upstream tokens
19+
```
20+
21+
The proxy:
22+
23+
1. **Accepts DCR** from MCP clients (issuing proxy-level client credentials)
24+
2. **Redirects authorization** to the upstream provider using pre-registered credentials
25+
3. **Exchanges upstream tokens** and issues its own tokens to MCP clients
26+
4. **Maps proxy tokens to upstream tokens** so the MCP server can call upstream APIs on behalf of the user
27+
28+
## Setup
29+
30+
### 1. Register an OAuth app with the upstream provider
31+
32+
For GitHub:
33+
34+
1. Go to [GitHub Developer Settings](https://github.com/settings/developers)
35+
2. Click "New OAuth App"
36+
3. Set the **Authorization callback URL** to `http://localhost:3001/callback`
37+
4. Note the **Client ID** and generate a **Client Secret**
38+
39+
### 2. Set environment variables
40+
41+
```bash
42+
export OAUTH_CLIENT_ID="your-github-client-id"
43+
export OAUTH_CLIENT_SECRET="your-github-client-secret"
44+
```
45+
46+
### 3. Run the server
47+
48+
From the SDK root:
49+
50+
```bash
51+
pnpm --filter @modelcontextprotocol/examples-server exec tsx src/oauthWithoutDCR.ts
52+
```
53+
54+
Or from within this package:
55+
56+
```bash
57+
pnpm tsx src/oauthWithoutDCR.ts
58+
```
59+
60+
### 4. Connect a client
61+
62+
Use the example OAuth client:
63+
64+
```bash
65+
pnpm --filter @modelcontextprotocol/examples-client exec tsx src/simpleOAuthClient.ts
66+
```
67+
68+
## Configuration
69+
70+
| Variable | Default | Description |
71+
| --------------------- | --------------------------------------------- | ------------------------------------ |
72+
| `OAUTH_CLIENT_ID` | (required) | Client ID from upstream provider |
73+
| `OAUTH_CLIENT_SECRET` | (required) | Client secret from upstream provider |
74+
| `MCP_PORT` | `3000` | Port for the MCP server |
75+
| `PROXY_PORT` | `3001` | Port for the OAuth proxy server |
76+
| `OAUTH_AUTHORIZE_URL` | `https://github.com/login/oauth/authorize` | Upstream authorization endpoint |
77+
| `OAUTH_TOKEN_URL` | `https://github.com/login/oauth/access_token` | Upstream token endpoint |
78+
| `OAUTH_SCOPES` | `read:user user:email` | Space-separated scopes for upstream |
79+
80+
## Adapting to other providers
81+
82+
To use Google instead of GitHub, set:
83+
84+
```bash
85+
export OAUTH_AUTHORIZE_URL="https://accounts.google.com/o/oauth2/v2/auth"
86+
export OAUTH_TOKEN_URL="https://oauth2.googleapis.com/token"
87+
export OAUTH_SCOPES="openid email profile"
88+
```
89+
90+
The proxy pattern works with any standard OAuth 2.0 provider. The only requirement is that you pre-register an OAuth application and provide the credentials via environment variables.
91+
92+
## Security considerations
93+
94+
This example is for **demonstration purposes**. For production use:
95+
96+
- Use HTTPS for all endpoints
97+
- Persist client registrations and tokens in a database (not in-memory)
98+
- Implement proper PKCE validation end-to-end
99+
- Implement token refresh and revocation
100+
- Restrict CORS origins
101+
- Add rate limiting to the registration and token endpoints
102+
- Validate redirect URIs strictly (exact match, no open redirects)
103+
- Set appropriate token expiration times
104+
- Consider adding CSRF protection to the authorization flow

0 commit comments

Comments
 (0)