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 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | 229x 229x 229x 229x 229x 5x 5x 5x 5x 5x 229x 14x 14x 1x 1x 13x 13x 13x 229x 10x 10x 1x 1x 9x 9x 9x | /**
* 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';
import { loadOAuthConfig } from '../config';
import {
MCP_SCOPES,
TOKEN_ENDPOINT_AUTH_METHODS,
protectedResources,
resourceForMetadataPath,
} from '../resource';
/**
* 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 config = loadOAuthConfig();
if (!config) {
res.status(500).json({ error: 'server_error', error_description: 'OAuth not configured' });
return;
}
// The issuer is configuration, never request headers (RFC 8414 section 3.3: it must
// equal the issuer the client expects).
const baseUrl = config.issuer;
// 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 (PKCE),
// and the secret of a client registered with one, in the header or the form
token_endpoint_auth_methods_supported: TOKEN_ENDPOINT_AUTH_METHODS,
// OPTIONAL: Supported scopes
scopes_supported: MCP_SCOPES,
// Every authorization response carries `iss` (RFC 9207 section 3)
authorization_response_iss_parameter_supported: true,
// REQUIRED for Claude.ai: Dynamic Client Registration endpoint (RFC 7591)
registration_endpoint: `${baseUrl}/register`,
// Token revocation (RFC 7009), advertised per RFC 8414 section 2
revocation_endpoint: `${baseUrl}/revoke`,
revocation_endpoint_auth_methods_supported: TOKEN_ENDPOINT_AUTH_METHODS,
// 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 config = loadOAuthConfig();
if (!config) {
res.status(500).json({ error: 'server_error', error_description: 'OAuth not configured' });
return;
}
const [root, mcp] = protectedResources(config.issuer);
// OAuth 2.0 Protected Resource Metadata (RFC 9728)
const metadata = {
// REQUIRED: Resource identifier; RFC 9728 section 3.3 requires it to equal the
// identifier the client derived this document's URL from.
resource:
resourceForMetadataPath(config.issuer, req.path) ?? (req.path.endsWith('/mcp') ? mcp : root),
// REQUIRED: Authorization servers that can be used to access this resource
authorization_servers: [config.issuer],
// OPTIONAL: Scopes required for this resource
scopes_supported: MCP_SCOPES,
// 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.
|