|
| 1 | +# OAuth 2.1 Support in MCPO |
| 2 | + |
| 3 | +MCPO now supports OAuth 2.1 client-to-server authentication for MCP servers that require it. This enables secure authentication with servers using the Authorization Code flow with refresh tokens. |
| 4 | + |
| 5 | +## Features |
| 6 | + |
| 7 | +- **OAuth 2.1 Authorization Code Flow**: Full support for the modern OAuth 2.1 standard |
| 8 | +- **Automatic Token Refresh**: Tokens are automatically refreshed when they expire |
| 9 | +- **Persistent Token Storage**: Tokens can be stored in memory or on disk |
| 10 | +- **Browser-based Authorization**: Automatically opens browser for user authorization |
| 11 | +- **Loopback Callback Server**: Built-in HTTP server for catching OAuth callbacks |
| 12 | + |
| 13 | +## Configuration |
| 14 | + |
| 15 | +### Basic OAuth Configuration (Recommended) |
| 16 | + |
| 17 | +MCPO defaults to **dynamic client registration** for maximum compatibility. Most OAuth servers only need minimal configuration: |
| 18 | + |
| 19 | +```json |
| 20 | +{ |
| 21 | + "mcpServers": { |
| 22 | + "my-oauth-server": { |
| 23 | + "type": "streamable-http", |
| 24 | + "url": "http://localhost:8000/mcp", |
| 25 | + "oauth": { |
| 26 | + "server_url": "http://localhost:8000" |
| 27 | + } |
| 28 | + } |
| 29 | + } |
| 30 | +} |
| 31 | +``` |
| 32 | + |
| 33 | +This minimal configuration allows MCPO to: |
| 34 | +- Automatically discover OAuth endpoints from `/.well-known/oauth-authorization-server` |
| 35 | +- Perform dynamic client registration with the server |
| 36 | +- Use server-provided scopes and configuration |
| 37 | + |
| 38 | +### Static Client Configuration (Legacy Servers) |
| 39 | + |
| 40 | +For servers that don't support dynamic client registration, you can specify static client metadata: |
| 41 | + |
| 42 | +```json |
| 43 | +{ |
| 44 | + "mcpServers": { |
| 45 | + "legacy-oauth-server": { |
| 46 | + "type": "streamable-http", |
| 47 | + "url": "http://localhost:8000/mcp", |
| 48 | + "oauth": { |
| 49 | + "server_url": "http://localhost:8000", |
| 50 | + "storage_type": "file", |
| 51 | + "client_metadata": { |
| 52 | + "client_name": "My MCPO Client", |
| 53 | + "redirect_uris": ["http://localhost:3030/callback"], |
| 54 | + "grant_types": ["authorization_code", "refresh_token"], |
| 55 | + "response_types": ["code"] |
| 56 | + } |
| 57 | + } |
| 58 | + } |
| 59 | + } |
| 60 | +} |
| 61 | +``` |
| 62 | + |
| 63 | +> ⚠️ **Important**: Avoid hardcoding `scope`, `authorization_endpoint`, or `token_endpoint` in your configuration. These should be automatically discovered from the server's OAuth metadata during the flow. |
| 64 | +
|
| 65 | +## OAuth Configuration Options |
| 66 | + |
| 67 | +### Required Fields |
| 68 | + |
| 69 | +- `server_url`: The base URL of the OAuth server (without `/mcp` endpoint) |
| 70 | + |
| 71 | +### Optional Fields |
| 72 | + |
| 73 | +- `callback_port`: Port for the local callback server (default: 3030) |
| 74 | +- `use_loopback`: Whether to use automatic browser flow (default: true) |
| 75 | +- `storage_type`: Where to store tokens - "memory" or "file" (default: "file") |
| 76 | +- `client_metadata`: OAuth client registration metadata |
| 77 | + |
| 78 | +### Client Metadata Fields (for static registration only) |
| 79 | + |
| 80 | +- `client_name`: Display name for your client |
| 81 | +- `redirect_uris`: List of allowed redirect URIs |
| 82 | +- `grant_types`: OAuth grant types to request (typically `["authorization_code", "refresh_token"]`) |
| 83 | +- `response_types`: OAuth response types to accept (typically `["code"]`) |
| 84 | +- `token_endpoint_auth_method`: How to authenticate with the token endpoint |
| 85 | + |
| 86 | +**Fields handled automatically (don't set these):** |
| 87 | +- `scope`: Automatically provided by the server during dynamic registration |
| 88 | +- `authorization_endpoint`: Discovered from server metadata |
| 89 | +- `token_endpoint`: Discovered from server metadata |
| 90 | + |
| 91 | +## How It Works |
| 92 | + |
| 93 | +1. **Metadata Discovery**: MCPO automatically discovers OAuth endpoints from the server's `/.well-known/oauth-authorization-server` metadata |
| 94 | +2. **Dynamic Registration**: If supported, MCPO registers itself as a client with the OAuth server |
| 95 | +3. **Token Check**: MCPO checks for existing valid tokens for this server |
| 96 | +4. **Authorization Flow**: If no valid tokens exist, MCPO: |
| 97 | + - Opens your browser to the authorization URL |
| 98 | + - Starts a local HTTP server to receive the callback |
| 99 | + - Exchanges the authorization code for tokens |
| 100 | +5. **Token Storage**: Tokens are stored securely: |
| 101 | + - **File storage**: In `~/.mcpo/tokens/` directory |
| 102 | + - **Memory storage**: Only for the duration of the session |
| 103 | +6. **Token Refresh**: When tokens expire, MCPO automatically refreshes them using the refresh token |
| 104 | +7. **Authentication**: All requests to the MCP server include the access token |
| 105 | + |
| 106 | +## Storage Types |
| 107 | + |
| 108 | +### File Storage (Recommended) |
| 109 | +- Tokens persist between MCPO restarts |
| 110 | +- Stored in `~/.mcpo/tokens/` with per-server isolation |
| 111 | +- Each server gets its own token file based on a hash of the server name |
| 112 | + |
| 113 | +### Memory Storage |
| 114 | +- Tokens only exist for the current session |
| 115 | +- Lost when MCPO restarts |
| 116 | +- Useful for testing or temporary sessions |
| 117 | + |
| 118 | +## Authorization Flows |
| 119 | + |
| 120 | +### Automatic Browser Flow (Default) |
| 121 | +When `use_loopback` is `true`: |
| 122 | +1. MCPO opens your default browser to the authorization page |
| 123 | +2. After you authorize, you're redirected to `http://localhost:PORT/callback` |
| 124 | +3. MCPO's built-in server captures the authorization code |
| 125 | +4. The browser shows a success message |
| 126 | + |
| 127 | +### Manual Copy/Paste Flow |
| 128 | +When `use_loopback` is `false`: |
| 129 | +1. MCPO prints the authorization URL |
| 130 | +2. You manually open the URL in a browser |
| 131 | +3. After authorization, you copy the full callback URL |
| 132 | +4. You paste it back into MCPO when prompted |
| 133 | + |
| 134 | +## Server Support |
| 135 | + |
| 136 | +OAuth authentication is supported for: |
| 137 | +- ✅ `streamable-http` servers |
| 138 | +- ❌ `sse` servers (not currently supported) |
| 139 | +- ❌ `stdio` servers (OAuth not applicable) |
| 140 | + |
| 141 | +## Security Considerations |
| 142 | + |
| 143 | +1. **Token Storage**: File-based tokens are stored in plaintext. For production use, consider implementing encrypted storage |
| 144 | +2. **Redirect URIs**: Always use `localhost` for development. For production, use HTTPS URLs |
| 145 | +3. **Scopes**: Only request the minimum scopes necessary for your use case |
| 146 | +4. **Token Expiry**: Tokens are automatically refreshed, but expired tokens are not automatically deleted |
| 147 | + |
| 148 | +## Troubleshooting |
| 149 | + |
| 150 | +### "OAuth server_url required" |
| 151 | +Ensure your configuration includes the `server_url` field in the `oauth` section. |
| 152 | + |
| 153 | +### Browser doesn't open |
| 154 | +Check that `use_loopback` is set to `true` and that your system has a default browser configured. |
| 155 | + |
| 156 | +### "No authorization code found" |
| 157 | +If using manual flow, ensure you're copying the complete callback URL including all query parameters. |
| 158 | + |
| 159 | +### Port already in use |
| 160 | +Change the `callback_port` to an available port number. |
| 161 | + |
| 162 | +### Tokens not persisting |
| 163 | +Ensure `storage_type` is set to `"file"` and that MCPO has write permissions to `~/.mcpo/tokens/`. |
| 164 | + |
| 165 | +## Example: Testing OAuth |
| 166 | + |
| 167 | +1. Create a config file with OAuth settings: |
| 168 | +```json |
| 169 | +{ |
| 170 | + "mcpServers": { |
| 171 | + "test-oauth": { |
| 172 | + "type": "streamable-http", |
| 173 | + "url": "http://localhost:8000/mcp", |
| 174 | + "oauth": { |
| 175 | + "server_url": "http://localhost:8000", |
| 176 | + "storage_type": "file" |
| 177 | + } |
| 178 | + } |
| 179 | + } |
| 180 | +} |
| 181 | +``` |
| 182 | + |
| 183 | +2. Start MCPO: |
| 184 | +```bash |
| 185 | +mcpo --config config_oauth.json |
| 186 | +``` |
| 187 | + |
| 188 | +3. MCPO will: |
| 189 | + - Open your browser for authorization |
| 190 | + - Capture the callback |
| 191 | + - Store tokens |
| 192 | + - Connect to the MCP server |
| 193 | + |
| 194 | +4. Subsequent connections will reuse the stored tokens |
| 195 | + |
| 196 | +## Implementation Details |
| 197 | + |
| 198 | +The OAuth implementation uses the Python MCP SDK's built-in `OAuthClientProvider` which handles: |
| 199 | +- PKCE (Proof Key for Code Exchange) when required |
| 200 | +- Token refresh logic |
| 201 | +- Authorization header injection |
| 202 | +- Dynamic client registration (if supported by server) |
| 203 | + |
| 204 | +Tokens are bound to individual server instances and isolated from each other, ensuring that each OAuth-enabled server maintains its own authentication session. |
0 commit comments