Skip to content

Conversation

@gazzadownunder
Copy link
Contributor

Summary

Implements step-up authentication support as specified in the MCP Specification 2025-11-25. This allows upstream MCP servers to request additional OAuth scopes from clients when they attempt to access resources requiring elevated permissions.

Addresses and resolves Issue #49

Changes

🔐 Authentication Middleware (src/authentication.ts)

Added getScopeChallengeResponse() method to generate RFC 6750 compliant HTTP 403 responses:

getScopeChallengeResponse(
  requiredScopes: string[],
  errorDescription?: string,
  requestId?: unknown,
): { body: string; headers: Record<string, string>; statusCode: number }

Features:

  • Returns HTTP 403 (Forbidden) status code for insufficient scopes
  • Generates WWW-Authenticate header with:
    • error="insufficient_scope" (fixed error code per RFC 6750)
    • scope="scope1 scope2 ..." (space-separated required scopes)
    • resource_metadata (OAuth protected resource metadata URI)
    • error_description (optional human-readable message)
  • Returns JSON-RPC 2.0 formatted error response body
  • Properly escapes quotes in error descriptions

🔄 HTTP Server (src/startHTTPServer.ts)

Added scope challenge error detection:

  • isScopeChallengeError() type guard to identify InsufficientScopeError from upstream servers
  • Updated handleStreamRequest() to catch and convert scope challenge errors to HTTP 403 responses
  • Automatically passes authMiddleware to enable scope challenge response generation

Error flow:

Upstream MCP Server → InsufficientScopeError
       ↓
mcp-proxy detects scope challenge
       ↓
Generates HTTP 403 with WWW-Authenticate header
       ↓
Client receives required scopes and can re-authenticate

✅ Tests (src/authentication.test.ts)

Added comprehensive test coverage for getScopeChallengeResponse():

  • Returns correct HTTP 403 status code
  • Includes required scopes in WWW-Authenticate header
  • Properly formats error descriptions and escapes quotes
  • Handles single/multiple scopes correctly
  • Returns valid JSON-RPC 2.0 error responses
  • Works with and without OAuth configuration (backward compatible)

Test results:

  • ✅ 16 new tests for scope challenge functionality
  • ✅ 37 total authentication tests (all passing)
  • ✅ 81 total tests across all modules (all passing)

Example Response

When a client attempts to access a resource without sufficient scopes:

HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope", scope="read write", resource_metadata="https://example.com/.well-known/oauth-protected-resource", error_description="Additional permissions required"
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": "request-123",
  "error": {
    "code": -32001,
    "message": "Additional permissions required",
    "data": {
      "error": "insufficient_scope",
      "required_scopes": ["read", "write"]
    }
  }
}

Specification Compliance

This implementation follows:

HTTP Status Code Usage

  • 401 Unauthorized: Token is invalid or missing (existing behavior)
  • 403 Forbidden: Token is valid but has insufficient scopes (new behavior)

Backward Compatibility

  • ✅ No breaking changes to existing APIs
  • ✅ All existing authentication flows unchanged
  • ✅ Works gracefully without OAuth configuration
  • ✅ All existing tests continue to pass

Usage Example

For mcp-proxy Configuration

import { startHTTPServer } from "mcp-proxy";

await startHTTPServer({
  port: 3000,
  oauth: {
    protectedResource: {
      resource: "https://example.com"  // Base URI for OAuth metadata
    }
  },
  // ... other options
});

For Upstream MCP Servers

Upstream servers (e.g., FastMCP) can throw errors that mcp-proxy will automatically convert:

class InsufficientScopeError extends Error {
  name = "InsufficientScopeError";
  data = {
    error: "insufficient_scope",
    requiredScopes: ["read", "write"],
    errorDescription: "This operation requires read and write access"
  };
}

mcp-proxy will automatically detect this error and return an HTTP 403 response with the appropriate WWW-Authenticate header.

Testing

All tests pass:

pnpm test
# ✓ 6 test files (81 tests passed)
# ✓ TypeScript compilation successful
# ✓ ESLint checks passing

Build successful:

pnpm run build
# ✔ Build complete

Documentation

  • Added STEP_UP_IMPLEMENTATION.md with complete implementation details
  • Includes usage examples, error flow diagrams, and integration guide

Related

This PR implements the mcp-proxy portion of step-up authentication. For full end-to-end support, upstream MCP servers (like FastMCP) need to implement the InsufficientScopeError error type as described in the documentation.

Added support for ​Scope Challenge Handling and Step-up Authentication
@punkpeye punkpeye merged commit dc5f1ab into punkpeye:main Dec 1, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants