@mcp-z/client 2.2.4 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -1
- package/dist/cjs/auth/capability-discovery.d.cts +3 -1
- package/dist/cjs/auth/capability-discovery.d.ts +3 -1
- package/dist/cjs/auth/capability-discovery.js +46 -32
- package/dist/cjs/auth/capability-discovery.js.map +1 -1
- package/dist/cjs/auth/discovery-fetch.d.cts +2 -0
- package/dist/cjs/auth/discovery-fetch.d.ts +2 -0
- package/dist/cjs/auth/discovery-fetch.js +85 -31
- package/dist/cjs/auth/discovery-fetch.js.map +1 -1
- package/dist/cjs/auth/interactive-oauth-flow.d.cts +1 -1
- package/dist/cjs/auth/interactive-oauth-flow.d.ts +1 -1
- package/dist/cjs/auth/interactive-oauth-flow.js +13 -6
- package/dist/cjs/auth/interactive-oauth-flow.js.map +1 -1
- package/dist/cjs/auth/oauth-callback-listener.d.cts +1 -1
- package/dist/cjs/auth/oauth-callback-listener.d.ts +1 -1
- package/dist/cjs/auth/oauth-callback-listener.js +38 -2
- package/dist/cjs/auth/oauth-callback-listener.js.map +1 -1
- package/dist/cjs/auth/rfc9728-discovery.d.cts +7 -2
- package/dist/cjs/auth/rfc9728-discovery.d.ts +7 -2
- package/dist/cjs/auth/rfc9728-discovery.js +120 -88
- package/dist/cjs/auth/rfc9728-discovery.js.map +1 -1
- package/dist/cjs/auth/types.d.cts +4 -0
- package/dist/cjs/auth/types.d.ts +4 -0
- package/dist/cjs/auth/types.js.map +1 -1
- package/dist/cjs/connection/connect-client.d.cts +9 -1
- package/dist/cjs/connection/connect-client.d.ts +9 -1
- package/dist/cjs/connection/connect-client.js +282 -75
- package/dist/cjs/connection/connect-client.js.map +1 -1
- package/dist/cjs/connection/existing-process-transport.d.cts +18 -18
- package/dist/cjs/connection/existing-process-transport.d.ts +18 -18
- package/dist/cjs/connection/existing-process-transport.js +187 -82
- package/dist/cjs/connection/existing-process-transport.js.map +1 -1
- package/dist/cjs/connection/wait-for-http-ready.d.cts +1 -1
- package/dist/cjs/connection/wait-for-http-ready.d.ts +1 -1
- package/dist/cjs/connection/wait-for-http-ready.js +30 -5
- package/dist/cjs/connection/wait-for-http-ready.js.map +1 -1
- package/dist/cjs/dcr/dcr-authenticator.d.cts +1 -1
- package/dist/cjs/dcr/dcr-authenticator.d.ts +1 -1
- package/dist/cjs/dcr/dcr-authenticator.js +29 -18
- package/dist/cjs/dcr/dcr-authenticator.js.map +1 -1
- package/dist/cjs/dcr/dynamic-client-registrar.js +2 -1
- package/dist/cjs/dcr/dynamic-client-registrar.js.map +1 -1
- package/dist/cjs/index.d.cts +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/spawn/owned-process-tree.d.cts +12 -0
- package/dist/cjs/spawn/owned-process-tree.d.ts +12 -0
- package/dist/cjs/spawn/owned-process-tree.js +93 -0
- package/dist/cjs/spawn/owned-process-tree.js.map +1 -0
- package/dist/cjs/spawn/spawn-server.d.cts +8 -32
- package/dist/cjs/spawn/spawn-server.d.ts +8 -32
- package/dist/cjs/spawn/spawn-server.js +398 -212
- package/dist/cjs/spawn/spawn-server.js.map +1 -1
- package/dist/cjs/spawn/spawn-servers.d.cts +18 -8
- package/dist/cjs/spawn/spawn-servers.d.ts +18 -8
- package/dist/cjs/spawn/spawn-servers.js +342 -113
- package/dist/cjs/spawn/spawn-servers.js.map +1 -1
- package/dist/cjs/types.d.cts +1 -1
- package/dist/cjs/types.d.ts +1 -1
- package/dist/cjs/types.js.map +1 -1
- package/dist/esm/auth/capability-discovery.d.ts +3 -1
- package/dist/esm/auth/capability-discovery.js +21 -11
- package/dist/esm/auth/capability-discovery.js.map +1 -1
- package/dist/esm/auth/discovery-fetch.d.ts +2 -0
- package/dist/esm/auth/discovery-fetch.js +64 -23
- package/dist/esm/auth/discovery-fetch.js.map +1 -1
- package/dist/esm/auth/interactive-oauth-flow.d.ts +1 -1
- package/dist/esm/auth/interactive-oauth-flow.js +13 -6
- package/dist/esm/auth/interactive-oauth-flow.js.map +1 -1
- package/dist/esm/auth/oauth-callback-listener.d.ts +1 -1
- package/dist/esm/auth/oauth-callback-listener.js +32 -2
- package/dist/esm/auth/oauth-callback-listener.js.map +1 -1
- package/dist/esm/auth/rfc9728-discovery.d.ts +7 -2
- package/dist/esm/auth/rfc9728-discovery.js +40 -16
- package/dist/esm/auth/rfc9728-discovery.js.map +1 -1
- package/dist/esm/auth/types.d.ts +4 -0
- package/dist/esm/auth/types.js.map +1 -1
- package/dist/esm/connection/connect-client.d.ts +9 -1
- package/dist/esm/connection/connect-client.js +97 -35
- package/dist/esm/connection/connect-client.js.map +1 -1
- package/dist/esm/connection/existing-process-transport.d.ts +18 -18
- package/dist/esm/connection/existing-process-transport.js +132 -70
- package/dist/esm/connection/existing-process-transport.js.map +1 -1
- package/dist/esm/connection/wait-for-http-ready.d.ts +1 -1
- package/dist/esm/connection/wait-for-http-ready.js +19 -4
- package/dist/esm/connection/wait-for-http-ready.js.map +1 -1
- package/dist/esm/dcr/dcr-authenticator.d.ts +1 -1
- package/dist/esm/dcr/dcr-authenticator.js +26 -15
- package/dist/esm/dcr/dcr-authenticator.js.map +1 -1
- package/dist/esm/dcr/dynamic-client-registrar.js +2 -1
- package/dist/esm/dcr/dynamic-client-registrar.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/spawn/owned-process-tree.d.ts +12 -0
- package/dist/esm/spawn/owned-process-tree.js +39 -0
- package/dist/esm/spawn/owned-process-tree.js.map +1 -0
- package/dist/esm/spawn/spawn-server.d.ts +8 -32
- package/dist/esm/spawn/spawn-server.js +224 -130
- package/dist/esm/spawn/spawn-server.js.map +1 -1
- package/dist/esm/spawn/spawn-servers.d.ts +18 -8
- package/dist/esm/spawn/spawn-servers.js +180 -55
- package/dist/esm/spawn/spawn-servers.js.map +1 -1
- package/dist/esm/types.d.ts +1 -1
- package/dist/esm/types.js.map +1 -1
- package/package.json +8 -5
- package/schemas/servers.d.ts +24 -0
- package/schemas/servers.schema.json +31 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/auth/discovery-fetch.ts"],"sourcesContent":["/**\n * Hardened fetch for OAuth discovery URLs, which the remote MCP server\n * controls (SSRF mitigation). See ARCHITECTURE.md's \"SSRF mitigation\" section for the threat model.\n */\nimport dns from 'node:dns';\nimport http from 'node:http';\nimport https from 'node:https';\nimport { isIP, type LookupFunction } from 'node:net';\nimport ipaddr from 'ipaddr.js';\n\nexport class DiscoveryFetchError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'DiscoveryFetchError';\n }\n}\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst MAX_REDIRECTS = 5;\nconst MAX_BODY_BYTES = 1_000_000; // 1 MB - discovery documents are small JSON\n\ntype LookupRecord = { address: string; family: number };\n/** DNS implementation, injectable for deterministic tests. Same contract as `dns.lookup` with `{ all: true, verbatim: true }`. */\ntype Lookup = (hostname: string, options: { all: true; verbatim: true }, callback: (error: NodeJS.ErrnoException | null, addresses: LookupRecord[]) => void) => void;\n\nfunction stripBrackets(hostname: string): string {\n return hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname;\n}\n\n/** True for the literal hostname `localhost` or an IP literal in the loopback range. */\nfunction isLoopbackHost(hostname: string): boolean {\n const host = stripBrackets(hostname).toLowerCase();\n if (host === 'localhost') return true;\n if (isIP(host)) {\n try {\n return ipaddr.process(host).range() === 'loopback';\n } catch {\n return false;\n }\n }\n return false;\n}\n\n/**\n * True if `rawUrl`'s host is loopback, used to decide `allowLoopback` grants\n * from the URL the caller configured, not remote-supplied data. Fails closed on an unparseable URL.\n */\nexport function isLoopbackUrl(rawUrl: string): boolean {\n try {\n return isLoopbackHost(new URL(rawUrl).hostname);\n } catch {\n return false;\n }\n}\n\n/** True when the address is anything other than ordinary public unicast space. */\nfunction isBlockedAddress(address: string): boolean {\n try {\n // ipaddr.process() normalizes IPv4-mapped IPv6 (::ffff:a.b.c.d) to IPv4 first.\n return ipaddr.process(address).range() !== 'unicast';\n } catch {\n return true; // unparseable - fail closed\n }\n}\n\nfunction isLoopbackAddress(address: string): boolean {\n try {\n return ipaddr.process(address).range() === 'loopback';\n } catch {\n return false;\n }\n}\n\n/**\n * Validates scheme and, for literal-IP hosts, address range. `allowLoopback`\n * reflects trust in the calling server, never in `rawUrl` itself.\n */\nfunction assertSafeUrl(rawUrl: string, context: string, allowLoopback: boolean): URL {\n let url: URL;\n try {\n url = new URL(rawUrl);\n } catch {\n throw new DiscoveryFetchError(`Refusing to fetch ${context}: invalid URL`);\n }\n\n if (url.protocol !== 'https:' && url.protocol !== 'http:') {\n throw new DiscoveryFetchError(`Refusing to fetch ${context}: unsupported scheme`);\n }\n\n const host = stripBrackets(url.hostname);\n const trustedLoopback = allowLoopback && isLoopbackHost(host);\n if (trustedLoopback) return url; // this call's own grant covers it, regardless of scheme\n\n if (url.protocol === 'http:') {\n throw new DiscoveryFetchError(`Refusing to fetch ${context}: http:// is only allowed for a trusted loopback origin`);\n }\n\n if (isIP(host) && isBlockedAddress(host)) {\n throw new DiscoveryFetchError(`Refusing to fetch ${context}: address is not publicly routable`);\n }\n\n return url;\n}\n\nfunction lookupAll(hostname: string, lookup: Lookup, timeoutMs: number): Promise<LookupRecord[]> {\n return new Promise((resolve, reject) => {\n let settled = false;\n const deadline = setTimeout(() => {\n if (!settled) {\n settled = true;\n reject(new Error('DNS resolution timed out'));\n }\n }, timeoutMs);\n try {\n lookup(hostname, { all: true, verbatim: true }, (error, addresses) => {\n if (settled) return;\n settled = true;\n clearTimeout(deadline);\n if (error || addresses.length === 0) reject(new Error('DNS resolution failed'));\n else resolve(addresses);\n });\n } catch {\n if (!settled) {\n settled = true;\n clearTimeout(deadline);\n reject(new Error('DNS resolution failed'));\n }\n }\n });\n}\n\n/**\n * Resolves the hostname once, rejects it unless every returned address is\n * acceptable, and returns the validated set. The set is pinned into the\n * request itself (see `requestOnce`), which is what closes the DNS-rebinding\n * TOCTOU: the transport can only dial addresses that were already validated,\n * and it never resolves the hostname a second time.\n */\nasync function resolveSafeAddresses(url: URL, context: string, allowLoopback: boolean, lookup: Lookup, timeoutMs: number): Promise<LookupRecord[]> {\n const host = stripBrackets(url.hostname);\n if (isIP(host)) return [{ address: host, family: host.includes(':') ? 6 : 4 }]; // literal IP already fully checked in assertSafeUrl\n\n // Literal `localhost` under a trusted-loopback grant: still resolved, and the\n // answers must be loopback - the grant covers loopback, not \"wherever\n // localhost happens to point\".\n const loopbackGrant = allowLoopback && isLoopbackHost(host);\n\n let addresses: LookupRecord[];\n try {\n addresses = await lookupAll(host, lookup, timeoutMs);\n } catch {\n throw new DiscoveryFetchError(`Refusing to fetch ${context}: host could not be resolved`);\n }\n\n if (loopbackGrant) {\n if (!addresses.every((record) => isLoopbackAddress(record.address))) {\n throw new DiscoveryFetchError(`Refusing to fetch ${context}: host does not resolve to a loopback address`);\n }\n } else if (addresses.some((record) => isBlockedAddress(record.address))) {\n throw new DiscoveryFetchError(`Refusing to fetch ${context}: host resolves to an address that is not publicly routable`);\n }\n\n return addresses;\n}\n\n/**\n * A `net` lookup that can only ever answer with `addresses` - the set already\n * validated for this URL. The request is built against the original URL (so\n * SNI and certificate verification still see the hostname), but the\n * connection can only land on a validated address.\n */\nfunction pinnedLookup(addresses: LookupRecord[]): LookupFunction {\n return (_hostname, options, callback) => {\n if (typeof options === 'object' && options.all === true) {\n callback(null, addresses);\n return;\n }\n const first = addresses[0];\n if (!first) {\n callback(Object.assign(new Error('no validated address'), { code: 'ENOTFOUND' }) as NodeJS.ErrnoException, '', 0);\n return;\n }\n callback(null, first.address, first.family);\n };\n}\n\nfunction toRequestHeaders(init: RequestInit): Record<string, string> {\n const headers = new Headers(init.headers);\n const out: Record<string, string> = {};\n for (const [name, value] of headers) out[name] = value;\n return out;\n}\n\nasync function toRequestBody(body: RequestInit['body']): Promise<Buffer | undefined> {\n if (body === undefined || body === null) return undefined;\n if (typeof body === 'string') return Buffer.from(body);\n if (body instanceof URLSearchParams) return Buffer.from(body.toString());\n if (body instanceof Blob) return Buffer.from(await body.arrayBuffer());\n if (body instanceof ArrayBuffer) return Buffer.from(body);\n if (ArrayBuffer.isView(body)) return Buffer.from(body.buffer, body.byteOffset, body.byteLength);\n if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) {\n const reader = body.getReader();\n const chunks: Uint8Array[] = [];\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value) chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n return Buffer.concat(chunks.map((chunk) => Buffer.from(chunk)));\n }\n throw new Error('unsupported request body');\n}\n\nfunction toResponse(status: number, statusText: string, headers: http.IncomingHttpHeaders, body: Buffer): Response {\n const responseHeaders = new Headers();\n for (const [name, value] of Object.entries(headers)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n for (const entry of value) responseHeaders.append(name, entry);\n } else {\n responseHeaders.append(name, value);\n }\n }\n // The fetch spec gives these statuses a null body, and the Response\n // constructor rejects a body for them.\n const nullBody = status === 204 || status === 205 || status === 304;\n return new Response(nullBody ? null : body, { status, statusText, headers: responseHeaders });\n}\n\nasync function requestOnce(url: URL, init: RequestInit, context: string, addresses: LookupRecord[], timeoutMs: number): Promise<Response> {\n let body: Buffer | undefined;\n try {\n body = await toRequestBody(init.body);\n } catch {\n throw new DiscoveryFetchError(`Failed to fetch ${context}`);\n }\n\n const transport = url.protocol === 'https:' ? https : http;\n const options: http.RequestOptions = { method: (init.method ?? 'GET').toUpperCase(), headers: toRequestHeaders(init), agent: false, lookup: pinnedLookup(addresses) };\n\n return new Promise<Response>((resolve, reject) => {\n let settled = false;\n let req: http.ClientRequest | undefined;\n let res: http.IncomingMessage | undefined;\n const fail = (message: string): void => {\n if (!settled) {\n settled = true;\n clearTimeout(deadline);\n reject(new DiscoveryFetchError(message));\n }\n };\n const deadline = setTimeout(() => {\n res?.destroy();\n req?.destroy();\n fail(`Failed to fetch ${context}`);\n }, timeoutMs);\n\n try {\n req = transport.request(url, options, (response) => {\n res = response;\n const status = response.statusCode;\n const statusText = response.statusMessage ?? '';\n if (status === undefined || status < 200 || status > 599) {\n response.destroy();\n fail(`Failed to fetch ${context}`);\n return;\n }\n const chunks: Buffer[] = [];\n let total = 0;\n response.on('data', (chunk: Buffer) => {\n total += chunk.length;\n if (total > MAX_BODY_BYTES) {\n response.destroy();\n fail(`Refusing to read ${context}: response too large`);\n return;\n }\n chunks.push(chunk);\n });\n response.once('error', () => fail(`Failed to fetch ${context}`));\n response.once('end', () => {\n if (settled) return;\n settled = true;\n clearTimeout(deadline);\n resolve(toResponse(status, statusText, response.headers, Buffer.concat(chunks)));\n });\n });\n } catch {\n fail(`Failed to fetch ${context}`);\n return;\n }\n\n req.once('error', () => fail(`Failed to fetch ${context}`));\n req.end(body);\n });\n}\n\nasync function fetchOnce(url: URL, init: RequestInit, context: string, allowLoopback: boolean, timeoutMs: number, lookup: Lookup): Promise<Response> {\n const addresses = await resolveSafeAddresses(url, context, allowLoopback, lookup, timeoutMs);\n return requestOnce(url, init, context, addresses, timeoutMs);\n}\n\nexport interface DiscoveryFetchOptions {\n /**\n * Loopback trust grant for this fetch, computed from the server the caller\n * is actually talking to - never from the URL being fetched. Defaults to `false`.\n */\n allowLoopback?: boolean;\n /**\n * Per-hop timeout in ms, applied to both the DNS resolution and the request.\n * Defaults to `DEFAULT_TIMEOUT_MS`; overridable so tests can bound slow-failure cases.\n */\n timeoutMs?: number;\n /**\n * DNS implementation used for the pre-request resolution, injectable for\n * deterministic tests. Defaults to `dns.lookup`.\n */\n lookup?: Lookup;\n}\n\n/**\n * Fetches an OAuth-discovery URL with SSRF mitigations applied to the\n * initial URL and every redirect hop; redirects are validated, not auto-followed.\n *\n * The hostname is resolved once, every returned address is validated, and the\n * validated set is pinned into the request through a custom `lookup`, so the\n * request cannot resolve the hostname a second time and cannot be steered to\n * a different address by a DNS-rebinding answer.\n *\n * @param rawUrl - URL to fetch; may be remote-server-supplied (the threat this guards against).\n * @param init - Standard fetch options; `redirect` is always forced to `'manual'`.\n * @param context - Short label used only in error messages, never echoing the URL.\n * @param options - See `DiscoveryFetchOptions`.\n */\nexport async function discoveryFetch(rawUrl: string, init: RequestInit = {}, context = 'discovery URL', options: DiscoveryFetchOptions = {}): Promise<Response> {\n const { allowLoopback = false, timeoutMs = DEFAULT_TIMEOUT_MS, lookup = dns.lookup as unknown as Lookup } = options;\n\n let url = assertSafeUrl(rawUrl, context, allowLoopback);\n let response = await fetchOnce(url, init, context, allowLoopback, timeoutMs, lookup);\n\n let redirectsLeft = MAX_REDIRECTS;\n while (response.status >= 300 && response.status < 400) {\n const location = response.headers.get('location');\n if (!location) break;\n if (redirectsLeft <= 0) {\n throw new DiscoveryFetchError(`Refusing to fetch ${context}: too many redirects`);\n }\n redirectsLeft -= 1;\n\n let nextUrl: URL;\n try {\n nextUrl = new URL(location, url);\n } catch {\n throw new DiscoveryFetchError(`Refusing to fetch ${context}: invalid redirect target`);\n }\n url = assertSafeUrl(nextUrl.toString(), context, allowLoopback);\n response = await fetchOnce(url, init, context, allowLoopback, timeoutMs, lookup);\n }\n\n return response;\n}\n\n/**\n * Parse a `discoveryFetch` response body as JSON, enforcing `MAX_BODY_BYTES`\n * while reading (not after buffering the whole thing).\n */\nexport async function readDiscoveryJson<T>(response: Response, context: string): Promise<T> {\n const text = await readLimited(response, context, MAX_BODY_BYTES);\n try {\n return JSON.parse(text) as T;\n } catch {\n throw new DiscoveryFetchError(`Refusing to parse ${context}: invalid JSON`);\n }\n}\n\nasync function readLimited(response: Response, context: string, maxBytes: number): Promise<string> {\n const contentLength = response.headers.get('content-length');\n if (contentLength && Number(contentLength) > maxBytes) {\n throw new DiscoveryFetchError(`Refusing to read ${context}: response too large`);\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n const text = await response.text();\n if (Buffer.byteLength(text, 'utf8') > maxBytes) {\n throw new DiscoveryFetchError(`Refusing to read ${context}: response too large`);\n }\n return text;\n }\n\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (!value) continue;\n total += value.byteLength;\n if (total > maxBytes) {\n throw new DiscoveryFetchError(`Refusing to read ${context}: response too large`);\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n\n return Buffer.concat(chunks.map((chunk) => Buffer.from(chunk))).toString('utf8');\n}\n"],"names":["DiscoveryFetchError","discoveryFetch","isLoopbackUrl","readDiscoveryJson","message","name","Error","DEFAULT_TIMEOUT_MS","MAX_REDIRECTS","MAX_BODY_BYTES","stripBrackets","hostname","startsWith","endsWith","slice","isLoopbackHost","host","toLowerCase","isIP","ipaddr","process","range","rawUrl","URL","isBlockedAddress","address","isLoopbackAddress","assertSafeUrl","context","allowLoopback","url","protocol","trustedLoopback","lookupAll","lookup","timeoutMs","Promise","resolve","reject","settled","deadline","setTimeout","all","verbatim","error","addresses","clearTimeout","length","resolveSafeAddresses","loopbackGrant","family","includes","every","record","some","pinnedLookup","_hostname","options","callback","first","Object","assign","code","toRequestHeaders","init","headers","Headers","out","value","toRequestBody","body","reader","chunks","done","undefined","Buffer","from","URLSearchParams","toString","Blob","arrayBuffer","ArrayBuffer","isView","buffer","byteOffset","byteLength","ReadableStream","getReader","read","push","releaseLock","concat","map","chunk","toResponse","status","statusText","responseHeaders","entries","Array","isArray","entry","append","nullBody","Response","requestOnce","transport","https","http","method","toUpperCase","agent","req","res","fail","destroy","request","response","statusCode","statusMessage","total","on","once","end","fetchOnce","redirectsLeft","location","nextUrl","dns","get","text","readLimited","JSON","parse","maxBytes","contentLength","Number"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/auth/discovery-fetch.ts"],"sourcesContent":["/**\n * Hardened fetch for OAuth discovery URLs, which the remote MCP server\n * controls (SSRF mitigation). See ARCHITECTURE.md's \"SSRF mitigation\" section for the threat model.\n */\nimport dns from 'node:dns';\nimport http from 'node:http';\nimport https from 'node:https';\nimport { isIP, type LookupFunction } from 'node:net';\nimport ipaddr from 'ipaddr.js';\n\nexport class DiscoveryFetchError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'DiscoveryFetchError';\n }\n}\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst MAX_REDIRECTS = 5;\nconst MAX_BODY_BYTES = 1_000_000; // 1 MB - discovery documents are small JSON\n\n/** Preserve cancellation while discovery falls back across endpoints. */\nexport function throwIfAborted(signal?: AbortSignal | null): void {\n signal?.throwIfAborted();\n}\n\ntype LookupRecord = { address: string; family: number };\n/** DNS implementation, injectable for deterministic tests. Same contract as `dns.lookup` with `{ all: true, verbatim: true }`. */\ntype Lookup = (hostname: string, options: { all: true; verbatim: true }, callback: (error: NodeJS.ErrnoException | null, addresses: LookupRecord[]) => void) => void;\n\nfunction stripBrackets(hostname: string): string {\n return hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname;\n}\n\n/** True for the literal hostname `localhost` or an IP literal in the loopback range. */\nfunction isLoopbackHost(hostname: string): boolean {\n const host = stripBrackets(hostname).toLowerCase();\n if (host === 'localhost') return true;\n if (isIP(host)) {\n try {\n return ipaddr.process(host).range() === 'loopback';\n } catch {\n return false;\n }\n }\n return false;\n}\n\n/**\n * True if `rawUrl`'s host is loopback, used to decide `allowLoopback` grants\n * from the URL the caller configured, not remote-supplied data. Fails closed on an unparseable URL.\n */\nexport function isLoopbackUrl(rawUrl: string): boolean {\n try {\n return isLoopbackHost(new URL(rawUrl).hostname);\n } catch {\n return false;\n }\n}\n\n/** True when the address is anything other than ordinary public unicast space. */\nfunction isBlockedAddress(address: string): boolean {\n try {\n // ipaddr.process() normalizes IPv4-mapped IPv6 (::ffff:a.b.c.d) to IPv4 first.\n return ipaddr.process(address).range() !== 'unicast';\n } catch {\n return true; // unparseable - fail closed\n }\n}\n\nfunction isLoopbackAddress(address: string): boolean {\n try {\n return ipaddr.process(address).range() === 'loopback';\n } catch {\n return false;\n }\n}\n\n/**\n * Validates scheme and, for literal-IP hosts, address range. `allowLoopback`\n * reflects trust in the calling server, never in `rawUrl` itself.\n */\nfunction assertSafeUrl(rawUrl: string, context: string, allowLoopback: boolean): URL {\n let url: URL;\n try {\n url = new URL(rawUrl);\n } catch {\n throw new DiscoveryFetchError(`Refusing to fetch ${context}: invalid URL`);\n }\n\n if (url.protocol !== 'https:' && url.protocol !== 'http:') {\n throw new DiscoveryFetchError(`Refusing to fetch ${context}: unsupported scheme`);\n }\n\n const host = stripBrackets(url.hostname);\n const trustedLoopback = allowLoopback && isLoopbackHost(host);\n if (trustedLoopback) return url; // this call's own grant covers it, regardless of scheme\n\n if (url.protocol === 'http:') {\n throw new DiscoveryFetchError(`Refusing to fetch ${context}: http:// is only allowed for a trusted loopback origin`);\n }\n\n if (isIP(host) && isBlockedAddress(host)) {\n throw new DiscoveryFetchError(`Refusing to fetch ${context}: address is not publicly routable`);\n }\n\n return url;\n}\n\nfunction lookupAll(hostname: string, lookup: Lookup, timeoutMs: number, signal?: AbortSignal | null): Promise<LookupRecord[]> {\n return new Promise((resolve, reject) => {\n let settled = false;\n const finish = (complete: () => void): void => {\n if (settled) return;\n settled = true;\n clearTimeout(deadline);\n signal?.removeEventListener('abort', onAbort);\n complete();\n };\n const onAbort = (): void => finish(() => reject(signal?.reason));\n const deadline = setTimeout(() => {\n finish(() => reject(new Error('DNS resolution timed out')));\n }, timeoutMs);\n signal?.addEventListener('abort', onAbort, { once: true });\n if (signal?.aborted) {\n onAbort();\n return;\n }\n try {\n lookup(hostname, { all: true, verbatim: true }, (error, addresses) => {\n finish(() => {\n if (error || addresses.length === 0) reject(new Error('DNS resolution failed'));\n else resolve(addresses);\n });\n });\n } catch {\n finish(() => reject(new Error('DNS resolution failed')));\n }\n });\n}\n\n/**\n * Resolves the hostname once, rejects it unless every returned address is\n * acceptable, and returns the validated set. The set is pinned into the\n * request itself (see `requestOnce`), which is what closes the DNS-rebinding\n * TOCTOU: the transport can only dial addresses that were already validated,\n * and it never resolves the hostname a second time.\n */\nasync function resolveSafeAddresses(url: URL, context: string, allowLoopback: boolean, lookup: Lookup, timeoutMs: number, signal?: AbortSignal | null): Promise<LookupRecord[]> {\n throwIfAborted(signal);\n const host = stripBrackets(url.hostname);\n if (isIP(host)) return [{ address: host, family: host.includes(':') ? 6 : 4 }]; // literal IP already fully checked in assertSafeUrl\n\n // Literal `localhost` under a trusted-loopback grant: still resolved, and the\n // answers must be loopback - the grant covers loopback, not \"wherever\n // localhost happens to point\".\n const loopbackGrant = allowLoopback && isLoopbackHost(host);\n\n let addresses: LookupRecord[];\n try {\n addresses = await lookupAll(host, lookup, timeoutMs, signal);\n } catch (_error) {\n throwIfAborted(signal);\n throw new DiscoveryFetchError(`Refusing to fetch ${context}: host could not be resolved`);\n }\n\n if (loopbackGrant) {\n if (!addresses.every((record) => isLoopbackAddress(record.address))) {\n throw new DiscoveryFetchError(`Refusing to fetch ${context}: host does not resolve to a loopback address`);\n }\n } else if (addresses.some((record) => isBlockedAddress(record.address))) {\n throw new DiscoveryFetchError(`Refusing to fetch ${context}: host resolves to an address that is not publicly routable`);\n }\n\n return addresses;\n}\n\n/**\n * A `net` lookup that can only ever answer with `addresses` - the set already\n * validated for this URL. The request is built against the original URL (so\n * SNI and certificate verification still see the hostname), but the\n * connection can only land on a validated address.\n */\nfunction pinnedLookup(addresses: LookupRecord[]): LookupFunction {\n return (_hostname, options, callback) => {\n if (typeof options === 'object' && options.all === true) {\n callback(null, addresses);\n return;\n }\n const first = addresses[0];\n if (!first) {\n callback(Object.assign(new Error('no validated address'), { code: 'ENOTFOUND' }) as NodeJS.ErrnoException, '', 0);\n return;\n }\n callback(null, first.address, first.family);\n };\n}\n\nfunction toRequestHeaders(init: RequestInit): Record<string, string> {\n const headers = new Headers(init.headers);\n const out: Record<string, string> = {};\n for (const [name, value] of headers) out[name] = value;\n return out;\n}\n\nasync function toRequestBody(body: RequestInit['body']): Promise<Buffer | undefined> {\n if (body === undefined || body === null) return undefined;\n if (typeof body === 'string') return Buffer.from(body);\n if (body instanceof URLSearchParams) return Buffer.from(body.toString());\n if (body instanceof Blob) return Buffer.from(await body.arrayBuffer());\n if (body instanceof ArrayBuffer) return Buffer.from(body);\n if (ArrayBuffer.isView(body)) return Buffer.from(body.buffer, body.byteOffset, body.byteLength);\n if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) {\n const reader = body.getReader();\n const chunks: Uint8Array[] = [];\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value) chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n return Buffer.concat(chunks.map((chunk) => Buffer.from(chunk)));\n }\n throw new Error('unsupported request body');\n}\n\nfunction toResponse(status: number, statusText: string, headers: http.IncomingHttpHeaders, body: Buffer): Response {\n const responseHeaders = new Headers();\n for (const [name, value] of Object.entries(headers)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n for (const entry of value) responseHeaders.append(name, entry);\n } else {\n responseHeaders.append(name, value);\n }\n }\n // The fetch spec gives these statuses a null body, and the Response\n // constructor rejects a body for them.\n const nullBody = status === 204 || status === 205 || status === 304;\n return new Response(nullBody ? null : body, { status, statusText, headers: responseHeaders });\n}\n\nasync function requestOnce(url: URL, init: RequestInit, context: string, addresses: LookupRecord[], timeoutMs: number): Promise<Response> {\n throwIfAborted(init.signal);\n let body: Buffer | undefined;\n try {\n body = await toRequestBody(init.body);\n } catch {\n throw new DiscoveryFetchError(`Failed to fetch ${context}`);\n }\n\n const transport = url.protocol === 'https:' ? https : http;\n const options: http.RequestOptions = { method: (init.method ?? 'GET').toUpperCase(), headers: toRequestHeaders(init), agent: false, lookup: pinnedLookup(addresses), signal: init.signal ?? undefined };\n\n return new Promise<Response>((resolve, reject) => {\n let settled = false;\n let req: http.ClientRequest | undefined;\n let res: http.IncomingMessage | undefined;\n const signal = init.signal ?? undefined;\n const cleanup = (): void => {\n clearTimeout(deadline);\n signal?.removeEventListener('abort', onAbort);\n };\n const fail = (message: string): void => {\n if (!settled) {\n settled = true;\n cleanup();\n reject(new DiscoveryFetchError(message));\n }\n };\n const failWith = (error: unknown): void => {\n if (!settled) {\n settled = true;\n cleanup();\n reject(error);\n }\n };\n const deadline = setTimeout(() => {\n res?.destroy();\n req?.destroy();\n fail(`Failed to fetch ${context}`);\n }, timeoutMs);\n const onAbort = (): void => {\n res?.destroy();\n req?.destroy();\n failWith(signal?.reason);\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n if (signal?.aborted) {\n onAbort();\n return;\n }\n\n try {\n req = transport.request(url, options, (response) => {\n res = response;\n const status = response.statusCode;\n const statusText = response.statusMessage ?? '';\n if (status === undefined || status < 200 || status > 599) {\n response.destroy();\n fail(`Failed to fetch ${context}`);\n return;\n }\n const chunks: Buffer[] = [];\n let total = 0;\n response.on('data', (chunk: Buffer) => {\n total += chunk.length;\n if (total > MAX_BODY_BYTES) {\n response.destroy();\n fail(`Refusing to read ${context}: response too large`);\n return;\n }\n chunks.push(chunk);\n });\n response.once('error', () => fail(`Failed to fetch ${context}`));\n response.once('end', () => {\n if (settled) return;\n settled = true;\n cleanup();\n resolve(toResponse(status, statusText, response.headers, Buffer.concat(chunks)));\n });\n });\n } catch {\n fail(`Failed to fetch ${context}`);\n return;\n }\n\n req.once('error', () => fail(`Failed to fetch ${context}`));\n req.end(body);\n });\n}\n\nasync function fetchOnce(url: URL, init: RequestInit, context: string, allowLoopback: boolean, timeoutMs: number, lookup: Lookup): Promise<Response> {\n throwIfAborted(init.signal);\n const addresses = await resolveSafeAddresses(url, context, allowLoopback, lookup, timeoutMs, init.signal);\n return requestOnce(url, init, context, addresses, timeoutMs);\n}\n\nexport interface DiscoveryFetchOptions {\n /**\n * Loopback trust grant for this fetch, computed from the server the caller\n * is actually talking to - never from the URL being fetched. Defaults to `false`.\n */\n allowLoopback?: boolean;\n /**\n * Per-hop timeout in ms, applied to both the DNS resolution and the request.\n * Defaults to `DEFAULT_TIMEOUT_MS`; overridable so tests can bound slow-failure cases.\n */\n timeoutMs?: number;\n /**\n * DNS implementation used for the pre-request resolution, injectable for\n * deterministic tests. Defaults to `dns.lookup`.\n */\n lookup?: Lookup;\n}\n\n/**\n * Fetches an OAuth-discovery URL with SSRF mitigations applied to the\n * initial URL and every redirect hop; redirects are validated, not auto-followed.\n *\n * The hostname is resolved once, every returned address is validated, and the\n * validated set is pinned into the request through a custom `lookup`, so the\n * request cannot resolve the hostname a second time and cannot be steered to\n * a different address by a DNS-rebinding answer.\n *\n * @param rawUrl - URL to fetch; may be remote-server-supplied (the threat this guards against).\n * @param init - Standard fetch options; `redirect` is always forced to `'manual'`.\n * @param context - Short label used only in error messages, never echoing the URL.\n * @param options - See `DiscoveryFetchOptions`.\n */\nexport async function discoveryFetch(rawUrl: string, init: RequestInit = {}, context = 'discovery URL', options: DiscoveryFetchOptions = {}): Promise<Response> {\n throwIfAborted(init.signal);\n const { allowLoopback = false, timeoutMs = DEFAULT_TIMEOUT_MS, lookup = dns.lookup as unknown as Lookup } = options;\n\n let url = assertSafeUrl(rawUrl, context, allowLoopback);\n let response = await fetchOnce(url, init, context, allowLoopback, timeoutMs, lookup);\n\n let redirectsLeft = MAX_REDIRECTS;\n while (response.status >= 300 && response.status < 400) {\n const location = response.headers.get('location');\n if (!location) break;\n if (redirectsLeft <= 0) {\n throw new DiscoveryFetchError(`Refusing to fetch ${context}: too many redirects`);\n }\n redirectsLeft -= 1;\n\n let nextUrl: URL;\n try {\n nextUrl = new URL(location, url);\n } catch {\n throw new DiscoveryFetchError(`Refusing to fetch ${context}: invalid redirect target`);\n }\n url = assertSafeUrl(nextUrl.toString(), context, allowLoopback);\n response = await fetchOnce(url, init, context, allowLoopback, timeoutMs, lookup);\n }\n\n return response;\n}\n\n/**\n * Parse a `discoveryFetch` response body as JSON, enforcing `MAX_BODY_BYTES`\n * while reading (not after buffering the whole thing).\n */\nexport async function readDiscoveryJson<T>(response: Response, context: string): Promise<T> {\n const text = await readLimited(response, context, MAX_BODY_BYTES);\n try {\n return JSON.parse(text) as T;\n } catch {\n throw new DiscoveryFetchError(`Refusing to parse ${context}: invalid JSON`);\n }\n}\n\nasync function readLimited(response: Response, context: string, maxBytes: number): Promise<string> {\n const contentLength = response.headers.get('content-length');\n if (contentLength && Number(contentLength) > maxBytes) {\n throw new DiscoveryFetchError(`Refusing to read ${context}: response too large`);\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n const text = await response.text();\n if (Buffer.byteLength(text, 'utf8') > maxBytes) {\n throw new DiscoveryFetchError(`Refusing to read ${context}: response too large`);\n }\n return text;\n }\n\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n if (!value) continue;\n total += value.byteLength;\n if (total > maxBytes) {\n throw new DiscoveryFetchError(`Refusing to read ${context}: response too large`);\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n\n return Buffer.concat(chunks.map((chunk) => Buffer.from(chunk))).toString('utf8');\n}\n"],"names":["DiscoveryFetchError","discoveryFetch","isLoopbackUrl","readDiscoveryJson","throwIfAborted","message","name","Error","DEFAULT_TIMEOUT_MS","MAX_REDIRECTS","MAX_BODY_BYTES","signal","stripBrackets","hostname","startsWith","endsWith","slice","isLoopbackHost","host","toLowerCase","isIP","ipaddr","process","range","rawUrl","URL","isBlockedAddress","address","isLoopbackAddress","assertSafeUrl","context","allowLoopback","url","protocol","trustedLoopback","lookupAll","lookup","timeoutMs","Promise","resolve","reject","settled","finish","complete","clearTimeout","deadline","removeEventListener","onAbort","reason","setTimeout","addEventListener","once","aborted","all","verbatim","error","addresses","length","resolveSafeAddresses","loopbackGrant","_error","family","includes","every","record","some","pinnedLookup","_hostname","options","callback","first","Object","assign","code","toRequestHeaders","init","headers","Headers","out","value","toRequestBody","body","reader","chunks","done","undefined","Buffer","from","URLSearchParams","toString","Blob","arrayBuffer","ArrayBuffer","isView","buffer","byteOffset","byteLength","ReadableStream","getReader","read","push","releaseLock","concat","map","chunk","toResponse","status","statusText","responseHeaders","entries","Array","isArray","entry","append","nullBody","Response","requestOnce","transport","https","http","method","toUpperCase","agent","req","res","cleanup","fail","failWith","destroy","request","response","statusCode","statusMessage","total","on","end","fetchOnce","redirectsLeft","location","nextUrl","dns","get","text","readLimited","JSON","parse","maxBytes","contentLength","Number"],"mappings":"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"}
|
|
@@ -56,7 +56,7 @@ export declare class InteractiveOAuthFlow {
|
|
|
56
56
|
* @returns New token set with a refreshed access token.
|
|
57
57
|
* @throws Error if refresh fails.
|
|
58
58
|
*/
|
|
59
|
-
refreshTokens(tokenEndpoint: string, refreshToken: string, clientId: string, clientSecret: string, resource: string, allowLoopback?: boolean): Promise<TokenSet>;
|
|
59
|
+
refreshTokens(tokenEndpoint: string, refreshToken: string, clientId: string, clientSecret: string, resource: string, allowLoopback?: boolean, signal?: AbortSignal): Promise<TokenSet>;
|
|
60
60
|
/**
|
|
61
61
|
* Open browser to authorization URL
|
|
62
62
|
* Uses platform-specific command to open default browser
|
|
@@ -56,7 +56,7 @@ export declare class InteractiveOAuthFlow {
|
|
|
56
56
|
* @returns New token set with a refreshed access token.
|
|
57
57
|
* @throws Error if refresh fails.
|
|
58
58
|
*/
|
|
59
|
-
refreshTokens(tokenEndpoint: string, refreshToken: string, clientId: string, clientSecret: string, resource: string, allowLoopback?: boolean): Promise<TokenSet>;
|
|
59
|
+
refreshTokens(tokenEndpoint: string, refreshToken: string, clientId: string, clientSecret: string, resource: string, allowLoopback?: boolean, signal?: AbortSignal): Promise<TokenSet>;
|
|
60
60
|
/**
|
|
61
61
|
* Open browser to authorization URL
|
|
62
62
|
* Uses platform-specific command to open default browser
|
|
@@ -221,6 +221,7 @@ var InteractiveOAuthFlow = /*#__PURE__*/ function() {
|
|
|
221
221
|
port: options.port,
|
|
222
222
|
logger: logger
|
|
223
223
|
});
|
|
224
|
+
throwIfAborted(options.signal);
|
|
224
225
|
if (!options.pkce) return [
|
|
225
226
|
3,
|
|
226
227
|
2
|
|
@@ -286,18 +287,19 @@ var InteractiveOAuthFlow = /*#__PURE__*/ function() {
|
|
|
286
287
|
_state.sent();
|
|
287
288
|
_state.label = 6;
|
|
288
289
|
case 6:
|
|
290
|
+
throwIfAborted(options.signal);
|
|
289
291
|
// Wait for callback with timeout
|
|
290
292
|
timeout = options.timeout || (options.headless ? 60000 : 300000);
|
|
291
293
|
return [
|
|
292
294
|
4,
|
|
293
|
-
callbackListener.waitForCallback(timeout)
|
|
295
|
+
callbackListener.waitForCallback(timeout, options.signal)
|
|
294
296
|
];
|
|
295
297
|
case 7:
|
|
296
298
|
result = _state.sent();
|
|
297
299
|
this.assertResponseIssuer(result.iss, options, logger);
|
|
298
300
|
return [
|
|
299
301
|
4,
|
|
300
|
-
this.exchangeCodeForTokens(tokenEndpoint, result.code, clientId, clientSecret, redirectUri, options.resource, (_options_allowLoopback = options.allowLoopback) !== null && _options_allowLoopback !== void 0 ? _options_allowLoopback : false, pkce === null || pkce === void 0 ? void 0 : pkce.codeVerifier)
|
|
302
|
+
this.exchangeCodeForTokens(tokenEndpoint, result.code, clientId, clientSecret, redirectUri, options.resource, (_options_allowLoopback = options.allowLoopback) !== null && _options_allowLoopback !== void 0 ? _options_allowLoopback : false, pkce === null || pkce === void 0 ? void 0 : pkce.codeVerifier, options.signal)
|
|
301
303
|
];
|
|
302
304
|
case 8:
|
|
303
305
|
tokens = _state.sent();
|
|
@@ -348,7 +350,7 @@ var InteractiveOAuthFlow = /*#__PURE__*/ function() {
|
|
|
348
350
|
* @param resource - Canonical resource server URI, audience-binding the token (RFC 8707).
|
|
349
351
|
* @param allowLoopback - Loopback trust grant computed by the caller from the server it is actually talking to, never from `tokenEndpoint`.
|
|
350
352
|
* @param codeVerifier - Optional PKCE code verifier (RFC 7636).
|
|
351
|
-
*/ _proto.exchangeCodeForTokens = function exchangeCodeForTokens(tokenEndpoint, code, clientId, clientSecret, redirectUri, resource, allowLoopback, codeVerifier) {
|
|
353
|
+
*/ _proto.exchangeCodeForTokens = function exchangeCodeForTokens(tokenEndpoint, code, clientId, clientSecret, redirectUri, resource, allowLoopback, codeVerifier, signal) {
|
|
352
354
|
return _async_to_generator(function() {
|
|
353
355
|
var params, response, errorText, data, tokenSet;
|
|
354
356
|
return _ts_generator(this, function(_state) {
|
|
@@ -375,7 +377,8 @@ var InteractiveOAuthFlow = /*#__PURE__*/ function() {
|
|
|
375
377
|
Accept: 'application/json',
|
|
376
378
|
Connection: 'close'
|
|
377
379
|
},
|
|
378
|
-
body: params
|
|
380
|
+
body: params,
|
|
381
|
+
signal: signal
|
|
379
382
|
}, 'token endpoint', {
|
|
380
383
|
allowLoopback: allowLoopback
|
|
381
384
|
})
|
|
@@ -432,7 +435,7 @@ var InteractiveOAuthFlow = /*#__PURE__*/ function() {
|
|
|
432
435
|
* @returns New token set with a refreshed access token.
|
|
433
436
|
* @throws Error if refresh fails.
|
|
434
437
|
*/ _proto.refreshTokens = function refreshTokens(tokenEndpoint, refreshToken, clientId, clientSecret, resource) {
|
|
435
|
-
var allowLoopback = arguments.length > 5 && arguments[5] !== void 0 ? arguments[5] : false;
|
|
438
|
+
var allowLoopback = arguments.length > 5 && arguments[5] !== void 0 ? arguments[5] : false, signal = arguments.length > 6 ? arguments[6] : void 0;
|
|
436
439
|
return _async_to_generator(function() {
|
|
437
440
|
var response, errorText, data, tokenSet;
|
|
438
441
|
return _ts_generator(this, function(_state) {
|
|
@@ -453,7 +456,8 @@ var InteractiveOAuthFlow = /*#__PURE__*/ function() {
|
|
|
453
456
|
client_id: clientId,
|
|
454
457
|
client_secret: clientSecret,
|
|
455
458
|
resource: resource
|
|
456
|
-
})
|
|
459
|
+
}),
|
|
460
|
+
signal: signal
|
|
457
461
|
}, 'token endpoint', {
|
|
458
462
|
allowLoopback: allowLoopback
|
|
459
463
|
})
|
|
@@ -541,4 +545,7 @@ var InteractiveOAuthFlow = /*#__PURE__*/ function() {
|
|
|
541
545
|
};
|
|
542
546
|
return InteractiveOAuthFlow;
|
|
543
547
|
}();
|
|
548
|
+
function throwIfAborted(signal) {
|
|
549
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) throw _instanceof(signal.reason, Error) ? signal.reason : new Error('OAuth authorization was cancelled');
|
|
550
|
+
}
|
|
544
551
|
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/auth/interactive-oauth-flow.ts"],"sourcesContent":["/**\n * OAuth Authorization Flow Handler\n * Manages browser-based OAuth flows and token exchange with PKCE support\n */\n\nimport * as child_process from 'node:child_process';\nimport { logger as defaultLogger, type Logger } from '../utils/logger.ts';\nimport { discoveryFetch } from './discovery-fetch.ts';\nimport { OAuthCallbackListener } from './oauth-callback-listener.ts';\nimport { generatePkce } from './pkce.ts';\nimport type { OAuthFlowOptions, PkceParams, TokenSet } from './types.ts';\n\n/**\n * OAuth token response from token endpoint\n */\ninterface TokenResponse {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n scope?: string;\n token_type?: string;\n}\n\n/**\n * InteractiveOAuthFlow manages the complete OAuth authorization code flow\n */\nexport class InteractiveOAuthFlow {\n /**\n * Perform OAuth authorization code flow\n *\n * @param authorizationEndpoint - OAuth authorization endpoint URL\n * @param tokenEndpoint - OAuth token endpoint URL\n * @param clientId - OAuth client ID\n * @param clientSecret - OAuth client secret\n * @param options - Flow options (port is required - use get-port to find available port)\n * @returns Token set with access and refresh tokens\n *\n * @throws Error if flow fails or times out\n *\n * @example\n * import getPort from 'get-port';\n *\n * const flow = new InteractiveOAuthFlow();\n * const port = await getPort();\n * const tokens = await flow.performAuthFlow(\n * 'https://example.com/oauth/authorize',\n * 'https://example.com/oauth/token',\n * 'client-id',\n * 'client-secret',\n * { port, issuer: 'https://example.com', resource: 'https://example.com/mcp', scopes: ['read', 'write'] }\n * );\n */\n async performAuthFlow(authorizationEndpoint: string, tokenEndpoint: string, clientId: string, clientSecret: string, options: OAuthFlowOptions): Promise<TokenSet> {\n const logger = options.logger ?? defaultLogger;\n const callbackListener = new OAuthCallbackListener({ port: options.port, logger });\n\n // Generate PKCE parameters if requested (RFC 7636)\n let pkce: PkceParams | undefined;\n if (options.pkce) {\n logger.debug('🔐 Generating PKCE parameters...');\n pkce = await generatePkce();\n }\n\n try {\n // Start callback server\n await callbackListener.start();\n\n // Build redirect URI\n const redirectUri = options.redirectUri || `http://localhost:${options.port}/callback`;\n\n // Build authorization URL\n const authUrl = new URL(authorizationEndpoint);\n authUrl.searchParams.set('client_id', clientId);\n authUrl.searchParams.set('redirect_uri', redirectUri);\n authUrl.searchParams.set('response_type', 'code');\n\n if (options.scopes && options.scopes.length > 0) {\n authUrl.searchParams.set('scope', options.scopes.join(' '));\n }\n\n // Audience-bind the request to the resource server (RFC 8707)\n authUrl.searchParams.set('resource', options.resource);\n\n // Add PKCE parameters if generated (RFC 7636)\n if (pkce) {\n authUrl.searchParams.set('code_challenge', pkce.codeChallenge);\n authUrl.searchParams.set('code_challenge_method', pkce.codeChallengeMethod);\n }\n\n // Open browser or print URL for headless mode\n if (options.headless) {\n logger.info('🔗 Please visit this URL to authorize:');\n logger.info(authUrl.toString());\n logger.info('Waiting for callback...');\n } else {\n logger.debug('🌐 Opening browser for OAuth authorization...');\n // Try to open browser (requires 'open' package or native command)\n await this.openBrowser(authUrl.toString());\n }\n\n // Wait for callback with timeout\n const timeout = options.timeout || (options.headless ? 60000 : 300000);\n const result = await callbackListener.waitForCallback(timeout);\n\n this.assertResponseIssuer(result.iss, options, logger);\n\n // Exchange authorization code for tokens (with PKCE verifier if used)\n const tokens = await this.exchangeCodeForTokens(tokenEndpoint, result.code, clientId, clientSecret, redirectUri, options.resource, options.allowLoopback ?? false, pkce?.codeVerifier);\n\n return tokens;\n } catch (error) {\n logger.error('❌ OAuth flow failed:', error instanceof Error ? error.message : String(error));\n throw error;\n } finally {\n // Always close callback server\n await callbackListener.stop();\n }\n }\n\n /**\n * Rejects an authorization response that was not minted by the issuer\n * discovered before the flow started (RFC 9207 authorization-server mix-up).\n */\n private assertResponseIssuer(iss: string | undefined, options: OAuthFlowOptions, logger: Logger): void {\n if (iss !== undefined) {\n if (iss !== options.issuer) {\n throw new Error(`Authorization response issuer mismatch: got '${iss}', expected '${options.issuer}' - refusing to redeem the authorization code`);\n }\n return;\n }\n\n if (options.authorizationResponseIssSupported) {\n throw new Error(`Authorization server '${options.issuer}' advertises authorization_response_iss_parameter_supported but omitted 'iss' - refusing to redeem the authorization code`);\n }\n\n logger.debug(`⚠️ Authorization response carried no 'iss' and '${options.issuer}' does not advertise support for it (RFC 9207)`);\n }\n\n /**\n * Exchanges an authorization code for access and refresh tokens.\n * @param resource - Canonical resource server URI, audience-binding the token (RFC 8707).\n * @param allowLoopback - Loopback trust grant computed by the caller from the server it is actually talking to, never from `tokenEndpoint`.\n * @param codeVerifier - Optional PKCE code verifier (RFC 7636).\n */\n private async exchangeCodeForTokens(tokenEndpoint: string, code: string, clientId: string, clientSecret: string, redirectUri: string, resource: string, allowLoopback: boolean, codeVerifier?: string): Promise<TokenSet> {\n const params = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: redirectUri,\n client_id: clientId,\n client_secret: clientSecret,\n resource,\n });\n\n // Add PKCE code verifier if provided (RFC 7636)\n if (codeVerifier) {\n params.set('code_verifier', codeVerifier);\n }\n\n // tokenEndpoint is remote-controlled discovery data; discoveryFetch blocks\n // a private/internal target before the client secret is sent to it.\n const response = await discoveryFetch(\n tokenEndpoint,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n Connection: 'close',\n },\n body: params,\n },\n 'token endpoint',\n { allowLoopback }\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token exchange failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n\n if (!data.access_token) {\n throw new Error('Token response missing access_token');\n }\n\n const tokenSet: TokenSet = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token || '',\n expiresAt: Date.now() + data.expires_in * 1000,\n clientId,\n clientSecret,\n };\n\n if (data.scope) {\n tokenSet.scopes = data.scope.split(' ');\n }\n\n return tokenSet;\n }\n\n /**\n * Refreshes an access token using a refresh token.\n * @param tokenEndpoint - OAuth token endpoint URL.\n * @param refreshToken - Refresh token from a previous token set.\n * @param clientId - OAuth client ID.\n * @param clientSecret - OAuth client secret.\n * @param resource - Canonical resource server URI, audience-binding the token (RFC 8707).\n * @param allowLoopback - Loopback trust grant computed by the caller from the server it is actually talking to, never from `tokenEndpoint`. Defaults to `false`.\n * @returns New token set with a refreshed access token.\n * @throws Error if refresh fails.\n */\n async refreshTokens(tokenEndpoint: string, refreshToken: string, clientId: string, clientSecret: string, resource: string, allowLoopback = false): Promise<TokenSet> {\n // See exchangeCodeForTokens - tokenEndpoint is remote-controlled discovery data.\n const response = await discoveryFetch(\n tokenEndpoint,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n Connection: 'close',\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: clientId,\n client_secret: clientSecret,\n resource,\n }),\n },\n 'token endpoint',\n { allowLoopback }\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token refresh failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n\n if (!data.access_token) {\n throw new Error('Token refresh response missing access_token');\n }\n\n const tokenSet: TokenSet = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token || refreshToken, // Reuse old refresh token if not provided\n expiresAt: Date.now() + data.expires_in * 1000,\n clientId,\n clientSecret,\n };\n\n if (data.scope) {\n tokenSet.scopes = data.scope.split(' ');\n }\n\n return tokenSet;\n }\n\n /**\n * Open browser to authorization URL\n * Uses platform-specific command to open default browser\n */\n private async openBrowser(url: string): Promise<void> {\n // Determine platform-specific command\n const platform = process.platform;\n let command: string;\n let args: string[];\n\n if (platform === 'darwin') {\n command = 'open';\n args = [url];\n } else if (platform === 'win32') {\n command = 'cmd';\n args = ['/c', 'start', url];\n } else {\n // Linux and others\n command = 'xdg-open';\n args = [url];\n }\n\n // Spawn browser process\n const child = child_process.spawn(command, args, {\n detached: true,\n stdio: 'ignore',\n });\n\n child.unref();\n }\n}\n"],"names":["InteractiveOAuthFlow","performAuthFlow","authorizationEndpoint","tokenEndpoint","clientId","clientSecret","options","logger","callbackListener","pkce","redirectUri","authUrl","timeout","result","tokens","error","defaultLogger","OAuthCallbackListener","port","debug","generatePkce","start","URL","searchParams","set","scopes","length","join","resource","codeChallenge","codeChallengeMethod","headless","info","toString","openBrowser","waitForCallback","assertResponseIssuer","iss","exchangeCodeForTokens","code","allowLoopback","codeVerifier","Error","message","String","stop","undefined","issuer","authorizationResponseIssSupported","params","response","errorText","data","tokenSet","URLSearchParams","grant_type","redirect_uri","client_id","client_secret","discoveryFetch","method","headers","Accept","Connection","body","ok","text","status","json","access_token","accessToken","refreshToken","refresh_token","expiresAt","Date","now","expires_in","scope","split","refreshTokens","url","platform","command","args","child","process","child_process","spawn","detached","stdio","unref"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/auth/interactive-oauth-flow.ts"],"sourcesContent":["/**\n * OAuth Authorization Flow Handler\n * Manages browser-based OAuth flows and token exchange with PKCE support\n */\n\nimport * as child_process from 'node:child_process';\nimport { logger as defaultLogger, type Logger } from '../utils/logger.ts';\nimport { discoveryFetch } from './discovery-fetch.ts';\nimport { OAuthCallbackListener } from './oauth-callback-listener.ts';\nimport { generatePkce } from './pkce.ts';\nimport type { OAuthFlowOptions, PkceParams, TokenSet } from './types.ts';\n\n/**\n * OAuth token response from token endpoint\n */\ninterface TokenResponse {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n scope?: string;\n token_type?: string;\n}\n\n/**\n * InteractiveOAuthFlow manages the complete OAuth authorization code flow\n */\nexport class InteractiveOAuthFlow {\n /**\n * Perform OAuth authorization code flow\n *\n * @param authorizationEndpoint - OAuth authorization endpoint URL\n * @param tokenEndpoint - OAuth token endpoint URL\n * @param clientId - OAuth client ID\n * @param clientSecret - OAuth client secret\n * @param options - Flow options (port is required - use get-port to find available port)\n * @returns Token set with access and refresh tokens\n *\n * @throws Error if flow fails or times out\n *\n * @example\n * import getPort from 'get-port';\n *\n * const flow = new InteractiveOAuthFlow();\n * const port = await getPort();\n * const tokens = await flow.performAuthFlow(\n * 'https://example.com/oauth/authorize',\n * 'https://example.com/oauth/token',\n * 'client-id',\n * 'client-secret',\n * { port, issuer: 'https://example.com', resource: 'https://example.com/mcp', scopes: ['read', 'write'] }\n * );\n */\n async performAuthFlow(authorizationEndpoint: string, tokenEndpoint: string, clientId: string, clientSecret: string, options: OAuthFlowOptions): Promise<TokenSet> {\n const logger = options.logger ?? defaultLogger;\n const callbackListener = new OAuthCallbackListener({ port: options.port, logger });\n throwIfAborted(options.signal);\n\n // Generate PKCE parameters if requested (RFC 7636)\n let pkce: PkceParams | undefined;\n if (options.pkce) {\n logger.debug('🔐 Generating PKCE parameters...');\n pkce = await generatePkce();\n }\n\n try {\n // Start callback server\n await callbackListener.start();\n\n // Build redirect URI\n const redirectUri = options.redirectUri || `http://localhost:${options.port}/callback`;\n\n // Build authorization URL\n const authUrl = new URL(authorizationEndpoint);\n authUrl.searchParams.set('client_id', clientId);\n authUrl.searchParams.set('redirect_uri', redirectUri);\n authUrl.searchParams.set('response_type', 'code');\n\n if (options.scopes && options.scopes.length > 0) {\n authUrl.searchParams.set('scope', options.scopes.join(' '));\n }\n\n // Audience-bind the request to the resource server (RFC 8707)\n authUrl.searchParams.set('resource', options.resource);\n\n // Add PKCE parameters if generated (RFC 7636)\n if (pkce) {\n authUrl.searchParams.set('code_challenge', pkce.codeChallenge);\n authUrl.searchParams.set('code_challenge_method', pkce.codeChallengeMethod);\n }\n\n // Open browser or print URL for headless mode\n if (options.headless) {\n logger.info('🔗 Please visit this URL to authorize:');\n logger.info(authUrl.toString());\n logger.info('Waiting for callback...');\n } else {\n logger.debug('🌐 Opening browser for OAuth authorization...');\n // Try to open browser (requires 'open' package or native command)\n await this.openBrowser(authUrl.toString());\n }\n throwIfAborted(options.signal);\n\n // Wait for callback with timeout\n const timeout = options.timeout || (options.headless ? 60000 : 300000);\n const result = await callbackListener.waitForCallback(timeout, options.signal);\n\n this.assertResponseIssuer(result.iss, options, logger);\n\n // Exchange authorization code for tokens (with PKCE verifier if used)\n const tokens = await this.exchangeCodeForTokens(tokenEndpoint, result.code, clientId, clientSecret, redirectUri, options.resource, options.allowLoopback ?? false, pkce?.codeVerifier, options.signal);\n\n return tokens;\n } catch (error) {\n logger.error('❌ OAuth flow failed:', error instanceof Error ? error.message : String(error));\n throw error;\n } finally {\n // Always close callback server\n await callbackListener.stop();\n }\n }\n\n /**\n * Rejects an authorization response that was not minted by the issuer\n * discovered before the flow started (RFC 9207 authorization-server mix-up).\n */\n private assertResponseIssuer(iss: string | undefined, options: OAuthFlowOptions, logger: Logger): void {\n if (iss !== undefined) {\n if (iss !== options.issuer) {\n throw new Error(`Authorization response issuer mismatch: got '${iss}', expected '${options.issuer}' - refusing to redeem the authorization code`);\n }\n return;\n }\n\n if (options.authorizationResponseIssSupported) {\n throw new Error(`Authorization server '${options.issuer}' advertises authorization_response_iss_parameter_supported but omitted 'iss' - refusing to redeem the authorization code`);\n }\n\n logger.debug(`⚠️ Authorization response carried no 'iss' and '${options.issuer}' does not advertise support for it (RFC 9207)`);\n }\n\n /**\n * Exchanges an authorization code for access and refresh tokens.\n * @param resource - Canonical resource server URI, audience-binding the token (RFC 8707).\n * @param allowLoopback - Loopback trust grant computed by the caller from the server it is actually talking to, never from `tokenEndpoint`.\n * @param codeVerifier - Optional PKCE code verifier (RFC 7636).\n */\n private async exchangeCodeForTokens(tokenEndpoint: string, code: string, clientId: string, clientSecret: string, redirectUri: string, resource: string, allowLoopback: boolean, codeVerifier?: string, signal?: AbortSignal): Promise<TokenSet> {\n const params = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: redirectUri,\n client_id: clientId,\n client_secret: clientSecret,\n resource,\n });\n\n // Add PKCE code verifier if provided (RFC 7636)\n if (codeVerifier) {\n params.set('code_verifier', codeVerifier);\n }\n\n // tokenEndpoint is remote-controlled discovery data; discoveryFetch blocks\n // a private/internal target before the client secret is sent to it.\n const response = await discoveryFetch(\n tokenEndpoint,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n Connection: 'close',\n },\n body: params,\n signal,\n },\n 'token endpoint',\n { allowLoopback }\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token exchange failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n\n if (!data.access_token) {\n throw new Error('Token response missing access_token');\n }\n\n const tokenSet: TokenSet = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token || '',\n expiresAt: Date.now() + data.expires_in * 1000,\n clientId,\n clientSecret,\n };\n\n if (data.scope) {\n tokenSet.scopes = data.scope.split(' ');\n }\n\n return tokenSet;\n }\n\n /**\n * Refreshes an access token using a refresh token.\n * @param tokenEndpoint - OAuth token endpoint URL.\n * @param refreshToken - Refresh token from a previous token set.\n * @param clientId - OAuth client ID.\n * @param clientSecret - OAuth client secret.\n * @param resource - Canonical resource server URI, audience-binding the token (RFC 8707).\n * @param allowLoopback - Loopback trust grant computed by the caller from the server it is actually talking to, never from `tokenEndpoint`. Defaults to `false`.\n * @returns New token set with a refreshed access token.\n * @throws Error if refresh fails.\n */\n async refreshTokens(tokenEndpoint: string, refreshToken: string, clientId: string, clientSecret: string, resource: string, allowLoopback = false, signal?: AbortSignal): Promise<TokenSet> {\n // See exchangeCodeForTokens - tokenEndpoint is remote-controlled discovery data.\n const response = await discoveryFetch(\n tokenEndpoint,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n Connection: 'close',\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: clientId,\n client_secret: clientSecret,\n resource,\n }),\n signal,\n },\n 'token endpoint',\n { allowLoopback }\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token refresh failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n\n if (!data.access_token) {\n throw new Error('Token refresh response missing access_token');\n }\n\n const tokenSet: TokenSet = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token || refreshToken, // Reuse old refresh token if not provided\n expiresAt: Date.now() + data.expires_in * 1000,\n clientId,\n clientSecret,\n };\n\n if (data.scope) {\n tokenSet.scopes = data.scope.split(' ');\n }\n\n return tokenSet;\n }\n\n /**\n * Open browser to authorization URL\n * Uses platform-specific command to open default browser\n */\n private async openBrowser(url: string): Promise<void> {\n // Determine platform-specific command\n const platform = process.platform;\n let command: string;\n let args: string[];\n\n if (platform === 'darwin') {\n command = 'open';\n args = [url];\n } else if (platform === 'win32') {\n command = 'cmd';\n args = ['/c', 'start', url];\n } else {\n // Linux and others\n command = 'xdg-open';\n args = [url];\n }\n\n // Spawn browser process\n const child = child_process.spawn(command, args, {\n detached: true,\n stdio: 'ignore',\n });\n\n child.unref();\n }\n}\n\nfunction throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted) throw signal.reason instanceof Error ? signal.reason : new Error('OAuth authorization was cancelled');\n}\n"],"names":["InteractiveOAuthFlow","performAuthFlow","authorizationEndpoint","tokenEndpoint","clientId","clientSecret","options","logger","callbackListener","pkce","redirectUri","authUrl","timeout","result","tokens","error","defaultLogger","OAuthCallbackListener","port","throwIfAborted","signal","debug","generatePkce","start","URL","searchParams","set","scopes","length","join","resource","codeChallenge","codeChallengeMethod","headless","info","toString","openBrowser","waitForCallback","assertResponseIssuer","iss","exchangeCodeForTokens","code","allowLoopback","codeVerifier","Error","message","String","stop","undefined","issuer","authorizationResponseIssSupported","params","response","errorText","data","tokenSet","URLSearchParams","grant_type","redirect_uri","client_id","client_secret","discoveryFetch","method","headers","Accept","Connection","body","ok","text","status","json","access_token","accessToken","refreshToken","refresh_token","expiresAt","Date","now","expires_in","scope","split","refreshTokens","url","platform","command","args","child","process","child_process","spawn","detached","stdio","unref","aborted","reason"],"mappings":"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"}
|
|
@@ -44,7 +44,7 @@ export declare class OAuthCallbackListener {
|
|
|
44
44
|
/**
|
|
45
45
|
* Wait for OAuth callback with timeout
|
|
46
46
|
*/
|
|
47
|
-
waitForCallback(timeoutMs?: number): Promise<CallbackResult>;
|
|
47
|
+
waitForCallback(timeoutMs?: number, signal?: AbortSignal): Promise<CallbackResult>;
|
|
48
48
|
/**
|
|
49
49
|
* Stop the callback server and close
|
|
50
50
|
*/
|
|
@@ -44,7 +44,7 @@ export declare class OAuthCallbackListener {
|
|
|
44
44
|
/**
|
|
45
45
|
* Wait for OAuth callback with timeout
|
|
46
46
|
*/
|
|
47
|
-
waitForCallback(timeoutMs?: number): Promise<CallbackResult>;
|
|
47
|
+
waitForCallback(timeoutMs?: number, signal?: AbortSignal): Promise<CallbackResult>;
|
|
48
48
|
/**
|
|
49
49
|
* Stop the callback server and close
|
|
50
50
|
*/
|
|
@@ -42,6 +42,12 @@ function _async_to_generator(fn) {
|
|
|
42
42
|
function _class_call_check(instance, Constructor) {
|
|
43
43
|
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
|
|
44
44
|
}
|
|
45
|
+
function _instanceof(left, right) {
|
|
46
|
+
"@swc/helpers - instanceof";
|
|
47
|
+
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
|
|
48
|
+
return !!right[Symbol.hasInstance](left);
|
|
49
|
+
} else return left instanceof right;
|
|
50
|
+
}
|
|
45
51
|
function _interop_require_default(obj) {
|
|
46
52
|
return obj && obj.__esModule ? obj : {
|
|
47
53
|
default: obj
|
|
@@ -259,7 +265,7 @@ var OAuthCallbackListener = /*#__PURE__*/ function() {
|
|
|
259
265
|
/**
|
|
260
266
|
* Wait for OAuth callback with timeout
|
|
261
267
|
*/ _proto.waitForCallback = function waitForCallback() {
|
|
262
|
-
var timeoutMs = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 300000;
|
|
268
|
+
var timeoutMs = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 300000, signal = arguments.length > 1 ? arguments[1] : void 0;
|
|
263
269
|
return _async_to_generator(function() {
|
|
264
270
|
var _this;
|
|
265
271
|
return _ts_generator(this, function(_state) {
|
|
@@ -267,13 +273,43 @@ var OAuthCallbackListener = /*#__PURE__*/ function() {
|
|
|
267
273
|
return [
|
|
268
274
|
2,
|
|
269
275
|
new Promise(function(resolve, reject) {
|
|
276
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
277
|
+
reject(_instanceof(signal.reason, Error) ? signal.reason : new Error('OAuth authorization was cancelled'));
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
270
280
|
_this.resolveCallback = resolve;
|
|
271
281
|
_this.rejectCallback = reject;
|
|
282
|
+
var onAbort = function onAbort() {
|
|
283
|
+
var _this_rejectCallback, _this1;
|
|
284
|
+
return (_this_rejectCallback = (_this1 = _this).rejectCallback) === null || _this_rejectCallback === void 0 ? void 0 : _this_rejectCallback.call(_this1, _instanceof(signal === null || signal === void 0 ? void 0 : signal.reason, Error) ? signal.reason : new Error('OAuth authorization was cancelled'));
|
|
285
|
+
};
|
|
286
|
+
signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', onAbort, {
|
|
287
|
+
once: true
|
|
288
|
+
});
|
|
272
289
|
// Set timeout to prevent hanging forever
|
|
273
290
|
_this.timeout = setTimeout(function() {
|
|
274
|
-
|
|
291
|
+
var _this_rejectCallback, _this1;
|
|
292
|
+
(_this_rejectCallback = (_this1 = _this).rejectCallback) === null || _this_rejectCallback === void 0 ? void 0 : _this_rejectCallback.call(_this1, new Error("Authorization timeout - no callback received within ".concat(timeoutMs / 1000, " seconds")));
|
|
275
293
|
_this.stop();
|
|
276
294
|
}, timeoutMs);
|
|
295
|
+
var cleanup = function cleanup() {
|
|
296
|
+
signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
|
|
297
|
+
if (_this.timeout) {
|
|
298
|
+
clearTimeout(_this.timeout);
|
|
299
|
+
_this.timeout = undefined;
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
// Promise settlement can originate from callback, abort, or timeout.
|
|
303
|
+
var originalResolve = _this.resolveCallback;
|
|
304
|
+
var originalReject = _this.rejectCallback;
|
|
305
|
+
_this.resolveCallback = function(result) {
|
|
306
|
+
cleanup();
|
|
307
|
+
originalResolve === null || originalResolve === void 0 ? void 0 : originalResolve(result);
|
|
308
|
+
};
|
|
309
|
+
_this.rejectCallback = function(error) {
|
|
310
|
+
cleanup();
|
|
311
|
+
originalReject === null || originalReject === void 0 ? void 0 : originalReject(error);
|
|
312
|
+
};
|
|
277
313
|
})
|
|
278
314
|
];
|
|
279
315
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/auth/oauth-callback-listener.ts"],"sourcesContent":["/**\n * OAuth Callback Server for CLI Authentication\n * Listens for OAuth authorization callbacks and captures authorization code\n */\n\nimport http from 'node:http';\nimport { logger as defaultLogger, type Logger } from '../utils/logger.ts';\nimport type { CallbackResult } from './types.ts';\n\nexport interface OAuthCallbackListenerOptions {\n /** Port to listen on (required - use get-port package to find available port) */\n port: number;\n /** Optional logger for debug output (defaults to singleton logger) */\n logger?: Logger;\n}\n\n/**\n * OAuthCallbackListener handles OAuth redirect callbacks\n * Starts a temporary HTTP server to receive authorization code\n *\n * Note: Caller is responsible for finding an available port using get-port package\n */\nexport class OAuthCallbackListener {\n private server: http.Server | undefined;\n private resolveCallback?: (result: CallbackResult) => void;\n private rejectCallback?: (error: Error) => void;\n private timeout: NodeJS.Timeout | undefined;\n private port: number;\n private logger: Logger;\n\n constructor(options: OAuthCallbackListenerOptions) {\n this.port = options.port;\n this.logger = options.logger ?? defaultLogger;\n }\n\n /**\n * Start the callback server\n * Fails fast if port is already in use - caller should use get-port to find available port\n */\n async start(): Promise<void> {\n await this.listen(this.port);\n this.logger.debug(`✅ Callback server listening on http://localhost:${this.port}/callback`);\n }\n\n /**\n * Listen on a specific port\n */\n private listen(port: number): Promise<void> {\n return new Promise((resolve, reject) => {\n this.server = http.createServer((req, res) => {\n this.handleRequest(req, res);\n });\n\n this.server.on('error', (error) => {\n reject(error);\n });\n\n this.server.listen(port, 'localhost', () => {\n resolve();\n });\n });\n }\n\n /**\n * Handle incoming HTTP requests\n */\n private handleRequest(req: http.IncomingMessage, res: http.ServerResponse): void {\n const url = new URL(req.url || '', `http://localhost:${this.port}`);\n\n if (url.pathname === '/callback') {\n this.handleCallback(url, res);\n } else {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not Found');\n }\n }\n\n /**\n * Handle OAuth callback\n */\n private handleCallback(url: URL, res: http.ServerResponse): void {\n const code = url.searchParams.get('code');\n const state = url.searchParams.get('state');\n const iss = url.searchParams.get('iss');\n const error = url.searchParams.get('error');\n const errorDescription = url.searchParams.get('error_description');\n\n // Handle OAuth errors\n if (error) {\n const errorMessage = errorDescription ? `${error}: ${errorDescription}` : error;\n\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(`\n <html>\n <body>\n <h1>Authorization Failed</h1>\n <p>The authorization provider returned an error.</p>\n <script>setTimeout(() => window.close(), 3000);</script>\n </body>\n </html>\n `);\n\n if (this.rejectCallback) {\n this.rejectCallback(new Error(errorMessage));\n }\n return;\n }\n\n // Validate code parameter\n if (!code) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(`\n <html>\n <body>\n <h1>Invalid Callback</h1>\n <p>Missing authorization code</p>\n <script>setTimeout(() => window.close(), 3000);</script>\n </body>\n </html>\n `);\n\n if (this.rejectCallback) {\n this.rejectCallback(new Error('Missing authorization code'));\n }\n return;\n }\n\n // Success - send confirmation page\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(`\n <html>\n <head>\n <meta charset=\"UTF-8\">\n </head>\n <body>\n <h1>Authorization Successful</h1>\n <p>You can close this window and return to the terminal.</p>\n <script>setTimeout(() => window.close(), 2000);</script>\n </body>\n </html>\n `);\n\n // Resolve the promise with authorization code\n if (this.resolveCallback) {\n const result: CallbackResult = { code };\n if (state) {\n result.state = state;\n }\n if (iss) {\n result.iss = iss;\n }\n this.resolveCallback(result);\n }\n }\n\n /**\n * Wait for OAuth callback with timeout\n */\n async waitForCallback(timeoutMs = 300000): Promise<CallbackResult> {\n return new Promise((resolve, reject) => {\n this.resolveCallback = resolve;\n this.rejectCallback = reject;\n\n // Set timeout to prevent hanging forever\n this.timeout = setTimeout(() => {\n reject(new Error(`Authorization timeout - no callback received within ${timeoutMs / 1000} seconds`));\n this.stop();\n }, timeoutMs);\n });\n }\n\n /**\n * Stop the callback server and close\n */\n async stop(): Promise<void> {\n // Clear the timeout\n if (this.timeout) {\n clearTimeout(this.timeout);\n this.timeout = undefined;\n }\n\n // Close the server\n if (this.server) {\n await new Promise<void>((resolve) => {\n this.server?.close(() => {\n this.logger.debug('🔒 Callback server closed');\n resolve();\n });\n });\n this.server = undefined;\n }\n }\n\n /**\n * Get the callback URL for this server\n */\n getCallbackUrl(): string {\n if (!this.port) {\n throw new Error('Server not started - call start() first');\n }\n return `http://localhost:${this.port}/callback`;\n }\n}\n"],"names":["OAuthCallbackListener","options","port","logger","defaultLogger","start","listen","debug","Promise","resolve","reject","server","http","createServer","req","res","handleRequest","on","error","url","URL","pathname","handleCallback","writeHead","end","code","searchParams","get","state","iss","errorDescription","errorMessage","rejectCallback","Error","resolveCallback","result","waitForCallback","timeoutMs","timeout","setTimeout","stop","clearTimeout","undefined","close","getCallbackUrl"],"mappings":"AAAA;;;CAGC;;;;+BAmBYA;;;eAAAA;;;+DAjBI;wBACoC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgB9C,IAAA,AAAMA,sCAAN;;aAAMA,sBAQCC,OAAqC;gCARtCD;YAUKC;QADd,IAAI,CAACC,IAAI,GAAGD,QAAQC,IAAI;QACxB,IAAI,CAACC,MAAM,IAAGF,kBAAAA,QAAQE,MAAM,cAAdF,6BAAAA,kBAAkBG,gBAAa;;iBAVpCJ;IAaX;;;GAGC,GACD,OAAMK,KAGL,GAHD,SAAMA;;;;;wBACJ;;4BAAM,IAAI,CAACC,MAAM,CAAC,IAAI,CAACJ,IAAI;;;wBAA3B;wBACA,IAAI,CAACC,MAAM,CAACI,KAAK,CAAC,AAAC,mDAA4D,OAAV,IAAI,CAACL,IAAI,EAAC;;;;;;QACjF;;IAEA;;GAEC,GACD,OAAQI,MAcP,GAdD,SAAQA,OAAOJ,IAAY;;QACzB,OAAO,IAAIM,QAAQ,SAACC,SAASC;YAC3B,MAAKC,MAAM,GAAGC,iBAAI,CAACC,YAAY,CAAC,SAACC,KAAKC;gBACpC,MAAKC,aAAa,CAACF,KAAKC;YAC1B;YAEA,MAAKJ,MAAM,CAACM,EAAE,CAAC,SAAS,SAACC;gBACvBR,OAAOQ;YACT;YAEA,MAAKP,MAAM,CAACL,MAAM,CAACJ,MAAM,aAAa;gBACpCO;YACF;QACF;IACF;IAEA;;GAEC,GACD,OAAQO,aASP,GATD,SAAQA,cAAcF,GAAyB,EAAEC,GAAwB;QACvE,IAAMI,MAAM,IAAIC,IAAIN,IAAIK,GAAG,IAAI,IAAI,AAAC,oBAA6B,OAAV,IAAI,CAACjB,IAAI;QAEhE,IAAIiB,IAAIE,QAAQ,KAAK,aAAa;YAChC,IAAI,CAACC,cAAc,CAACH,KAAKJ;QAC3B,OAAO;YACLA,IAAIQ,SAAS,CAAC,KAAK;gBAAE,gBAAgB;YAAa;YAClDR,IAAIS,GAAG,CAAC;QACV;IACF;IAEA;;GAEC,GACD,OAAQF,cAyEP,GAzED,SAAQA,eAAeH,GAAQ,EAAEJ,GAAwB;QACvD,IAAMU,OAAON,IAAIO,YAAY,CAACC,GAAG,CAAC;QAClC,IAAMC,QAAQT,IAAIO,YAAY,CAACC,GAAG,CAAC;QACnC,IAAME,MAAMV,IAAIO,YAAY,CAACC,GAAG,CAAC;QACjC,IAAMT,QAAQC,IAAIO,YAAY,CAACC,GAAG,CAAC;QACnC,IAAMG,mBAAmBX,IAAIO,YAAY,CAACC,GAAG,CAAC;QAE9C,sBAAsB;QACtB,IAAIT,OAAO;YACT,IAAMa,eAAeD,mBAAmB,AAAC,GAAYA,OAAVZ,OAAM,MAAqB,OAAjBY,oBAAqBZ;YAE1EH,IAAIQ,SAAS,CAAC,KAAK;gBAAE,gBAAgB;YAAY;YACjDR,IAAIS,GAAG,CAAC;YAUR,IAAI,IAAI,CAACQ,cAAc,EAAE;gBACvB,IAAI,CAACA,cAAc,CAAC,IAAIC,MAAMF;YAChC;YACA;QACF;QAEA,0BAA0B;QAC1B,IAAI,CAACN,MAAM;YACTV,IAAIQ,SAAS,CAAC,KAAK;gBAAE,gBAAgB;YAAY;YACjDR,IAAIS,GAAG,CAAC;YAUR,IAAI,IAAI,CAACQ,cAAc,EAAE;gBACvB,IAAI,CAACA,cAAc,CAAC,IAAIC,MAAM;YAChC;YACA;QACF;QAEA,mCAAmC;QACnClB,IAAIQ,SAAS,CAAC,KAAK;YAAE,gBAAgB;QAA2B;QAChER,IAAIS,GAAG,CAAC;QAaR,8CAA8C;QAC9C,IAAI,IAAI,CAACU,eAAe,EAAE;YACxB,IAAMC,SAAyB;gBAAEV,MAAAA;YAAK;YACtC,IAAIG,OAAO;gBACTO,OAAOP,KAAK,GAAGA;YACjB;YACA,IAAIC,KAAK;gBACPM,OAAON,GAAG,GAAGA;YACf;YACA,IAAI,CAACK,eAAe,CAACC;QACvB;IACF;IAEA;;GAEC,GACD,OAAMC,eAWL,GAXD,SAAMA;YAAgBC,YAAAA,iEAAY;;;;;gBAChC;;oBAAO,IAAI7B,QAAQ,SAACC,SAASC;wBAC3B,MAAKwB,eAAe,GAAGzB;wBACvB,MAAKuB,cAAc,GAAGtB;wBAEtB,yCAAyC;wBACzC,MAAK4B,OAAO,GAAGC,WAAW;4BACxB7B,OAAO,IAAIuB,MAAM,AAAC,uDAAuE,OAAjBI,YAAY,MAAK;4BACzF,MAAKG,IAAI;wBACX,GAAGH;oBACL;;;QACF;;IAEA;;GAEC,GACD,OAAMG,IAiBL,GAjBD,SAAMA;;;;;;;wBACJ,oBAAoB;wBACpB,IAAI,IAAI,CAACF,OAAO,EAAE;4BAChBG,aAAa,IAAI,CAACH,OAAO;4BACzB,IAAI,CAACA,OAAO,GAAGI;wBACjB;6BAGI,IAAI,CAAC/B,MAAM,EAAX;;;;wBACF;;4BAAM,IAAIH,QAAc,SAACC;oCACvB;iCAAA,eAAA,MAAKE,MAAM,cAAX,mCAAA,aAAagC,KAAK,CAAC;oCACjB,MAAKxC,MAAM,CAACI,KAAK,CAAC;oCAClBE;gCACF;4BACF;;;wBALA;wBAMA,IAAI,CAACE,MAAM,GAAG+B;;;;;;;;QAElB;;IAEA;;GAEC,GACDE,OAAAA,cAKC,GALDA,SAAAA;QACE,IAAI,CAAC,IAAI,CAAC1C,IAAI,EAAE;YACd,MAAM,IAAI+B,MAAM;QAClB;QACA,OAAO,AAAC,oBAA6B,OAAV,IAAI,CAAC/B,IAAI,EAAC;IACvC;WAnLWF"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/client/src/auth/oauth-callback-listener.ts"],"sourcesContent":["/**\n * OAuth Callback Server for CLI Authentication\n * Listens for OAuth authorization callbacks and captures authorization code\n */\n\nimport http from 'node:http';\nimport { logger as defaultLogger, type Logger } from '../utils/logger.ts';\nimport type { CallbackResult } from './types.ts';\n\nexport interface OAuthCallbackListenerOptions {\n /** Port to listen on (required - use get-port package to find available port) */\n port: number;\n /** Optional logger for debug output (defaults to singleton logger) */\n logger?: Logger;\n}\n\n/**\n * OAuthCallbackListener handles OAuth redirect callbacks\n * Starts a temporary HTTP server to receive authorization code\n *\n * Note: Caller is responsible for finding an available port using get-port package\n */\nexport class OAuthCallbackListener {\n private server: http.Server | undefined;\n private resolveCallback?: (result: CallbackResult) => void;\n private rejectCallback?: (error: Error) => void;\n private timeout: NodeJS.Timeout | undefined;\n private port: number;\n private logger: Logger;\n\n constructor(options: OAuthCallbackListenerOptions) {\n this.port = options.port;\n this.logger = options.logger ?? defaultLogger;\n }\n\n /**\n * Start the callback server\n * Fails fast if port is already in use - caller should use get-port to find available port\n */\n async start(): Promise<void> {\n await this.listen(this.port);\n this.logger.debug(`✅ Callback server listening on http://localhost:${this.port}/callback`);\n }\n\n /**\n * Listen on a specific port\n */\n private listen(port: number): Promise<void> {\n return new Promise((resolve, reject) => {\n this.server = http.createServer((req, res) => {\n this.handleRequest(req, res);\n });\n\n this.server.on('error', (error) => {\n reject(error);\n });\n\n this.server.listen(port, 'localhost', () => {\n resolve();\n });\n });\n }\n\n /**\n * Handle incoming HTTP requests\n */\n private handleRequest(req: http.IncomingMessage, res: http.ServerResponse): void {\n const url = new URL(req.url || '', `http://localhost:${this.port}`);\n\n if (url.pathname === '/callback') {\n this.handleCallback(url, res);\n } else {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not Found');\n }\n }\n\n /**\n * Handle OAuth callback\n */\n private handleCallback(url: URL, res: http.ServerResponse): void {\n const code = url.searchParams.get('code');\n const state = url.searchParams.get('state');\n const iss = url.searchParams.get('iss');\n const error = url.searchParams.get('error');\n const errorDescription = url.searchParams.get('error_description');\n\n // Handle OAuth errors\n if (error) {\n const errorMessage = errorDescription ? `${error}: ${errorDescription}` : error;\n\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(`\n <html>\n <body>\n <h1>Authorization Failed</h1>\n <p>The authorization provider returned an error.</p>\n <script>setTimeout(() => window.close(), 3000);</script>\n </body>\n </html>\n `);\n\n if (this.rejectCallback) {\n this.rejectCallback(new Error(errorMessage));\n }\n return;\n }\n\n // Validate code parameter\n if (!code) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(`\n <html>\n <body>\n <h1>Invalid Callback</h1>\n <p>Missing authorization code</p>\n <script>setTimeout(() => window.close(), 3000);</script>\n </body>\n </html>\n `);\n\n if (this.rejectCallback) {\n this.rejectCallback(new Error('Missing authorization code'));\n }\n return;\n }\n\n // Success - send confirmation page\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(`\n <html>\n <head>\n <meta charset=\"UTF-8\">\n </head>\n <body>\n <h1>Authorization Successful</h1>\n <p>You can close this window and return to the terminal.</p>\n <script>setTimeout(() => window.close(), 2000);</script>\n </body>\n </html>\n `);\n\n // Resolve the promise with authorization code\n if (this.resolveCallback) {\n const result: CallbackResult = { code };\n if (state) {\n result.state = state;\n }\n if (iss) {\n result.iss = iss;\n }\n this.resolveCallback(result);\n }\n }\n\n /**\n * Wait for OAuth callback with timeout\n */\n async waitForCallback(timeoutMs = 300000, signal?: AbortSignal): Promise<CallbackResult> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason instanceof Error ? signal.reason : new Error('OAuth authorization was cancelled'));\n return;\n }\n this.resolveCallback = resolve;\n this.rejectCallback = reject;\n\n const onAbort = () => this.rejectCallback?.(signal?.reason instanceof Error ? signal.reason : new Error('OAuth authorization was cancelled'));\n signal?.addEventListener('abort', onAbort, { once: true });\n\n // Set timeout to prevent hanging forever\n this.timeout = setTimeout(() => {\n this.rejectCallback?.(new Error(`Authorization timeout - no callback received within ${timeoutMs / 1000} seconds`));\n this.stop();\n }, timeoutMs);\n\n const cleanup = () => {\n signal?.removeEventListener('abort', onAbort);\n if (this.timeout) {\n clearTimeout(this.timeout);\n this.timeout = undefined;\n }\n };\n // Promise settlement can originate from callback, abort, or timeout.\n const originalResolve = this.resolveCallback;\n const originalReject = this.rejectCallback;\n this.resolveCallback = (result) => {\n cleanup();\n originalResolve?.(result);\n };\n this.rejectCallback = (error) => {\n cleanup();\n originalReject?.(error);\n };\n });\n }\n\n /**\n * Stop the callback server and close\n */\n async stop(): Promise<void> {\n // Clear the timeout\n if (this.timeout) {\n clearTimeout(this.timeout);\n this.timeout = undefined;\n }\n\n // Close the server\n if (this.server) {\n await new Promise<void>((resolve) => {\n this.server?.close(() => {\n this.logger.debug('🔒 Callback server closed');\n resolve();\n });\n });\n this.server = undefined;\n }\n }\n\n /**\n * Get the callback URL for this server\n */\n getCallbackUrl(): string {\n if (!this.port) {\n throw new Error('Server not started - call start() first');\n }\n return `http://localhost:${this.port}/callback`;\n }\n}\n"],"names":["OAuthCallbackListener","options","port","logger","defaultLogger","start","listen","debug","Promise","resolve","reject","server","http","createServer","req","res","handleRequest","on","error","url","URL","pathname","handleCallback","writeHead","end","code","searchParams","get","state","iss","errorDescription","errorMessage","rejectCallback","Error","resolveCallback","result","waitForCallback","timeoutMs","signal","aborted","reason","onAbort","addEventListener","once","timeout","setTimeout","stop","cleanup","removeEventListener","clearTimeout","undefined","originalResolve","originalReject","close","getCallbackUrl"],"mappings":"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"}
|
|
@@ -10,7 +10,9 @@ import type { AuthorizationServerMetadata, ProtectedResourceMetadata } from './t
|
|
|
10
10
|
* @param resourceUrl - URL of the protected resource (e.g. `https://ai.todoist.net/mcp`).
|
|
11
11
|
* @returns Discovered metadata, or `null` if none is found.
|
|
12
12
|
*/
|
|
13
|
-
export declare function discoverProtectedResourceMetadata(resourceUrl: string
|
|
13
|
+
export declare function discoverProtectedResourceMetadata(resourceUrl: string, options?: {
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
}): Promise<ProtectedResourceMetadata | null>;
|
|
14
16
|
/**
|
|
15
17
|
* Discovers RFC 8414 Authorization Server Metadata at
|
|
16
18
|
* `.well-known/oauth-authorization-server`, path-local variant first.
|
|
@@ -21,6 +23,7 @@ export declare function discoverProtectedResourceMetadata(resourceUrl: string):
|
|
|
21
23
|
*/
|
|
22
24
|
export declare function discoverAuthorizationServerMetadata(authServerUrl: string, options?: {
|
|
23
25
|
allowLoopback?: boolean;
|
|
26
|
+
signal?: AbortSignal;
|
|
24
27
|
}): Promise<AuthorizationServerMetadata | null>;
|
|
25
28
|
/**
|
|
26
29
|
* Discover OAuth Authorization Server Issuer from resource response (RFC 9207)
|
|
@@ -28,4 +31,6 @@ export declare function discoverAuthorizationServerMetadata(authServerUrl: strin
|
|
|
28
31
|
* @param resourceUrl - URL of the protected resource
|
|
29
32
|
* @returns Issuer URL if present in WWW-Authenticate header, null otherwise
|
|
30
33
|
*/
|
|
31
|
-
export declare function discoverAuthorizationServerIssuer(resourceUrl: string
|
|
34
|
+
export declare function discoverAuthorizationServerIssuer(resourceUrl: string, options?: {
|
|
35
|
+
signal?: AbortSignal;
|
|
36
|
+
}): Promise<string | null>;
|
|
@@ -10,7 +10,9 @@ import type { AuthorizationServerMetadata, ProtectedResourceMetadata } from './t
|
|
|
10
10
|
* @param resourceUrl - URL of the protected resource (e.g. `https://ai.todoist.net/mcp`).
|
|
11
11
|
* @returns Discovered metadata, or `null` if none is found.
|
|
12
12
|
*/
|
|
13
|
-
export declare function discoverProtectedResourceMetadata(resourceUrl: string
|
|
13
|
+
export declare function discoverProtectedResourceMetadata(resourceUrl: string, options?: {
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
}): Promise<ProtectedResourceMetadata | null>;
|
|
14
16
|
/**
|
|
15
17
|
* Discovers RFC 8414 Authorization Server Metadata at
|
|
16
18
|
* `.well-known/oauth-authorization-server`, path-local variant first.
|
|
@@ -21,6 +23,7 @@ export declare function discoverProtectedResourceMetadata(resourceUrl: string):
|
|
|
21
23
|
*/
|
|
22
24
|
export declare function discoverAuthorizationServerMetadata(authServerUrl: string, options?: {
|
|
23
25
|
allowLoopback?: boolean;
|
|
26
|
+
signal?: AbortSignal;
|
|
24
27
|
}): Promise<AuthorizationServerMetadata | null>;
|
|
25
28
|
/**
|
|
26
29
|
* Discover OAuth Authorization Server Issuer from resource response (RFC 9207)
|
|
@@ -28,4 +31,6 @@ export declare function discoverAuthorizationServerMetadata(authServerUrl: strin
|
|
|
28
31
|
* @param resourceUrl - URL of the protected resource
|
|
29
32
|
* @returns Issuer URL if present in WWW-Authenticate header, null otherwise
|
|
30
33
|
*/
|
|
31
|
-
export declare function discoverAuthorizationServerIssuer(resourceUrl: string
|
|
34
|
+
export declare function discoverAuthorizationServerIssuer(resourceUrl: string, options?: {
|
|
35
|
+
signal?: AbortSignal;
|
|
36
|
+
}): Promise<string | null>;
|