/** * Scheme/transport safety for OAuth endpoints Paperclip learned from a remote * server (PAP-17099). * * A generic remote MCP connection can point at any endpoint, and that endpoint * gets to advertise its own authorization-server metadata. The * `authorization_endpoint` it names is not just fetched server-side — Paperclip * hands it to the operator's browser as a top-level navigation, so an arbitrary * string there is a code-execution and open-redirect primitive: * `javascript:` / `data:` would run in the board's origin, and plain `http:` * would carry the authorization request (and the operator) over a network any * intermediary can rewrite. * * So every endpoint URL discovered from metadata, pasted by an operator, taken * from a `WWW-Authenticate` hint, or shipped as a gallery default is parsed * here — once, in shared code — and must be: * * - a well-formed absolute URL (anything `new URL` rejects is rejected); * - `https:`, or `http:` only for a loopback host under the caller's explicit * local-development policy (the server passes its private-network policy, the * board passes "am I myself served over plaintext HTTP"); * - free of embedded credentials, because `https://evil.test@real.test/...` * reads as the wrong origin to a human and Paperclip must not help; * - free of a fragment, which never survives an authorization request usefully * and is a classic way to hide the effective target from a reader. * * Both the server and the board import this so a value that passes the API * boundary cannot fail the navigation boundary, or vice versa. */ export type OAuthEndpointKind = "authorization" | "token" | "registration" | "metadata"; export type OAuthEndpointUrlRejection = "missing" | "malformed" | "unsupported_scheme" | "insecure_transport" | "embedded_credentials" | "fragment"; export interface OAuthEndpointUrlOptions { /** * Allow `http:` for loopback hosts. Only true under an explicit * local-development policy — never for a public deployment. */ allowInsecureLoopback?: boolean; /** * Origins whose `http:` is already the operator's own trust boundary — * in practice just Paperclip's own deployment origin. A deployment served over * plaintext HTTP on a LAN address can still run its own authorization * endpoints (the smoke-lab fixture does), and opening Paperclip from Paperclip * adds no exposure the board does not already have. */ allowInsecureOrigins?: string[]; } export type OAuthEndpointUrlCheck = { ok: true; url: string; host: string; } | { ok: false; reason: OAuthEndpointUrlRejection; }; /** * Is `value` an OAuth endpoint Paperclip is willing to use — and, for the * authorization endpoint, to navigate a browser to? Returns the normalized URL * and its host on success, or the specific rejection reason so callers can * produce an actionable message. */ export declare function checkOAuthEndpointUrl(value: unknown, options?: OAuthEndpointUrlOptions): OAuthEndpointUrlCheck; export declare function isSafeOAuthEndpointUrl(value: unknown, options?: OAuthEndpointUrlOptions): boolean; /** * A UI-safe explanation for a rejected endpoint. Never echoes the URL, because * the string came from the remote server and may itself be the attack. */ export declare function oauthEndpointUrlRejectionMessage(kind: OAuthEndpointKind, reason: OAuthEndpointUrlRejection): string; /** * The host to show an operator before they are sent to an authorization page. * `null` when the value is not a usable URL — callers should be refusing to * navigate at that point anyway. */ export declare function oauthEndpointDisplayHost(value: unknown): string | null; //# sourceMappingURL=oauth-endpoint-url.d.ts.map