# OAuth 2.1 Implementation Summary
## Overview
NCP now supports **OAuth 2.1 with PKCE** authentication for remote MCP servers, following the MCP specification 2025-03-26. This enables secure, standards-based authentication with automatic token management.
## Implementation Details
### Core Components
#### 1. **MCPOAuthProvider** (`src/auth/mcp-oauth-provider.ts`)
- Implements `OAuthClientProvider` interface from `@modelcontextprotocol/sdk`
- Handles complete OAuth 2.1 flow with PKCE
- Manages token storage, refresh, and lifecycle
- Supports browser-based and headless authorization flows
**Key Methods:**
- `redirectUrl`: Local callback URL for OAuth redirect (`http://localhost:{port}/callback`)
- `clientMetadata`: OAuth client metadata (name, redirect URI, scopes)
- `state()`: Generates/returns OAuth state parameter for CSRF protection
- `clientInformation()`: Loads registered client info (ID, secret)
- `saveClientInformation()`: Stores dynamically registered client
- `tokens()`: Retrieves stored OAuth tokens
- `saveTokens()`: Persists access/refresh tokens
- `redirectToAuthorization()`: Opens browser or prints URL for authorization
- `saveCodeVerifier()` / `codeVerifier()`: PKCE code verifier management
- `invalidateCredentials()`: Clears stored credentials (all/client/tokens/verifier)
**OAuth Flow:**
1. **Protected Resource Discovery** - Discovers MCP server's OAuth metadata (RFC 9728)
2. **Authorization Server Discovery** - Finds token/authorization endpoints
3. **Dynamic Registration** - Auto-registers client if no clientId provided (RFC 7591)
4. **PKCE Authorization** - Generates code challenge, opens browser for consent
5. **Token Exchange** - Exchanges authorization code for access token (with PKCE verifier)
6. **Token Refresh** - Automatically refreshes tokens when expired
#### 2. **Transport Factory Integration** (`src/orchestrator/services/transport-factory.ts`)
- Added `getOAuthProvider()` method to create/cache OAuth providers
- Modified `createStreamableHTTPTransport()` to pass `authProvider` to SDK
- Provider caching prevents duplicate authorization flows
**Key Changes:**
```typescript
// Cache OAuth providers by server URL
private oauthProviders: Map<string, MCPOAuthProvider> = new Map();
getOAuthProvider(config: MCPConfig): MCPOAuthProvider {
const key = config.url || config.name;
let provider = this.oauthProviders.get(key);
if (!provider) {
provider = createMCPOAuthProvider({
serverUrl: config.url!,
clientName: 'NCP - MCP Aggregator',
scopes: config.auth?.oauth21?.scopes,
callbackPort: config.auth?.oauth21?.callbackPort,
clientId: config.auth?.oauth21?.clientId,
clientSecret: config.auth?.oauth21?.clientSecret,
});
this.oauthProviders.set(key, provider);
}
return provider;
}
```
#### 3. **Connection Types** (`src/orchestrator/types/connection.ts`)
- Added `OAuth21Auth` type for OAuth configuration
- Extended `MCPAuth` union to include `oauth` type
**Type Definitions:**
```typescript
export type OAuth21Auth = {
type: 'oauth';
oauth21?: {
scopes?: string[];
callbackPort?: number;
clientId?: string;
clientSecret?: string;
};
};
export type MCPAuth = BearerAuth | BasicAuth | ApiKeyAuth | OAuth21Auth;
```
### Token Storage
Tokens are stored in `~/.ncp/auth/` with per-server isolation:
- File format: `{serverKey}.json`
- Server key: SHA256 hash of server URL
- Stored data:
- `clientInfo`: Registered client ID/secret
- `tokens`: Access token, refresh token, expiry
- `codeVerifier`: PKCE verifier (temporary, during auth flow)
**Security:**
- Tokens stored locally (not in config.json)
- Each server has separate token storage
- Automatic cleanup of invalid credentials
### Browser Authorization Flow
1. **NCP starts local callback server** on configured port (default: 9876)
2. **Opens browser** to authorization URL (or prints URL if headless)
3. **User grants permissions** on OAuth server
4. **OAuth server redirects** to `http://localhost:9876/callback?code=...&state=...`
5. **NCP receives callback**, exchanges code for tokens (with PKCE)
6. **Callback server shuts down**, returns success page
7. **Tokens saved** to `~/.ncp/auth/` for future use
**Headless Fallback:**
- If browser fails to open, prints authorization URL
- User manually visits URL, grants permissions
- Copies authorization code from redirect URL
- Pastes code into NCP prompt
- NCP exchanges code for tokens
## Configuration
### Basic OAuth 2.1 (Dynamic Registration)
```json
{
"mcpServers": {
"my-oauth-mcp": {
"url": "https://mcp.example.com/api",
"auth": {
"type": "oauth",
"oauth21": {
"scopes": ["read", "write"]
}
}
}
}
}
```
### Pre-registered Client
```json
{
"mcpServers": {
"my-oauth-mcp": {
"url": "https://mcp.example.com/api",
"auth": {
"type": "oauth",
"oauth21": {
"scopes": ["read", "write"],
"callbackPort": 8080,
"clientId": "my-pre-registered-client-id",
"clientSecret": "my-client-secret"
}
}
}
}
}
```
### Configuration Options
- `scopes`: Array of requested scopes (e.g., `["read", "write"]`)
- `callbackPort`: Local port for OAuth callback (default: 9876)
- `clientId`: Pre-registered client ID (optional - uses dynamic registration if omitted)
- `clientSecret`: Pre-registered client secret (optional)
## Testing
### Unit Tests (`tests/integration/oauth-integration.test.ts`)
- ✅ OAuth provider creation with default/custom config
- ✅ Interface compliance (`OAuthClientProvider`)
- ✅ Transport factory provider caching
- ✅ Multiple servers get different providers
- ✅ Static auth types don't create OAuth providers
**All 8 tests passing**
### Integration Testing
```bash
npm run build
npm test -- tests/integration/oauth-integration.test.ts
```
## SDK Compatibility
Uses official `@modelcontextprotocol/sdk` OAuth infrastructure:
- `OAuthClientProvider` interface from `sdk/client/auth.js`
- `StreamableHTTPClientTransport` with `authProvider` option
- `OAuthTokens`, `OAuthClientInformation`, `OAuthMetadata` types
- Built-in discovery, registration, and token exchange functions
**No custom OAuth implementation** - fully leverages SDK's battle-tested OAuth stack.
## Security Features
1. **PKCE Required** - OAuth 2.1 mandates PKCE (Proof Key for Code Exchange)
2. **State Parameter** - CSRF protection via state validation
3. **Token Isolation** - Per-server token storage prevents cross-contamination
4. **Secure Storage** - Tokens stored in `~/.ncp/auth/` (not in config)
5. **Automatic Refresh** - Expired tokens refreshed transparently
6. **Credential Invalidation** - Can clear all/client/tokens/verifier on demand
## Future Enhancements
Potential improvements for future releases:
- [ ] Token expiry tracking (store `issued_at` timestamp with tokens)
- [ ] Revocation endpoint support (RFC 7009)
- [ ] Device flow for headless environments (RFC 8628)
- [ ] Refresh token rotation
- [ ] Multiple callback ports (auto-find available port)
- [ ] OAuth server health checks
## References
- [MCP Specification 2025-03-26](https://modelcontextprotocol.io/specification/2025-03-26/authentication/oauth)
- [OAuth 2.1 Draft](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-11)
- [RFC 7636 - PKCE](https://datatracker.ietf.org/doc/html/rfc7636)
- [RFC 7591 - Dynamic Client Registration](https://datatracker.ietf.org/doc/html/rfc7591)
- [RFC 9728 - Protected Resource Metadata](https://datatracker.ietf.org/doc/html/rfc9728)
- [@modelcontextprotocol/sdk Documentation](https://github.com/modelcontextprotocol/sdk)
## Files Changed
### New Files
- `src/auth/mcp-oauth-provider.ts` - OAuth 2.1 provider implementation
- `tests/integration/oauth-integration.test.ts` - OAuth integration tests
- `docs/OAUTH-IMPLEMENTATION.md` - This document
### Modified Files
- `src/orchestrator/services/transport-factory.ts` - Added OAuth provider integration
- `src/orchestrator/types/connection.ts` - Added OAuth21Auth type
- `README.md` - Added OAuth 2.1 documentation
- `CHANGELOG.md` - Documented OAuth 2.1 feature
## Build & Test Results
```
✅ TypeScript compilation successful
✅ All 8 OAuth integration tests passing
✅ No breaking changes to existing functionality
✅ Transport factory properly routes OAuth configs
✅ Provider caching prevents duplicate auth flows
```
---
**Implementation complete and tested.** Ready for use with OAuth 2.1-enabled MCP servers.