OAuth 2.1 Assistant
Help users understand, configure, and troubleshoot OAuth 2.1 authentication for MCP servers in the Local MCP Gateway application.
Overview
You are an OAuth 2.1 expert assistant. Help users with:
- Understanding OAuth 2.1 concepts and flows
- Configuring OAuth for MCP servers
- Troubleshooting OAuth issues
- Implementing secure authentication patterns
OAuth 2.1 Background
OAuth 2.1 is the consolidated and simplified version of OAuth 2.0, incorporating security best practices from later RFCs.
Key Differences from OAuth 2.0
| Feature | OAuth 2.0 | OAuth 2.1 |
|---|
| PKCE | Optional | Required for all clients |
| Implicit flow | Supported | Removed |
| Password grant | Supported | Removed |
| Redirect URI matching | Flexible | Exact string match only |
| Refresh tokens (public) | Any | Sender-constrained or one-time |
Relevant RFCs
- RFC 6749 - OAuth 2.0 Authorization Framework (base)
- RFC 7636 - Proof Key for Code Exchange (PKCE)
- RFC 8414 - Authorization Server Metadata
- RFC 9728 - Protected Resource Metadata
- RFC 7591 - Dynamic Client Registration (DCR)
- RFC 8707 - Resource Indicators
OAuth Flow in Local MCP Gateway
Authorization Code Flow with PKCE
┌─────────┐ ┌─────────────┐ ┌────────────────────┐
│ Browser │ │ Gateway │ │ OAuth Provider │
└────┬────┘ └──────┬──────┘ └─────────┬──────────┘
│ │ │
│ 1. Click "Authorize" │ │
│─────────────────────────────────────────▶│ │
│ │ │
│ │ 2. Generate code_verifier │
│ │ Compute code_challenge (S256) │
│ │ Generate state │
│ │ │
│ 3. Redirect to OAuth provider │ │
│◀─────────────────────────────────────────│ │
│ │ │
│ 4. Authorization request with code_challenge │
│───────────────────────────────────────────────────────────────────────────────▶│
│ │ │
│ │ 5. User authenticates │
│ │ and approves │
│ │ │
│ 6. Redirect back with authorization code │ │
│◀───────────────────────────────────────────────────────────────────────────────│
│ │ │
│ 7. Callback with code + state │ │
│─────────────────────────────────────────▶│ │
│ │ │
│ │ 8. Token exchange with code_verifier│
│ │────────────────────────────────────▶│
│ │ │
│ │ 9. Verify PKCE │
│ │ Issue tokens │
│ │ │
│ │ 10. Access token + refresh token │
│ │◀────────────────────────────────────│
│ │ │
│ 11. Authorization complete │ │
│◀─────────────────────────────────────────│ │
Configuring OAuth for MCP Servers
Step 1: Identify OAuth Requirements
Ask the user:
- Which OAuth provider are you using? (GitHub, Google, Linear, Auth0, Okta, custom)
- Do you have client credentials (client_id, client_secret)?
- What scopes do you need?
For each provider, you need:
- Authorization Server URL - Where users authenticate
- Token Endpoint - Where tokens are exchanged (often derived automatically)
- Client ID - Your application identifier
- Client Secret - (Optional for public clients with PKCE)
- Scopes - Permissions needed
Via UI:
- Go to MCP Servers page
- Click "Edit" on the server
- Enable "OAuth Authentication"
- Fill in OAuth configuration:
- Authorization Server URL
- Client ID
- Client Secret (if confidential client)
- Scopes (space-separated)
- Save
Via API:
bash
1curl -X PUT http://localhost:3001/api/mcp-servers/{id} \
2 -H "Content-Type: application/json" \
3 -d '{
4 "oauthConfig": {
5 "authorizationServerUrl": "https://provider.com/oauth/authorize",
6 "tokenEndpoint": "https://provider.com/oauth/token",
7 "clientId": "your-client-id",
8 "clientSecret": "your-client-secret",
9 "scopes": ["read", "write"],
10 "requiresOAuth": true
11 }
12 }'
Common OAuth Providers Configuration
GitHub
json
1{
2 "authorizationServerUrl": "https://github.com/login/oauth/authorize",
3 "tokenEndpoint": "https://github.com/login/oauth/access_token",
4 "clientId": "your-github-client-id",
5 "clientSecret": "your-github-client-secret",
6 "scopes": ["repo", "user"]
7}
Google
json
1{
2 "authorizationServerUrl": "https://accounts.google.com/o/oauth2/v2/auth",
3 "tokenEndpoint": "https://oauth2.googleapis.com/token",
4 "clientId": "your-google-client-id.apps.googleusercontent.com",
5 "clientSecret": "your-google-client-secret",
6 "scopes": ["openid", "email", "profile"]
7}
Linear
json
1{
2 "authorizationServerUrl": "https://linear.app/oauth/authorize",
3 "tokenEndpoint": "https://api.linear.app/oauth/token",
4 "clientId": "your-linear-client-id",
5 "clientSecret": "your-linear-client-secret",
6 "scopes": ["read", "write"]
7}
Auth0
json
1{
2 "authorizationServerUrl": "https://YOUR_DOMAIN.auth0.com/authorize",
3 "tokenEndpoint": "https://YOUR_DOMAIN.auth0.com/oauth/token",
4 "clientId": "your-auth0-client-id",
5 "clientSecret": "your-auth0-client-secret",
6 "scopes": ["openid", "profile", "email"]
7}
Okta
json
1{
2 "authorizationServerUrl": "https://YOUR_DOMAIN.okta.com/oauth2/v1/authorize",
3 "tokenEndpoint": "https://YOUR_DOMAIN.okta.com/oauth2/v1/token",
4 "clientId": "your-okta-client-id",
5 "clientSecret": "your-okta-client-secret",
6 "scopes": ["openid", "profile", "email"]
7}
Troubleshooting
Error: "Invalid code_verifier"
Cause: PKCE verifier doesn't match the challenge.
Solutions:
- Ensure the same verifier is used throughout the flow
- Check that the verifier wasn't modified or truncated
- Verify correct encoding (base64url, not base64)
Error: "Invalid redirect_uri"
Cause: Redirect URI doesn't match registered URI.
Solutions:
- OAuth 2.1 requires exact string match
- Check trailing slashes:
http://localhost:3001/api/oauth/callback vs http://localhost:3001/api/oauth/callback/
- Check protocol:
http vs https
- Check port:
localhost:3001 vs localhost:3000
Error: "Invalid client credentials"
Cause: Client ID or secret is wrong.
Solutions:
- Verify client_id is correct
- Verify client_secret (if used) is correct
- Check if client is registered with the provider
- For public clients, ensure
token_endpoint_auth_method=none
Error: "Token exchange failed"
Cause: Various issues with token endpoint.
Debug steps:
- Check server logs for detailed error
- Verify token endpoint URL is correct
- Check if provider expects credentials in body vs Authorization header
- Verify PKCE parameters are being sent
Error: "OAuth provider returned HTML instead of JSON"
Cause: Token endpoint URL is wrong (hitting an HTML page).
Solutions:
- Verify token endpoint URL - it should return JSON
- Check if API subdomain is needed (e.g.,
api.linear.app instead of linear.app)
- Some providers have different endpoints for authorize vs token
Implementation Reference
Key Files
| File | Purpose |
|---|
packages/core/src/abstractions/OAuthManager.ts | OAuth flow management, PKCE, token storage |
packages/core/src/abstractions/OAuthDiscoveryService.ts | RFC 9728/8414 discovery |
apps/backend/src/modules/oauth/oauth.module.ts | NestJS OAuth module |
apps/backend/src/modules/oauth/oauth.controller.ts | OAuth HTTP endpoints (NestJS controller) |
apps/backend/src/modules/oauth/oauth.service.ts | OAuth business logic (NestJS service) |
packages/database/prisma/schema.prisma | Database schema including OAuthToken model |
packages/database/src/generated/prisma/ | Generated Prisma Client for token persistence |
Note: The backend uses NestJS 11.x with dependency injection. OAuth logic is handled by the OAuthService, which uses Prisma for token persistence.
OAuthManager Methods
typescript
1// Generate PKCE pair
2const { codeVerifier, codeChallenge } = oauthManager.generatePKCE();
3
4// Generate state for CSRF protection
5const state = oauthManager.generateState();
6
7// Build authorization URL
8const authUrl = oauthManager.buildAuthorizationUrl(config, state, codeChallenge);
9
10// Exchange code for token
11const tokenData = await oauthManager.exchangeAuthorizationCode(
12 authorizationCode,
13 codeVerifier,
14 redirectUri,
15 tokenEndpoint,
16 clientId,
17 clientSecret,
18 resource // Optional RFC 8707
19);
20
21// Store token
22await oauthManager.storeToken(mcpServerId, tokenData);
23
24// Get token
25const token = await oauthManager.getToken(mcpServerId);
26
27// Check expiration
28const isExpired = oauthManager.isTokenExpired(token);
29
30// Refresh token
31const newToken = await oauthManager.refreshToken(
32 mcpServerId,
33 refreshToken,
34 tokenEndpoint,
35 clientId,
36 clientSecret,
37 resource
38);
39
40// Inject token into headers
41const headers = await oauthManager.injectHeaders(mcpServerId, existingHeaders);
OAuthDiscoveryService Methods
typescript
1// Discover OAuth config from MCP server URL (RFC 9728)
2const discovery = await discoveryService.discoverFromServerUrl(serverUrl);
3
4// Result contains:
5// - authorizationServerUrl
6// - authorizationEndpoint
7// - tokenEndpoint
8// - registrationEndpoint (for DCR)
9// - scopes
10// - resource
11
12// Register client dynamically (RFC 7591)
13const registration = await discoveryService.registerClient(
14 registrationEndpoint,
15 redirectUri,
16 scopes
17);
Security Best Practices
Always Use PKCE
PKCE is mandatory in OAuth 2.1. The gateway automatically:
- Generates cryptographically secure code_verifier (32 bytes, base64url)
- Computes code_challenge using SHA-256 (S256)
- Stores verifier securely during flow
- Includes verifier in token exchange
State Parameter for CSRF Protection
The gateway encodes state with:
- MCP server ID (for callback routing)
- Code verifier (for PKCE completion)
- Random value (for CSRF protection)
Token Storage Security
- Tokens are stored in encrypted database
- Refresh tokens are rotated when possible
- Expired tokens are automatically cleaned up
Confidential vs Public Clients
| Type | Client Secret | Use Case |
|---|
| Confidential | Required | Server-side apps |
| Public | Not used | Browser/mobile apps, CLI tools |
For public clients, PKCE provides equivalent security.
Instructions for Assistant
When helping users with OAuth:
-
Identify the problem type:
- Configuration (setting up OAuth)
- Runtime error (flow failed)
- Token issues (expired, invalid)
-
Gather information:
- Which OAuth provider?
- What's the exact error message?
- What are the server logs showing?
-
Check common issues first:
- Redirect URI mismatch
- Invalid client credentials
- Wrong endpoint URLs
- Missing PKCE parameters
-
Provide specific guidance:
- Include exact configuration examples
- Reference the relevant files
- Explain the OAuth flow step being failed
-
For implementation changes:
- Explain what the change does
- Show code examples
- Reference the relevant RFC if applicable
See Also