/** * MCP Authorization Endpoint (OAuth 2.1 + RFC 7636 PKCE + RFC 8707 audience) * * Handles GET /oauth/mcp/authorize. The MCP client navigates the user's * browser here with PKCE + resource params; this handler validates the * request, picks the configured upstream IdP, and 302-redirects the user * to the upstream provider's authorize URL. * * For CIMD-resolved clients the handler returns an interstitial HTML page * instead of immediately redirecting. The page shows the client_id host * (the authoritative CIMD identity), client_name, and redirect URI hostname * (with a loopback warning), and requires an explicit user confirmation via * POST /oauth/mcp/confirm. This satisfies the MCP auth spec requirement to * "clearly display the redirect URI hostname during authorization." * * The consent is bound to the user's browser via a nonce cookie whose hash * travels in the confirm token and upstream state; /confirm and the OAuth * callback both verify it (see consentBinding.ts). The page is served with * anti-framing headers — a framed consent page can be clickjacked. * * Two-phase validation per OAuth 2.1 §3.1.2.5: * - Pre-redirect: client_id + redirect_uri must match a registered * client. Failure → 400 JSON (can't safely redirect to unverified URI). * - Post-redirect: every other validation error → 302 to client's * verified redirect_uri with `?error=...&error_description=...`. */ import type { RequestTarget } from 'harper'; import type { Logger, MCPClientRecord, MCPConfig, ProviderRegistry, Request } from '../../types.ts'; type ErrorJSON = { status: 400 | 429 | 500; body: { error: string; error_description?: string; }; headers?: Record; }; type Redirect = { status: 302; headers: { Location: string; }; }; type HtmlResponse = { status: 200; headers: Record; body: string; }; /** * Resolve which upstream provider an MCP authorize request flows through. * v1 requires the resolved set to be exactly one entry (multi-provider * chooser UI is v1.1). */ export declare function selectMCPProvider(mcpConfig: MCPConfig, providers: ProviderRegistry): { providerName: string; } | { error: string; description: string; }; /** * Match a requested redirect_uri against a list of registered URIs. * * Exact string match in the general case, but RFC 8252 §7.3 requires * authorization servers to accept ANY port on loopback redirect URIs * (127.0.0.1 / [::1]) — native MCP clients like Claude Desktop and * mcp-remote bind to a dynamic port at runtime and can't pre-register * it. We treat `localhost` the same way; the RFC notes its use is "not * recommended" but is widespread in practice, and our DCR validation * already accepts it as a loopback equivalent (Stage 1). * * Two URIs match if they're identical, OR they both parse as URLs whose * host is in the loopback set AND scheme + pathname + search match. */ export declare function redirectUriMatches(requested: string, registered: string[]): boolean; /** * Escape HTML special characters. Used to safely embed client-supplied * strings (client_name, URIs) into the CIMD interstitial page. Every * client-controlled interpolation in `buildInterstitialPage` MUST go * through this function — client_name is attacker-controlled. */ export declare function escapeHtml(str: string): string; /** * Build the CIMD interstitial confirmation page. * * SECURITY: every client-supplied string is HTML-escaped via `escapeHtml` * before interpolation. client_name and URIs are attacker-controlled. */ export declare function buildInterstitialPage(client: MCPClientRecord, redirectUri: string, confirmToken: string, confirmPath: string): string; /** * Handle GET /oauth/mcp/authorize. * * Returns either a 302 redirect (success or post-validation error), * a 400 JSON response (pre-validation error), or a 200 HTML response * (CIMD interstitial page). */ export declare function handleAuthorize(request: Request, target: RequestTarget, mcpConfig: MCPConfig, providers: ProviderRegistry, logger?: Logger): Promise; /** * Handle POST /oauth/mcp/confirm. * * Validates the one-time confirm token minted by `handleAuthorize` for CIMD * clients, then performs the upstream IdP redirect. The token binds the full * set of authorize params — redirect_uri, code_challenge, resource, scope, * client state — so they cannot be swapped after the interstitial is shown. * * Invalid/expired/reused tokens return 400 JSON (pre-validation failure — * never redirect to an unverified redirect_uri on an invalid token). */ export declare function handleAuthorizeConfirm(request: Request, body: any, mcpConfig: MCPConfig, providers: ProviderRegistry, logger?: Logger): Promise; export {};