All files / src/oauth/endpoints metadata.ts

82.35% Statements 14/17
83.33% Branches 5/6
75% Functions 3/4
82.35% Lines 14/17

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129                    75x                   75x   14x 14x     14x 14x   14x                           75x 9x     9x                                                                 9x                       75x                                                         75x 2x            
/**
 * OAuth Metadata Endpoint
 *
 * Implements the OAuth 2.0 Authorization Server Metadata endpoint
 * (RFC 8414) at /.well-known/oauth-authorization-server
 *
 * This endpoint allows OAuth clients to discover server capabilities.
 */
 
import { Request, Response } from "express";
import { HOST, PORT } from "../../config";
 
/**
 * Get the base URL from the request
 *
 * Handles reverse proxy scenarios by checking X-Forwarded-* headers.
 *
 * @param req - Express request
 * @returns Base URL string (e.g., "https://example.com")
 */
export function getBaseUrl(req: Request): string {
  // Check for forwarded protocol (from reverse proxy)
  const forwardedProto = req.get("x-forwarded-proto");
  const protocol = forwardedProto ?? req.protocol ?? "http";
 
  // Check for forwarded host (from reverse proxy)
  const forwardedHost = req.get("x-forwarded-host");
  const host = forwardedHost ?? req.get("host") ?? `${HOST}:${PORT}`;
 
  return `${protocol}://${host}`;
}
 
/**
 * OAuth Authorization Server Metadata endpoint handler
 *
 * Returns metadata about the OAuth server's capabilities including:
 * - Issuer
 * - Authorization and token endpoints
 * - Supported response types, grant types, and PKCE methods
 *
 * @param req - Express request
 * @param res - Express response
 */
export function metadataHandler(req: Request, res: Response): void {
  const baseUrl = getBaseUrl(req);
 
  // OAuth 2.0 Authorization Server Metadata (RFC 8414)
  const metadata = {
    // REQUIRED: Issuer identifier (must be HTTPS in production)
    issuer: baseUrl,
 
    // REQUIRED: Authorization endpoint
    authorization_endpoint: `${baseUrl}/authorize`,
 
    // REQUIRED: Token endpoint
    token_endpoint: `${baseUrl}/token`,
 
    // OPTIONAL: Supported response types
    response_types_supported: ["code"],
 
    // OPTIONAL: Supported grant types
    grant_types_supported: ["authorization_code", "refresh_token"],
 
    // OPTIONAL: Supported PKCE code challenge methods (S256 required for OAuth 2.1)
    code_challenge_methods_supported: ["S256"],
 
    // OPTIONAL: Token endpoint authentication methods
    // "none" for public clients (device flow with non-confidential apps)
    token_endpoint_auth_methods_supported: ["none"],
 
    // OPTIONAL: Supported scopes
    scopes_supported: ["mcp:tools", "mcp:resources"],
 
    // REQUIRED for Claude.ai: Dynamic Client Registration endpoint (RFC 7591)
    registration_endpoint: `${baseUrl}/register`,
 
    // MCP-specific metadata
    mcp_version: "2025-03-26",
  };
 
  res.json(metadata);
}
 
/**
 * OAuth Protected Resource Metadata endpoint handler (RFC 9470)
 *
 * Returns metadata about this MCP server as a protected resource,
 * including a reference to the authorization server.
 *
 * @param req - Express request
 * @param res - Express response
 */
export function protectedResourceHandler(req: Request, res: Response): void {
  const baseUrl = getBaseUrl(req);
 
  // OAuth 2.0 Protected Resource Metadata (RFC 9470)
  const metadata = {
    // REQUIRED: Resource identifier
    resource: baseUrl,
 
    // REQUIRED: Authorization servers that can be used to access this resource
    authorization_servers: [baseUrl],
 
    // OPTIONAL: Scopes required for this resource
    scopes_supported: ["mcp:tools", "mcp:resources"],
 
    // OPTIONAL: Bearer token methods supported
    bearer_methods_supported: ["header"],
  };
 
  res.json(metadata);
}
 
/**
 * Health check endpoint handler
 *
 * Returns server health status for monitoring.
 *
 * @param req - Express request
 * @param res - Express response
 */
export function healthHandler(req: Request, res: Response): void {
  res.json({
    status: "ok",
    mode: "oauth",
    timestamp: new Date().toISOString(),
  });
}