|
| 1 | +# OAuth 2.1 Implementation - Change Log |
| 2 | + |
| 3 | +## Summary |
| 4 | + |
| 5 | +OAuth 2.1 authorization support has been added to the MCP Framework, implementing the Model Context Protocol authorization specification. This enables secure user authentication with authorization servers like AWS Cognito, Auth0, Okta, and others. |
| 6 | + |
| 7 | +## New Files Created |
| 8 | + |
| 9 | +### Core Implementation |
| 10 | +- **`src/auth/providers/oauth.ts`** (630+ lines) |
| 11 | + - Complete OAuth 2.1 provider with PKCE |
| 12 | + - Token validation (JWT and introspection) |
| 13 | + - Protected Resource Metadata generation |
| 14 | + - Authorization flow management |
| 15 | + - Token caching with configurable TTL |
| 16 | + |
| 17 | +### Documentation |
| 18 | +- **`OAUTH_GUIDE.md`** - Comprehensive OAuth setup guide with: |
| 19 | + - Feature overview |
| 20 | + - Quick start examples |
| 21 | + - AWS Cognito setup instructions |
| 22 | + - Authorization flow details |
| 23 | + - Advanced configuration |
| 24 | + - Security considerations |
| 25 | + - Testing instructions |
| 26 | + - Troubleshooting guide |
| 27 | + |
| 28 | +- **`OAUTH_IMPLEMENTATION_SUMMARY.md`** - Technical implementation summary |
| 29 | +- **`OAUTH_QUICK_START.md`** - 5-minute quick start guide |
| 30 | + |
| 31 | +### Examples |
| 32 | +- **`examples/oauth-simple-example.ts`** - Minimal OAuth setup |
| 33 | +- **`examples/oauth-cognito-example.ts`** - Complete Cognito integration |
| 34 | +- **`examples/oauth-custom-validator.ts`** - Advanced token validation |
| 35 | +- **`examples/oauth-test-client.ts`** - OAuth testing client |
| 36 | +- **`examples/README.md`** - Examples documentation with flow diagrams |
| 37 | + |
| 38 | +## Modified Files |
| 39 | + |
| 40 | +### Core Framework |
| 41 | + |
| 42 | +1. **`src/auth/types.ts`** |
| 43 | + - Added optional `headers` field to `AuthProvider.getAuthError()` return type |
| 44 | + - Added `oauth` endpoint option to `AuthConfig.endpoints` |
| 45 | + |
| 46 | +2. **`src/auth/index.ts`** |
| 47 | + - Export `OAuthProvider` class |
| 48 | + - Export `OAuthConfig` type |
| 49 | + |
| 50 | +3. **`src/transports/sse/types.ts`** |
| 51 | + - Added `oauth` configuration section with callback handlers |
| 52 | + - Updated `SSETransportConfigInternal` to include oauth option |
| 53 | + |
| 54 | +4. **`src/transports/sse/server.ts`** |
| 55 | + - Added `/.well-known/oauth-protected-resource` endpoint (RFC 9728) |
| 56 | + - Added OAuth callback handler at `/oauth/callback` |
| 57 | + - Enhanced authentication handling with OAuth-specific WWW-Authenticate headers |
| 58 | + - Added `handleProtectedResourceMetadata()` method |
| 59 | + - Added `handleOAuthCallback()` method |
| 60 | + - Updated `handleAuthentication()` to support OAuth provider |
| 61 | + |
| 62 | +5. **`README.md`** |
| 63 | + - Added OAuth 2.1 authentication section |
| 64 | + - Updated features list to mention OAuth support |
| 65 | + - Added links to OAuth documentation |
| 66 | + |
| 67 | +## Features Implemented |
| 68 | + |
| 69 | +### OAuth 2.1 Core |
| 70 | +- ✅ Authorization Code Flow with PKCE (RFC 7636) |
| 71 | +- ✅ Token validation (JWT and opaque tokens) |
| 72 | +- ✅ State parameter for CSRF protection |
| 73 | +- ✅ Token caching with configurable TTL |
| 74 | +- ✅ Automatic token cache cleanup |
| 75 | + |
| 76 | +### RFC Compliance |
| 77 | +- ✅ **RFC 9728**: OAuth 2.0 Protected Resource Metadata |
| 78 | + - `/.well-known/oauth-protected-resource` endpoint |
| 79 | + - Automatic metadata generation |
| 80 | + |
| 81 | +- ✅ **RFC 8707**: Resource Indicators for OAuth 2.0 |
| 82 | + - `resource` parameter in authorization/token requests |
| 83 | + - Audience validation in tokens |
| 84 | + |
| 85 | +- ✅ **RFC 8414**: OAuth 2.0 Authorization Server Metadata |
| 86 | + - Automatic metadata discovery |
| 87 | + - Endpoint caching |
| 88 | + |
| 89 | +- ✅ **RFC 7636**: Proof Key for Code Exchange |
| 90 | + - SHA256 code challenge generation |
| 91 | + - Code verifier validation |
| 92 | + |
| 93 | +### Security Features |
| 94 | +- ✅ Strict audience validation (prevents token misuse) |
| 95 | +- ✅ PKCE mandatory for all flows |
| 96 | +- ✅ WWW-Authenticate headers with proper metadata |
| 97 | +- ✅ Token passthrough prevention |
| 98 | +- ✅ Configurable security policies |
| 99 | + |
| 100 | +### Integration Support |
| 101 | +- ✅ AWS Cognito compatibility |
| 102 | +- ✅ Auth0 support |
| 103 | +- ✅ Okta support |
| 104 | +- ✅ Custom token validators |
| 105 | +- ✅ Generic OAuth 2.1 server support |
| 106 | + |
| 107 | +## API Changes |
| 108 | + |
| 109 | +### New Exports from `mcp-framework` |
| 110 | + |
| 111 | +```typescript |
| 112 | +// OAuth Provider |
| 113 | +export { OAuthProvider } from "./auth/providers/oauth.js"; |
| 114 | + |
| 115 | +// OAuth Types |
| 116 | +export type { OAuthConfig } from "./auth/providers/oauth.js"; |
| 117 | +``` |
| 118 | + |
| 119 | +### New Configuration Options |
| 120 | + |
| 121 | +```typescript |
| 122 | +// SSE Transport OAuth Configuration |
| 123 | +interface SSETransportConfig { |
| 124 | + oauth?: { |
| 125 | + onCallback?: (params: { |
| 126 | + accessToken: string; |
| 127 | + refreshToken?: string; |
| 128 | + expiresIn?: number; |
| 129 | + state?: string; |
| 130 | + }) => Promise<void> | void; |
| 131 | + |
| 132 | + onError?: (error: Error, state?: string) => Promise<void> | void; |
| 133 | + }; |
| 134 | +} |
| 135 | + |
| 136 | +// OAuth Provider Configuration |
| 137 | +interface OAuthConfig { |
| 138 | + authorizationServer: string; |
| 139 | + clientId?: string; |
| 140 | + clientSecret?: string; |
| 141 | + resourceUri: string; |
| 142 | + callbackPath?: string; |
| 143 | + tokenEndpoint?: string; |
| 144 | + jwksUri?: string; |
| 145 | + requiredScopes?: string[]; |
| 146 | + customValidator?: (token: string) => Promise<{ valid: boolean; data?: Record<string, any> }>; |
| 147 | + tokenCacheTTL?: number; |
| 148 | + supportDynamicRegistration?: boolean; |
| 149 | + authorizationEndpoint?: string; |
| 150 | + strictAudienceValidation?: boolean; |
| 151 | + metadata?: Record<string, any>; |
| 152 | +} |
| 153 | +``` |
| 154 | + |
| 155 | +## Usage Example |
| 156 | + |
| 157 | +### Before (API Key) |
| 158 | +```typescript |
| 159 | +import { MCPServer, APIKeyAuthProvider } from "mcp-framework"; |
| 160 | + |
| 161 | +const server = new MCPServer({ |
| 162 | + transport: { |
| 163 | + type: "sse", |
| 164 | + options: { |
| 165 | + auth: { |
| 166 | + provider: new APIKeyAuthProvider({ |
| 167 | + keys: [process.env.API_KEY] |
| 168 | + }) |
| 169 | + } |
| 170 | + } |
| 171 | + } |
| 172 | +}); |
| 173 | +``` |
| 174 | + |
| 175 | +### After (OAuth with Cognito) |
| 176 | +```typescript |
| 177 | +import { MCPServer, OAuthProvider } from "mcp-framework"; |
| 178 | + |
| 179 | +const server = new MCPServer({ |
| 180 | + transport: { |
| 181 | + type: "sse", |
| 182 | + options: { |
| 183 | + auth: { |
| 184 | + provider: new OAuthProvider({ |
| 185 | + authorizationServer: "https://your-domain.auth.us-east-1.amazoncognito.com", |
| 186 | + clientId: process.env.COGNITO_CLIENT_ID, |
| 187 | + clientSecret: process.env.COGNITO_CLIENT_SECRET, |
| 188 | + resourceUri: "https://mcp.example.com", |
| 189 | + requiredScopes: ["openid", "profile"], |
| 190 | + }), |
| 191 | + endpoints: { |
| 192 | + sse: false, |
| 193 | + messages: true, |
| 194 | + } |
| 195 | + }, |
| 196 | + oauth: { |
| 197 | + onCallback: async ({ accessToken, refreshToken }) => { |
| 198 | + console.log("User authorized successfully!"); |
| 199 | + }, |
| 200 | + onError: async (error) => { |
| 201 | + console.error("Authorization failed:", error); |
| 202 | + } |
| 203 | + } |
| 204 | + } |
| 205 | + } |
| 206 | +}); |
| 207 | +``` |
| 208 | + |
| 209 | +## Backward Compatibility |
| 210 | + |
| 211 | +✅ **Fully Backward Compatible** |
| 212 | + |
| 213 | +- All existing authentication providers (JWT, API Key) continue to work unchanged |
| 214 | +- OAuth is opt-in - no breaking changes to existing code |
| 215 | +- SSE transport works exactly as before if OAuth is not configured |
| 216 | +- No changes to STDIO transport |
| 217 | + |
| 218 | +## Testing |
| 219 | + |
| 220 | +### Automated Tests |
| 221 | +- OAuth provider can be unit tested independently |
| 222 | +- PKCE generation is deterministic and testable |
| 223 | +- Token validation logic is isolated |
| 224 | + |
| 225 | +### Manual Testing |
| 226 | +- Test client provided (`examples/oauth-test-client.ts`) |
| 227 | +- Full example servers for various scenarios |
| 228 | +- Step-by-step testing instructions in documentation |
| 229 | + |
| 230 | +## Documentation |
| 231 | + |
| 232 | +| Document | Purpose | |
| 233 | +|----------|---------| |
| 234 | +| `OAUTH_GUIDE.md` | Complete setup and usage guide | |
| 235 | +| `OAUTH_QUICK_START.md` | 5-minute quick start | |
| 236 | +| `OAUTH_IMPLEMENTATION_SUMMARY.md` | Technical details | |
| 237 | +| `examples/README.md` | Example usage and flow diagrams | |
| 238 | +| `README.md` | Updated with OAuth section | |
| 239 | + |
| 240 | +## Migration Guide |
| 241 | + |
| 242 | +### From API Key to OAuth |
| 243 | + |
| 244 | +1. Replace `APIKeyAuthProvider` with `OAuthProvider` |
| 245 | +2. Configure OAuth settings (authorization server, client ID, resource URI) |
| 246 | +3. Add OAuth callback handlers |
| 247 | +4. Update client to use OAuth flow instead of static API keys |
| 248 | + |
| 249 | +### From JWT to OAuth |
| 250 | + |
| 251 | +1. Replace `JWTAuthProvider` with `OAuthProvider` |
| 252 | +2. Configure authorization server (instead of JWT secret) |
| 253 | +3. Optionally use `customValidator` for JWT validation with JWKS |
| 254 | +4. Add OAuth callback handlers |
| 255 | + |
| 256 | +## Known Limitations |
| 257 | + |
| 258 | +1. **HTTP Only**: OAuth requires HTTP-based transport (SSE) |
| 259 | +2. **No Token Persistence**: Framework doesn't persist refresh tokens |
| 260 | +3. **Basic JWT Validation**: Use `customValidator` for production JWKS validation |
| 261 | +4. **No Dynamic Registration**: Clients must pre-register with auth server |
| 262 | + |
| 263 | +## Future Enhancements |
| 264 | + |
| 265 | +Planned improvements: |
| 266 | +- Built-in JWKS-based JWT validation |
| 267 | +- Refresh token management |
| 268 | +- Token revocation support |
| 269 | +- Full dynamic client registration (RFC 7591) |
| 270 | +- OpenID Connect support |
| 271 | +- Multi-tenant authorization |
| 272 | + |
| 273 | +## Dependencies |
| 274 | + |
| 275 | +No new dependencies added - uses existing dependencies: |
| 276 | +- `jsonwebtoken` (already present for JWT auth) |
| 277 | +- Native `crypto` module for PKCE |
| 278 | +- Native `fetch` for HTTP requests |
| 279 | + |
| 280 | +## Performance |
| 281 | + |
| 282 | +- Token validation results are cached (default 5 minutes) |
| 283 | +- PKCE session cleanup runs automatically |
| 284 | +- Authorization server metadata is cached (1 hour) |
| 285 | +- Minimal overhead - only validates tokens on protected endpoints |
| 286 | + |
| 287 | +## Security Audit Checklist |
| 288 | + |
| 289 | +✅ PKCE mandatory for all authorization flows |
| 290 | +✅ State parameter prevents CSRF |
| 291 | +✅ Strict audience validation prevents token misuse |
| 292 | +✅ Token caching reduces validation overhead |
| 293 | +✅ Tokens not logged or exposed |
| 294 | +✅ HTTPS enforcement for authorization server |
| 295 | +✅ Proper WWW-Authenticate headers |
| 296 | +✅ No token passthrough to upstream services |
| 297 | + |
| 298 | +## Version |
| 299 | + |
| 300 | +- **Implementation Date**: October 30, 2025 |
| 301 | +- **MCP Framework Version**: 0.1.29+ |
| 302 | +- **OAuth Specification**: OAuth 2.1 (draft-ietf-oauth-v2-1-13) |
| 303 | + |
| 304 | +## Contributors |
| 305 | + |
| 306 | +Implementation completed by AI Assistant for MCP Framework. |
| 307 | + |
0 commit comments