All files / src/oauth/endpoints metadata.ts

81.25% Statements 13/16
83.33% Branches 5/6
66.66% Functions 2/3
81.25% Lines 13/16

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                    91x           91x                   91x   14x 14x     14x 14x   14x                           91x 9x     9x                                                                 9x                       91x                                                
/**
 * 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";
 
/**
 * MCP Protocol version supported by this server
 * @see https://modelcontextprotocol.io/spec
 */
export const MCP_PROTOCOL_VERSION = "2025-03-26";
 
/**
 * 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: MCP_PROTOCOL_VERSION,
  };
 
  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);
}
 
// NOTE: healthHandler was removed in favor of a simple /health endpoint in server.ts.
// MCP metadata (version, tools, auth mode, instances) is available via GET / (dashboard)
// with Accept: application/json header.