Files
mcp-use__mcp-use/docs/python/client/authentication/oauth.mdx
Vincenzo Reina aca5f9c040 feat(python): Adapt OAuth to 2.1 version from new MCP spec (#518)
* feat: Add PRM in the metadata discovery

* fix: fallback anyway to PRM

* feat: Implement complete discovery logic

* feat: Final implementation of CIMD

* feat: Implement PKCE

* feat: Add scope from WWW-Authenticate

* feat: Add all URLs possible styles

* feat: Update tests and example

* docs: Update docs

* fix: linting

* docs: update API documentation

* fix: correct typos in OAuth metadata and error messages

- Updated the comment in ProtectedResourceMetadata to use the correct abbreviation (PRM).
- Fixed a typo in the OAuthAuthenticationError message regarding code challenge support.
- Corrected the comment for legacy client paths to use "pre-registered" instead of "pre-reggistered".

* feat: restructure authentication documentation and add new methods

- Updated the authentication section in  to group authentication methods under a new Authentication category with an icon.
- Removed the old  file and replaced it with new documentation files for Bearer Token, Custom Auth, and OAuth 2.1.
- Introduced an overview page for authentication methods, detailing usage and configuration for each method.
- Enhanced security best practices and examples for each authentication type to improve clarity and usability.

* refactor: improve debug logging for OAuth metadata discovery

- Updated debug messages to provide clearer context by specifying the issuer or host being queried during OAuth and OpenID Connect metadata discovery.
- Enhanced error logging to remove redundant information, focusing on the failure reason without repeating the URL.

* fix: correct abbreviation in ProtectedResourceMetadata documentation

- Updated the abbreviation from PRC to PRM in the ProtectedResourceMetadata section to ensure accuracy in the documentation.

* fix(oauth): correct RFC 8414 well-known URL construction for path-based issuers

  The OAuth metadata discovery was incorrectly constructing .well-known URLs
  by appending to the issuer instead of inserting between host and path.

  RFC 8414 specifies that for an issuer like https://github.com/login/oauth,
  the .well-known URL should be:
    https://github.com/.well-known/oauth-authorization-server/login/oauth

  We were incorrectly building:
    https://github.com/login/oauth/.well-known/oauth-authorization-server

  This fix:
  - Implements correct RFC 8414 URL construction (insert between host/path)
  - Supports OpenID Connect style as fallback (append to issuer)
  - Removes GitHub-specific hardcode since discovery now works correctly
  - Adds documentation comments explaining the two standards

  Ref: https://www.rfc-editor.org/rfc/rfc8414.html#section-3

* chore: oauth examples

* docs: enhance OAuth endpoint discovery documentation

- Updated the OAuth documentation to clarify how `mcp-use` discovers OAuth endpoints using both OAuth 2.0 and OpenID Connect standards.
- Added a table detailing the URL construction methods for each standard.
- Included examples for better understanding and a note on fallback behavior when the issuer has no path.

* response to bs codeql

* fix: update API URL Updated the GitHub API URL in the configuration to the correct endpoint for OAuth operations.

---------

Co-authored-by: Enrico Toniato <2827496+tonxxd@users.noreply.github.com>
Co-authored-by: pietrozullo <pietro.zullo@gmail.com>
2026-01-11 18:09:34 +01:00

362 lines
11 KiB
Plaintext

---
title: "OAuth 2.1"
description: "MCP-compliant OAuth 2.1 authentication with PKCE, Protected Resource Metadata discovery, and multiple client registration methods."
icon: "shield-check"
---
## Overview
MCP implements [OAuth 2.1](https://datatracker.ietf.org/doc/draft-ietf-oauth-v2-1/) for HTTP transports, enabling clients to access restricted servers on behalf of users. The implementation follows the [MCP Authorization Specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization).
**Key requirements:**
- **PKCE (S256)** - Mandatory for all authorization flows
- **Resource parameter** - Tokens are bound to specific MCP servers
- **Protected Resource Metadata** - Standard discovery mechanism
## Authorization Flow
```mermaid
sequenceDiagram
participant C as Client
participant M as MCP Server
participant A as Auth Server
participant B as Browser
Note over C,M: 1. Discovery
C->>M: MCP request (no auth)
M-->>C: 401 + WWW-Authenticate
C->>M: GET /.well-known/oauth-protected-resource
M-->>C: PRM (authorization_servers, scopes)
C->>A: GET /.well-known/oauth-authorization-server
A-->>C: AS Metadata (endpoints, capabilities)
Note over C,A: 2. Client Registration (if needed)
alt CIMD
Note over C: Use metadata URL as client_id
else DCR
C->>A: POST /register
A-->>C: client_id, client_secret
else Pre-registered
Note over C: Use configured credentials
end
Note over C,B: 3. Authorization (PKCE)
C->>C: Generate code_verifier, code_challenge
C->>B: Open authorization URL
B->>A: Authorize (code_challenge, resource, scope)
A->>B: Consent page
B->>A: User approves
A->>B: Redirect with code
B->>C: Authorization code
Note over C,A: 4. Token Exchange
C->>A: POST /token (code, code_verifier, resource)
A-->>C: access_token, refresh_token
Note over C,M: 5. Authenticated Access
C->>M: MCP request (Bearer token)
M-->>C: MCP response
```
## Discovery
### Protected Resource Metadata (PRM)
When connecting to an MCP server, `mcp-use` discovers how to authenticate:
**1. WWW-Authenticate header** (preferred)
```http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource",
scope="mcp:tools mcp:resources"
```
**2. Well-known URLs** (fallback)
```
/.well-known/oauth-protected-resource/path/to/mcp (path-specific)
/.well-known/oauth-protected-resource (root)
```
The PRM document tells the client which authorization servers to use:
```json
{
"resource": "https://mcp.example.com/mcp",
"authorization_servers": ["https://auth.example.com"],
"scopes_supported": ["mcp:tools", "mcp:resources"]
}
```
### Authorization Server Metadata
For each authorization server in PRM, `mcp-use` discovers OAuth endpoints using two standards:
| Standard | URL Construction |
|----------|------------------|
| OAuth 2.0 ([RFC 8414](https://www.rfc-editor.org/rfc/rfc8414.html#section-3)) | Insert `.well-known` between host and path |
| OpenID Connect | Append `.well-known` to issuer |
For an issuer like `https://github.com/login/oauth`:
```
OAuth 2.0: https://github.com/.well-known/oauth-authorization-server/login/oauth
OIDC: https://github.com/login/oauth/.well-known/openid-configuration
```
<Note>
When the issuer has no path, both methods produce the same URL. `mcp-use` tries OAuth 2.0 style first, then falls back to OpenID Connect.
</Note>
## Client Registration
`mcp-use` supports three methods to obtain OAuth credentials, in priority order:
### 1. Pre-registered Credentials
Use existing OAuth app credentials:
```python
config = {
"mcpServers": {
"github": {
"url": "https://api.githubcopilot.com/mcp/",
"auth": {
"client_id": "your-client-id",
"client_secret": "your-client-secret",
"scope": "repo",
"callback_port": 8080
}
}
}
}
```
<Tip>
Use this when your OAuth provider requires manual app registration (like GitHub).
</Tip>
### 2. Client ID Metadata Documents (CIMD)
CIMD lets you use a URL as your `client_id`. The authorization server fetches your client metadata from that URL.
**Create a metadata document** (host at a public HTTPS URL):
```json
{
"client_id": "https://your-app.com/oauth/metadata.json",
"client_name": "Your App",
"redirect_uris": ["http://127.0.0.1:8080/callback"],
"grant_types": ["authorization_code"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}
```
**Configure mcp-use:**
```python
config = {
"mcpServers": {
"example": {
"url": "https://mcp.example.com/mcp",
"auth": {
"client_metadata_url": "https://your-app.com/oauth/metadata.json"
}
}
}
}
```
<Note>
CIMD is available when the authorization server advertises `client_id_metadata_document_supported: true`.
</Note>
### 3. Dynamic Client Registration (DCR)
When no credentials are configured and the server supports DCR, `mcp-use` registers automatically:
```python
config = {
"mcpServers": {
"linear": {
"url": "https://mcp.linear.app/sse"
# No auth config - uses DCR automatically
}
}
}
```
DCR sends a registration request to the `registration_endpoint`:
```json
{
"client_name": "mcp-use",
"redirect_uris": ["http://127.0.0.1:8080/callback"],
"grant_types": ["authorization_code"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}
```
Registered credentials are cached in `~/.mcp_use/tokens/registrations/`.
## PKCE (Required)
PKCE (Proof Key for Code Exchange) protects against authorization code interception attacks. `mcp-use` implements PKCE S256 for all OAuth flows.
**How it works:**
1. Client generates a random `code_verifier`
2. Client computes `code_challenge = BASE64URL(SHA256(code_verifier))`
3. Authorization request includes `code_challenge` and `code_challenge_method=S256`
4. Token request includes `code_verifier` for validation
<Warning>
Servers that don't support PKCE S256 are rejected. This is a security requirement of the MCP spec.
</Warning>
## Resource Parameter
Per [RFC 8707](https://datatracker.ietf.org/doc/html/rfc8707), `mcp-use` includes the `resource` parameter in authorization requests to bind tokens to specific MCP servers.
This prevents tokens from being used with unintended servers (confused deputy attacks).
## Scope Selection
`mcp-use` selects scopes in this order:
1. **WWW-Authenticate header** - Use `scope` from 401 response
2. **PRM document** - Use `scopes_supported` if no scope in header
3. **Configured scope** - Use your explicit `scope` configuration
4. **Omit** - Let the authorization server decide
## Configuration Reference
All OAuth parameters go inside the `auth` object of your server configuration:
```python
config = {
"mcpServers": {
"my_server": {
"url": "https://mcp.example.com/mcp",
"auth": {
"client_id": "...", # Pre-registered OAuth client ID
"client_secret": "...", # OAuth client secret (confidential clients only)
"scope": "read write", # Space-separated scopes to request
"callback_port": 8080, # Local callback port (default: 8080)
"client_metadata_url": "...", # CIMD document URL
"oauth_provider": { ... } # Skip discovery with explicit metadata
}
}
}
}
```
| Parameter | Type | Description |
|-----------|------|-------------|
| `client_id` | string | Pre-registered OAuth client ID |
| `client_secret` | string | OAuth client secret (confidential clients only) |
| `scope` | string | Space-separated scopes to request |
| `callback_port` | int | Local callback port (default: 8080) |
| `client_metadata_url` | string | CIMD document URL |
| `oauth_provider` | object | Skip discovery with explicit metadata |
### OAuth Provider (Skip Discovery)
If you know the OAuth endpoints, skip discovery:
```python
config = {
"mcpServers": {
"custom": {
"url": "https://mcp.example.com/mcp",
"auth": {
"oauth_provider": {
"id": "custom",
"display_name": "Custom Server",
"metadata": {
"issuer": "https://auth.example.com",
"authorization_endpoint": "https://auth.example.com/authorize",
"token_endpoint": "https://auth.example.com/token",
"code_challenge_methods_supported": ["S256"]
}
}
}
}
}
}
```
## Token Storage
Tokens are stored locally and reused across sessions:
| Data | Location |
|------|----------|
| Access tokens | `~/.mcp_use/tokens/{server}.json` |
| Client registrations | `~/.mcp_use/tokens/registrations/{server}_registration.json` |
## Troubleshooting
<AccordionGroup>
<Accordion title="Server requires PKCE S256" icon="shield-exclamation">
The authorization server doesn't advertise `code_challenge_methods_supported: ["S256"]`.
<Warning>
PKCE S256 is required by the MCP spec. Contact the server administrator to enable it.
</Warning>
</Accordion>
<Accordion title="OAuth server only supports CIMD" icon="file-code">
The server has `client_id_metadata_document_supported: true` but no `registration_endpoint`.
**Solution:** Configure a CIMD document URL:
```python
"auth": {
"client_metadata_url": "https://your-app.com/oauth/metadata.json"
}
```
</Accordion>
<Accordion title="Port already in use" icon="plug">
Another process is using port 8080.
**Solution:** Use a different callback port:
```python
"auth": {
"callback_port": 8081
}
```
</Accordion>
<Accordion title="Invalid redirect URI" icon="link-slash">
Your OAuth app's registered redirect URI doesn't match the callback URL.
**Solutions:**
<Steps>
<Step title="Update OAuth app">
Add `http://127.0.0.1:8080/callback` to your OAuth app's allowed redirect URIs
</Step>
<Step title="Or use DCR">
Remove `client_id` from config to use automatic registration
</Step>
<Step title="Or match the port">
Set `callback_port` to match your registered redirect URI
</Step>
</Steps>
</Accordion>
</AccordionGroup>
## Servers with OAuth Support
| Server | Registration | URL |
|--------|-------------|-----|
| Linear | DCR | `https://mcp.linear.app/sse` |
| Asana | DCR | `https://mcp.asana.com/sse` |
| Atlassian | DCR | `https://mcp.atlassian.com/v1/sse` |
| GitHub | Pre-registered | `https://api.githubcopilot.com/mcp/` |