# OAuth 2.0 + PKCE Flow Analysis This document details the complete OAuth 2.0 authorization code flow with PKCE as implemented in this reference server, including how it differs between integrated and separate modes. ## Flow Overview The server implements OAuth 2.1 with PKCE (Proof Key for Code Exchange) for secure authorization. Here's how each step maps to data storage, expiry, and mode-specific behavior: ## 1. Client Registration **Purpose**: Register the application with the OAuth server (happens once during app setup). **Flow**: ``` App → Auth Server: POST /register {"client_name": "...", "redirect_uris": [...]} Auth Server → App: {"client_id": "XYZ", "client_secret": "ABC", ...} ``` **Storage**: - Redis key: `auth:client:{clientId}` - Expiry: 30 days (long-lived app credentials) **Mode differences**: None - identical in both modes. --- ## 2. Authorization Request **Purpose**: User initiates connection to MCP server. **Flow**: ``` App → Auth Server: GET /authorize? client_id=XYZ &redirect_uri=http://localhost:3000/callback &code_challenge= &code_challenge_method=S256 &state= Auth Server: Saves pending authorization, shows authorization page ``` **Storage**: - Redis key: `auth:pending:{authCode}` - Expiry: 10 minutes (short-lived temporary state) - Data: redirect_uri, code_challenge, code_challenge_method, client_id, state **Mode differences**: - **Embedded OAuth**: OAuth server runs on MCP server (port 3232) - **External OAuth**: OAuth server runs independently (port 3001) --- ## 3. User Authentication & Authorization **Purpose**: Authenticate the user and obtain consent. **Flow** (identical in both modes): ``` OAuth Server → User: Shows auth page with "Continue to Authentication" button User → OAuth Server: Clicks button OAuth Server → Upstream IDP: Redirects to /mock-upstream-idp/authorize Upstream IDP → User: Shows user selection UI User → Upstream IDP: Selects/creates user ID Upstream IDP → OAuth Server: Redirects to /mock-upstream-idp/callback?userId=X OAuth Server: Validates user, issues authorization code OAuth Server → App: Redirects to app's redirect_uri with code ``` **Mode differences**: - **Embedded OAuth**: OAuth Server runs on MCP server (port 3232) - **External OAuth**: OAuth Server runs independently (port 3001) The flow itself is identical. Both modes delegate user authentication to an upstream IDP (simulated by `/mock-upstream-idp/*` endpoints, which represent corporate SSO or social login in production). --- ## 4. Authorization Code Exchange **Purpose**: Exchange authorization code for access and refresh tokens. **Flow**: ``` App → Auth Server: POST /token grant_type=authorization_code &client_id=XYZ &client_secret=ABC &code= &redirect_uri=http://localhost:3000/callback &code_verifier= Auth Server: 1. Validates code_verifier matches code_challenge (PKCE) 2. Validates redirect_uri matches original 3. Validates client credentials 4. Issues tokens Auth Server → App: { "access_token": "...", "refresh_token": "...", "expires_in": 604800, "token_type": "Bearer" } ``` **Storage**: - `auth:exch:{authCode}` - Token exchange record (prevents replay attacks) - `auth:installation:{accessToken}` - Active MCP installation - `auth:refresh:{refreshToken}` - Refresh token mapping - Expiry: 10 minutes for exchange record, 7 days for installation **Mode differences**: None in the exchange itself. The endpoint location differs: - **Embedded OAuth**: `http://localhost:3232/token` - **External OAuth**: `http://localhost:3001/token` --- ## 5. Using Access Tokens **Purpose**: Access MCP resources with the token. **Flow**: ``` App → MCP Server: POST /mcp Authorization: Bearer Mcp-Session-Id: {MCP request} MCP Server: Validates token → Serves MCP resource ``` **Token Validation - Embedded OAuth**: ``` MCP Server (same process as OAuth server): - Reads auth:installation:{accessToken} from Redis - Validates expiry, scopes - Returns user info directly ``` **Token Validation - External OAuth**: ``` MCP Server: - Calls POST {AUTH_SERVER_URL}/introspect with token - Auth server validates and returns token info - MCP server validates audience matches BASE_URI - Proceeds with request ``` **Mode differences**: This is the key architectural difference: - **Embedded OAuth**: In-process token validation (fast, direct Redis access) - **External OAuth**: Remote token validation via HTTP introspection (adds network hop, follows RFC 7662) --- ## 6. Token Refresh **Purpose**: Obtain new access token when current one expires. **Flow**: ``` App → Auth Server: POST /token grant_type=refresh_token &refresh_token= &client_id=XYZ &client_secret=ABC Auth Server: 1. Validates refresh_token 2. Issues new access_token (and optionally new refresh_token) 3. Updates installation record Auth Server → App: {"access_token": "...", ...} ``` **Storage**: - Reads: `auth:refresh:{refreshToken}` → gets access token - Reads: `auth:installation:{accessToken}` → gets installation - Writes: New `auth:installation:{newAccessToken}` - Writes: New `auth:refresh:{newRefreshToken}` - Expiry: 7 days **Mode differences**: None - identical flow, just different endpoint locations. --- ## Data Lifecycle Hierarchy **Timeline (shortest to longest expiry):** 1. **OAuth flow state** (10 minutes) - `auth:pending`, `auth:exch` 2. **User sessions** (7 days) - `auth:installation`, `auth:refresh` 3. **Client credentials** (30 days) - `auth:client` This hierarchy ensures each layer outlives the layers it supports. --- ## Security: PKCE (Proof Key for Code Exchange) PKCE prevents authorization code interception attacks: 1. **Authorization request**: Client sends `code_challenge` (SHA256 hash of random string) 2. **Token exchange**: Client must provide original `code_verifier` 3. **Server validates**: SHA256(code_verifier) must equal stored code_challenge This ensures only the client that initiated the flow can exchange the code, even if the code is intercepted. **Mode differences**: None - PKCE works identically in both modes. --- ## References - [RFC 6749: OAuth 2.0 Framework](https://datatracker.ietf.org/doc/html/rfc6749) - [RFC 7636: PKCE](https://datatracker.ietf.org/doc/html/rfc7636) - [RFC 7662: Token Introspection](https://datatracker.ietf.org/doc/html/rfc7662) - [MCP Authorization Spec](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization)