@cybedefend/vibedefend 1.4.7 → 1.6.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 +77 -2
- package/dist/api/gateway-request.js +49 -0
- package/dist/api/gateway-request.js.map +1 -0
- package/dist/api/projects.js +67 -0
- package/dist/api/projects.js.map +1 -1
- package/dist/api/repository-link.js +157 -0
- package/dist/api/repository-link.js.map +1 -0
- package/dist/auth/auth-store.js +107 -25
- package/dist/auth/auth-store.js.map +1 -1
- package/dist/auth/auth.js +21 -0
- package/dist/auth/auth.js.map +1 -1
- package/dist/clients/cursor.js +5 -1
- package/dist/clients/cursor.js.map +1 -1
- package/dist/clients/vscode.js +38 -29
- package/dist/clients/vscode.js.map +1 -1
- package/dist/commands/mcp/allow.js +219 -0
- package/dist/commands/mcp/allow.js.map +1 -0
- package/dist/commands/mcp/connect.js +337 -0
- package/dist/commands/mcp/connect.js.map +1 -0
- package/dist/commands/mcp/index.js +170 -0
- package/dist/commands/mcp/index.js.map +1 -0
- package/dist/commands/mcp/proxy.js +264 -0
- package/dist/commands/mcp/proxy.js.map +1 -0
- package/dist/commands/mcp/scan.js +349 -0
- package/dist/commands/mcp/scan.js.map +1 -0
- package/dist/commands/mcp/sync.js +215 -0
- package/dist/commands/mcp/sync.js.map +1 -0
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/diagnostics.js +78 -12
- package/dist/diagnostics.js.map +1 -1
- package/dist/doctor.js +19 -0
- package/dist/doctor.js.map +1 -1
- package/dist/guards-evaluator/analysis-types.js +27 -0
- package/dist/guards-evaluator/analysis-types.js.map +1 -0
- package/dist/guards-evaluator/command-analysis.js +572 -0
- package/dist/guards-evaluator/command-analysis.js.map +1 -0
- package/dist/guards-evaluator/command-view.js +586 -0
- package/dist/guards-evaluator/command-view.js.map +1 -0
- package/dist/guards-evaluator/env.js +11 -0
- package/dist/guards-evaluator/env.js.map +1 -0
- package/dist/guards-evaluator/evaluate.js +91 -41
- package/dist/guards-evaluator/evaluate.js.map +1 -1
- package/dist/guards-evaluator/index.js +2 -1
- package/dist/guards-evaluator/index.js.map +1 -1
- package/dist/guards-evaluator/inline-code.js +259 -0
- package/dist/guards-evaluator/inline-code.js.map +1 -0
- package/dist/guards-evaluator/normalize.js +716 -0
- package/dist/guards-evaluator/normalize.js.map +1 -0
- package/dist/guards-evaluator/preprocess.js +13 -5
- package/dist/guards-evaluator/preprocess.js.map +1 -1
- package/dist/guards-evaluator/redact.js +19 -0
- package/dist/guards-evaluator/redact.js.map +1 -1
- package/dist/guards-evaluator/regex.js +25 -3
- package/dist/guards-evaluator/regex.js.map +1 -1
- package/dist/guards-evaluator/safe-commands.js +155 -0
- package/dist/guards-evaluator/safe-commands.js.map +1 -0
- package/dist/guards-evaluator/triage.js +315 -0
- package/dist/guards-evaluator/triage.js.map +1 -0
- package/dist/hook-runner.js +24873 -5068
- package/dist/hooks/install.js +38 -0
- package/dist/hooks/install.js.map +1 -1
- package/dist/hooks/runtime/config.js +38 -0
- package/dist/hooks/runtime/config.js.map +1 -1
- package/dist/hooks/runtime/emit.js +126 -53
- package/dist/hooks/runtime/emit.js.map +1 -1
- package/dist/hooks/runtime/fetch-rules.js +27 -7
- package/dist/hooks/runtime/fetch-rules.js.map +1 -1
- package/dist/hooks/runtime/gateway-http-error.js +34 -0
- package/dist/hooks/runtime/gateway-http-error.js.map +1 -0
- package/dist/hooks/runtime/guard-rules-cache.js +139 -17
- package/dist/hooks/runtime/guard-rules-cache.js.map +1 -1
- package/dist/hooks/runtime/guard-violations-buffer.js +321 -48
- package/dist/hooks/runtime/guard-violations-buffer.js.map +1 -1
- package/dist/hooks/runtime/judge-cache.js +6 -0
- package/dist/hooks/runtime/judge-cache.js.map +1 -1
- package/dist/hooks/runtime/judge-triage.js +193 -0
- package/dist/hooks/runtime/judge-triage.js.map +1 -0
- package/dist/hooks/runtime/judge.js +7 -1
- package/dist/hooks/runtime/judge.js.map +1 -1
- package/dist/hooks/runtime/pre-compact.js +21 -6
- package/dist/hooks/runtime/pre-compact.js.map +1 -1
- package/dist/hooks/runtime/resolve.js +108 -17
- package/dist/hooks/runtime/resolve.js.map +1 -1
- package/dist/hooks/runtime/retry-policy.js +57 -0
- package/dist/hooks/runtime/retry-policy.js.map +1 -0
- package/dist/hooks/runtime/review-sessions.js +74 -0
- package/dist/hooks/runtime/review-sessions.js.map +1 -0
- package/dist/hooks/runtime/script-resolve.js +695 -0
- package/dist/hooks/runtime/script-resolve.js.map +1 -0
- package/dist/hooks/runtime/session-activation.js +226 -0
- package/dist/hooks/runtime/session-activation.js.map +1 -0
- package/dist/hooks/runtime/session-review.js +174 -46
- package/dist/hooks/runtime/session-review.js.map +1 -1
- package/dist/hooks/runtime/session-start.js +35 -4
- package/dist/hooks/runtime/session-start.js.map +1 -1
- package/dist/hooks/runtime/session-writes.js +232 -0
- package/dist/hooks/runtime/session-writes.js.map +1 -0
- package/dist/hooks/runtime/sniff.js +26 -7
- package/dist/hooks/runtime/sniff.js.map +1 -1
- package/dist/hooks/runtime/target-resolve.js +336 -0
- package/dist/hooks/runtime/target-resolve.js.map +1 -0
- package/dist/hooks/runtime/unmapped-tools.js +93 -0
- package/dist/hooks/runtime/unmapped-tools.js.map +1 -0
- package/dist/hooks/runtime/usage-block.js +4 -0
- package/dist/hooks/runtime/usage-block.js.map +1 -1
- package/dist/hooks/runtime/user-prompt-submit.js +8 -2
- package/dist/hooks/runtime/user-prompt-submit.js.map +1 -1
- package/dist/hooks/runtime/wire-text.js +41 -0
- package/dist/hooks/runtime/wire-text.js.map +1 -0
- package/dist/hooks/runtime/work-dir.js +84 -0
- package/dist/hooks/runtime/work-dir.js.map +1 -0
- package/dist/index.js +45 -12
- package/dist/index.js.map +1 -1
- package/dist/install.js +174 -11
- package/dist/install.js.map +1 -1
- package/dist/link.js +74 -48
- package/dist/link.js.map +1 -1
- package/dist/login.js +99 -14
- package/dist/login.js.map +1 -1
- package/dist/mcp/auth/agent-sign-ins.js +174 -0
- package/dist/mcp/auth/agent-sign-ins.js.map +1 -0
- package/dist/mcp/auth/callback.js +200 -0
- package/dist/mcp/auth/callback.js.map +1 -0
- package/dist/mcp/auth/connect.js +392 -0
- package/dist/mcp/auth/connect.js.map +1 -0
- package/dist/mcp/auth/discovery.js +399 -0
- package/dist/mcp/auth/discovery.js.map +1 -0
- package/dist/mcp/auth/oauth.js +326 -0
- package/dist/mcp/auth/oauth.js.map +1 -0
- package/dist/mcp/auth/probe.js +128 -0
- package/dist/mcp/auth/probe.js.map +1 -0
- package/dist/mcp/auth/secrets.js +446 -0
- package/dist/mcp/auth/secrets.js.map +1 -0
- package/dist/mcp/auth/store.js +260 -0
- package/dist/mcp/auth/store.js.map +1 -0
- package/dist/mcp/auth/tokenSource.js +55 -0
- package/dist/mcp/auth/tokenSource.js.map +1 -0
- package/dist/mcp/config/atomicWrite.js +98 -0
- package/dist/mcp/config/atomicWrite.js.map +1 -0
- package/dist/mcp/config/clients.js +171 -0
- package/dist/mcp/config/clients.js.map +1 -0
- package/dist/mcp/config/cybedefend-servers.js +48 -0
- package/dist/mcp/config/cybedefend-servers.js.map +1 -0
- package/dist/mcp/config/discover.js +114 -0
- package/dist/mcp/config/discover.js.map +1 -0
- package/dist/mcp/config/hook.js +123 -0
- package/dist/mcp/config/hook.js.map +1 -0
- package/dist/mcp/config/index.js +11 -0
- package/dist/mcp/config/index.js.map +1 -0
- package/dist/mcp/config/install.js +132 -0
- package/dist/mcp/config/install.js.map +1 -0
- package/dist/mcp/config/installed.js +37 -0
- package/dist/mcp/config/installed.js.map +1 -0
- package/dist/mcp/config/jsonEdit.js +119 -0
- package/dist/mcp/config/jsonEdit.js.map +1 -0
- package/dist/mcp/config/lock.js +102 -0
- package/dist/mcp/config/lock.js.map +1 -0
- package/dist/mcp/config/proxyBinary.js +211 -0
- package/dist/mcp/config/proxyBinary.js.map +1 -0
- package/dist/mcp/config/rewrite.js +388 -0
- package/dist/mcp/config/rewrite.js.map +1 -0
- package/dist/mcp/config/types.js +34 -0
- package/dist/mcp/config/types.js.map +1 -0
- package/dist/mcp/proxy/audit.js +83 -0
- package/dist/mcp/proxy/audit.js.map +1 -0
- package/dist/mcp/proxy/authInterceptor.js +119 -0
- package/dist/mcp/proxy/authInterceptor.js.map +1 -0
- package/dist/mcp/proxy/expand.js +93 -0
- package/dist/mcp/proxy/expand.js.map +1 -0
- package/dist/mcp/proxy/inject.js +397 -0
- package/dist/mcp/proxy/inject.js.map +1 -0
- package/dist/mcp/proxy/intercept.js +37 -0
- package/dist/mcp/proxy/intercept.js.map +1 -0
- package/dist/mcp/proxy/jsonrpc.js +153 -0
- package/dist/mcp/proxy/jsonrpc.js.map +1 -0
- package/dist/mcp/proxy/log.js +109 -0
- package/dist/mcp/proxy/log.js.map +1 -0
- package/dist/mcp/proxy/paths.js +53 -0
- package/dist/mcp/proxy/paths.js.map +1 -0
- package/dist/mcp/proxy/proxy.js +510 -0
- package/dist/mcp/proxy/proxy.js.map +1 -0
- package/dist/mcp/proxy/spec.js +203 -0
- package/dist/mcp/proxy/spec.js.map +1 -0
- package/dist/mcp/proxy/sse.js +77 -0
- package/dist/mcp/proxy/sse.js.map +1 -0
- package/dist/mcp/proxy/upstream.js +2 -0
- package/dist/mcp/proxy/upstream.js.map +1 -0
- package/dist/mcp/proxy/upstreamHttp.js +499 -0
- package/dist/mcp/proxy/upstreamHttp.js.map +1 -0
- package/dist/mcp/proxy/upstreamStdio.js +188 -0
- package/dist/mcp/proxy/upstreamStdio.js.map +1 -0
- package/dist/mcp/proxy/upstreams.js +143 -0
- package/dist/mcp/proxy/upstreams.js.map +1 -0
- package/dist/mcp/proxy/verifyInterceptor.js +559 -0
- package/dist/mcp/proxy/verifyInterceptor.js.map +1 -0
- package/dist/mcp/proxy/windowsCommand.js +99 -0
- package/dist/mcp/proxy/windowsCommand.js.map +1 -0
- package/dist/mcp/scan/args.js +228 -0
- package/dist/mcp/scan/args.js.map +1 -0
- package/dist/mcp/scan/results.js +253 -0
- package/dist/mcp/scan/results.js.map +1 -0
- package/dist/mcp/scan/types.js +40 -0
- package/dist/mcp/scan/types.js.map +1 -0
- package/dist/mcp/verify/api.js +475 -0
- package/dist/mcp/verify/api.js.map +1 -0
- package/dist/mcp/verify/backend.js +143 -0
- package/dist/mcp/verify/backend.js.map +1 -0
- package/dist/mcp/verify/cache.js +182 -0
- package/dist/mcp/verify/cache.js.map +1 -0
- package/dist/mcp/verify/contract-errors.js +198 -0
- package/dist/mcp/verify/contract-errors.js.map +1 -0
- package/dist/mcp/verify/digest.js +91 -0
- package/dist/mcp/verify/digest.js.map +1 -0
- package/dist/mcp/verify/factory.js +56 -0
- package/dist/mcp/verify/factory.js.map +1 -0
- package/dist/mcp/verify/identity.js +173 -0
- package/dist/mcp/verify/identity.js.map +1 -0
- package/dist/mcp/verify/policy-store.js +63 -0
- package/dist/mcp/verify/policy-store.js.map +1 -0
- package/dist/mcp/verify/policy.js +158 -0
- package/dist/mcp/verify/policy.js.map +1 -0
- package/dist/mcp/verify/pool.js +27 -0
- package/dist/mcp/verify/pool.js.map +1 -0
- package/dist/mcp/verify/token-refresh.js +44 -0
- package/dist/mcp/verify/token-refresh.js.map +1 -0
- package/dist/mcp/verify/token.js +67 -0
- package/dist/mcp/verify/token.js.map +1 -0
- package/dist/mcp/verify/verifier.js +1009 -0
- package/dist/mcp/verify/verifier.js.map +1 -0
- package/dist/org-gate.js +1 -1
- package/dist/org-gate.js.map +1 -1
- package/dist/org.js +146 -0
- package/dist/org.js.map +1 -0
- package/dist/project-create.js +129 -0
- package/dist/project-create.js.map +1 -0
- package/dist/prompts.js +21 -0
- package/dist/prompts.js.map +1 -1
- package/dist/re2.wasm +0 -0
- package/dist/status.js +61 -0
- package/dist/status.js.map +1 -1
- package/dist/uninstall.js +49 -4
- package/dist/uninstall.js.map +1 -1
- package/dist/utils.js +62 -6
- package/dist/utils.js.map +1 -1
- package/package.json +10 -3
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth discovery for a third-party MCP server — MCP 2025-06-18, RFC 9728
|
|
3
|
+
* (protected resource metadata), RFC 8414 (authorization server metadata).
|
|
4
|
+
*
|
|
5
|
+
* ─── Why ────────────────────────────────────────────────────────────────────
|
|
6
|
+
*
|
|
7
|
+
* A third-party server's OAuth token belongs to whoever obtained it, not to
|
|
8
|
+
* VibeDefend. There is no shortcut: to talk to an auth-gated server the proxy
|
|
9
|
+
* has to authenticate as itself. The good news is that the spec makes the
|
|
10
|
+
* discovery automatic — the server refuses (401, or 403) and says where its
|
|
11
|
+
* metadata lives, and from there we climb to the endpoints. With no such
|
|
12
|
+
* pointer, the well-known path is derived from the server URL (RFC 9728
|
|
13
|
+
* §3.1). The user types nothing: no URL, no client id.
|
|
14
|
+
*
|
|
15
|
+
* ─── What is refused ────────────────────────────────────────────────────────
|
|
16
|
+
*
|
|
17
|
+
* Everything here is dictated by the server being asked. Four requirements,
|
|
18
|
+
* or we give up:
|
|
19
|
+
* • HTTPS everywhere — a cleartext endpoint would expose the whole
|
|
20
|
+
* authorization flow. The single exception is a loopback address, which
|
|
21
|
+
* no network attacker can reach and which is how a local authorization
|
|
22
|
+
* server (a test one, or a self-hosted gateway) is reachable at all;
|
|
23
|
+
* • PKCE S256 advertised — the ONLY protection the authorization code has
|
|
24
|
+
* for a public client like ours (see `oauth.ts`); never a silent
|
|
25
|
+
* downgrade to `plain`;
|
|
26
|
+
* • the protected resource (PRM) and the authorization server's issuer must
|
|
27
|
+
* DESIGNATE THE SERVER BEING ASKED (RFC 9728 §3.3, RFC 8414 §3.3) — see
|
|
28
|
+
* the mix-up section below;
|
|
29
|
+
* • no redirect followed on OUR OWN requests — a 3xx would move the request
|
|
30
|
+
* to an origin we did not choose to talk to.
|
|
31
|
+
*
|
|
32
|
+
* ─── The mix-up (confused deputy), and why the binding is vital ─────────────
|
|
33
|
+
*
|
|
34
|
+
* The server chooses its metadata URL, the issuer they announce and the
|
|
35
|
+
* resource they name. Unchecked, `evil.example` could return a challenge
|
|
36
|
+
* pointing at ANOTHER service's metadata (Linear, say): VibeDefend would
|
|
37
|
+
* climb that third party's AUTHENTIC chain, open its real consent page — which
|
|
38
|
+
* the user would approve in good faith — exchange the code there, and file a
|
|
39
|
+
* REAL third-party token under `evil.example`'s identity. The next request
|
|
40
|
+
* would then present it as `Authorization: Bearer`… to `evil.example`. A
|
|
41
|
+
* `.mcp.json` in a cloned repository is enough to pick that server, which is
|
|
42
|
+
* this file's reason to exist.
|
|
43
|
+
*
|
|
44
|
+
* Hence: the metadata URL must be on the server's origin, the `resource` they
|
|
45
|
+
* announce must canonicalise to the server, and the authorization server's
|
|
46
|
+
* `issuer` must designate itself (RFC 8414 §3.3).
|
|
47
|
+
*
|
|
48
|
+
* An accepted limit: a server may legitimately delegate its authorization to
|
|
49
|
+
* a third-party issuer, so we cannot require the `issuer` to be on the
|
|
50
|
+
* server's origin. What remains is the `resource` parameter (RFC 8707), which
|
|
51
|
+
* the exchange (`oauth.ts`) ALWAYS sets to the server being asked: a
|
|
52
|
+
* conforming issuer refuses a resource that is not its own
|
|
53
|
+
* (`invalid_target`). That last line of defence depends on the third party,
|
|
54
|
+
* not on us.
|
|
55
|
+
*
|
|
56
|
+
* ─── A check that was tried and removed, deliberately ───────────────────────
|
|
57
|
+
*
|
|
58
|
+
* Binding `authorization_endpoint` / `token_endpoint` /
|
|
59
|
+
* `registration_endpoint` to the issuer's origin looks attractive and is not
|
|
60
|
+
* done, for two reasons that together condemn it:
|
|
61
|
+
*
|
|
62
|
+
* 1. It closes the INSTANCE, not the CLASS — the attacker simply hosts a
|
|
63
|
+
* 302 on their OWN `authorization_endpoint`: the browser lands on the
|
|
64
|
+
* real consent page of a genuine third party, with THEIR `client_id` and
|
|
65
|
+
* OUR `code_challenge`. The check structurally cannot see it: that
|
|
66
|
+
* endpoint opens in the BROWSER, we never fetch it, and `redirect:
|
|
67
|
+
* 'manual'` does not apply to it.
|
|
68
|
+
* 2. It refuses PERFECTLY CONFORMING deployments: AWS Cognito (issuer
|
|
69
|
+
* `cognito-idp.<region>.amazonaws.com/<pool>`, endpoints
|
|
70
|
+
* `<domain>.auth.<region>.amazoncognito.com` — the pattern AWS
|
|
71
|
+
* recommends for MCP) and Google (`accounts.google.com` /
|
|
72
|
+
* `oauth2.googleapis.com`) would come back `unusable`. Neither RFC 8414
|
|
73
|
+
* nor the MCP spec requires co-origin, and `amazonaws.com` /
|
|
74
|
+
* `amazoncognito.com` are two distinct registrable domains — no
|
|
75
|
+
* loosening ("same parent domain") saves the rule without emptying it.
|
|
76
|
+
*
|
|
77
|
+
* The structural difference with the PRM ↔ server binding, which IS kept: that
|
|
78
|
+
* one anchors on a TRUSTED value, the URL the USER chose (`serverUrl`). The
|
|
79
|
+
* removed check anchored on a value CHOSEN BY THE ATTACKER — the issuer they
|
|
80
|
+
* designate themselves — which they only have to be self-consistent to
|
|
81
|
+
* satisfy.
|
|
82
|
+
*
|
|
83
|
+
* What closes the class: `state` (anti-CSRF) plus `iss` (RFC 9207, the issuer
|
|
84
|
+
* announced in the authorization response itself, compared to the expected
|
|
85
|
+
* one), both enforced by `callback.ts` on the BROWSER's response rather than
|
|
86
|
+
* on a document the attacker controls.
|
|
87
|
+
*/
|
|
88
|
+
import { pkg } from '../../version.js';
|
|
89
|
+
import { canonicalMcpUrl } from './oauth.js';
|
|
90
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
91
|
+
/**
|
|
92
|
+
* Common options for every discovery request.
|
|
93
|
+
*
|
|
94
|
+
* `redirect: 'manual'` is not cosmetic: without it a 3xx would be enough to
|
|
95
|
+
* have us read metadata on an origin we never chose to ask, and the checks
|
|
96
|
+
* below would be about the URL REQUESTED rather than the one that answered.
|
|
97
|
+
*/
|
|
98
|
+
function requestInit(signal, extra = {}) {
|
|
99
|
+
return { redirect: 'manual', signal, ...extra };
|
|
100
|
+
}
|
|
101
|
+
const LOOPBACK_HOSTS = new Set(['127.0.0.1', 'localhost', '::1', '[::1]']);
|
|
102
|
+
/**
|
|
103
|
+
* A usable URL, or `null`.
|
|
104
|
+
*
|
|
105
|
+
* https, or http on a loopback address. The loopback exception is RFC 8252's
|
|
106
|
+
* reasoning applied to the other side of the flow: a listener on 127.0.0.1 is
|
|
107
|
+
* not reachable by a network attacker, so cleartext buys them nothing, and
|
|
108
|
+
* refusing it would make a self-hosted or local authorization server
|
|
109
|
+
* impossible to talk to — including the mock one the tests run against.
|
|
110
|
+
*/
|
|
111
|
+
function usableUrl(value) {
|
|
112
|
+
if (typeof value !== 'string' || !value.trim())
|
|
113
|
+
return null;
|
|
114
|
+
let parsed;
|
|
115
|
+
try {
|
|
116
|
+
parsed = new URL(value);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
if (parsed.protocol === 'https:')
|
|
122
|
+
return value;
|
|
123
|
+
if (parsed.protocol === 'http:' && LOOPBACK_HOSTS.has(parsed.hostname.toLowerCase())) {
|
|
124
|
+
return value;
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
/** `scheme//host[:port]` of the server, or `null`. */
|
|
129
|
+
function mcpOrigin(url) {
|
|
130
|
+
const canonical = canonicalMcpUrl(url);
|
|
131
|
+
if (!canonical)
|
|
132
|
+
return null;
|
|
133
|
+
const pathStart = canonical.indexOf('/', canonical.indexOf('//') + 2);
|
|
134
|
+
return pathStart === -1 ? canonical : canonical.slice(0, pathStart);
|
|
135
|
+
}
|
|
136
|
+
/** Do two URLs designate the SAME MCP server? */
|
|
137
|
+
function sameMcpServer(a, b) {
|
|
138
|
+
const ca = canonicalMcpUrl(a);
|
|
139
|
+
return ca !== null && ca === canonicalMcpUrl(b);
|
|
140
|
+
}
|
|
141
|
+
/** A comparable issuer identifier — RFC 8414 tolerates the trailing slash. */
|
|
142
|
+
function issuerId(value) {
|
|
143
|
+
const url = usableUrl(value);
|
|
144
|
+
return url ? url.replace(/\/+$/, '') : null;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The metadata URL announced by the `WWW-Authenticate` challenge, or `null`.
|
|
148
|
+
*
|
|
149
|
+
* The challenge comes from the server being asked, so two requirements:
|
|
150
|
+
* • a usable scheme — following an `http://` on a public host would expose
|
|
151
|
+
* the rest of the flow to anyone on the path;
|
|
152
|
+
* • the SAME ORIGIN as `serverUrl` — a protected resource's metadata is read
|
|
153
|
+
* from IT (RFC 9728 §3). A server pointing them at a third party is
|
|
154
|
+
* trying to make us go looking for that third party's token; see the
|
|
155
|
+
* mix-up section at the top of this file.
|
|
156
|
+
*
|
|
157
|
+
* `serverUrl` is not optional: without it a caller could forget the binding,
|
|
158
|
+
* which is exactly what made the mix-up possible.
|
|
159
|
+
*/
|
|
160
|
+
export function parseResourceMetadataUrl(header, serverUrl) {
|
|
161
|
+
if (!header)
|
|
162
|
+
return null;
|
|
163
|
+
const m = /resource_metadata\s*=\s*"?([^",\s]+)"?/i.exec(header);
|
|
164
|
+
const url = m ? usableUrl(m[1]) : null;
|
|
165
|
+
if (!url)
|
|
166
|
+
return null;
|
|
167
|
+
const expected = mcpOrigin(serverUrl);
|
|
168
|
+
return expected && mcpOrigin(url) === expected ? url : null;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* The well-known path derived from the server URL (RFC 9728 §3.1), or `null`.
|
|
172
|
+
*
|
|
173
|
+
* A server may refuse without carrying a `WWW-Authenticate`; no RFC makes it
|
|
174
|
+
* mandatory. This path is derived from the server ASKED, so it is bound to it
|
|
175
|
+
* by construction and does not reopen the door the origin check just closed.
|
|
176
|
+
*/
|
|
177
|
+
function derivedResourceMetadataUrl(serverUrl) {
|
|
178
|
+
const canonical = canonicalMcpUrl(serverUrl);
|
|
179
|
+
const origin = mcpOrigin(serverUrl);
|
|
180
|
+
if (!canonical || !origin || !usableUrl(origin))
|
|
181
|
+
return null;
|
|
182
|
+
// `https://host/mcp` → `https://host/.well-known/oauth-protected-resource/mcp`
|
|
183
|
+
return `${origin}/.well-known/oauth-protected-resource${canonical.slice(origin.length)}`;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Every place an authorization server may publish its metadata, in the order
|
|
187
|
+
* they are tried.
|
|
188
|
+
*
|
|
189
|
+
* RFC 8414 §3.1 inserts the well-known segment BETWEEN the host and the
|
|
190
|
+
* issuer's path (`https://host/.well-known/oauth-authorization-server/tenant`),
|
|
191
|
+
* while OpenID Connect Discovery appends it (`https://host/tenant/.well-known/
|
|
192
|
+
* openid-configuration`). Real deployments implement one, the other, or both:
|
|
193
|
+
* Auth0 and Okta answer the OIDC form, Keycloak answers the RFC 8414 form
|
|
194
|
+
* under its realm path, and an AS with no path at all answers either. Trying
|
|
195
|
+
* a single shape is why a conforming tenant-scoped issuer came back
|
|
196
|
+
* `unusable`.
|
|
197
|
+
*/
|
|
198
|
+
export function authorizationServerMetadataUrls(issuer) {
|
|
199
|
+
let parsed;
|
|
200
|
+
try {
|
|
201
|
+
parsed = new URL(issuer);
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
const origin = `${parsed.protocol}//${parsed.host}`;
|
|
207
|
+
const path = parsed.pathname.replace(/\/+$/, '');
|
|
208
|
+
if (path === '') {
|
|
209
|
+
return [
|
|
210
|
+
`${origin}/.well-known/oauth-authorization-server`,
|
|
211
|
+
`${origin}/.well-known/openid-configuration`,
|
|
212
|
+
];
|
|
213
|
+
}
|
|
214
|
+
return [
|
|
215
|
+
`${origin}/.well-known/oauth-authorization-server${path}`,
|
|
216
|
+
// The appended form is not in RFC 8414, but it is what earlier clients
|
|
217
|
+
// asked for and what a great many deployments still serve — AWS Cognito
|
|
218
|
+
// among them. Dropping it turns a working tenant-scoped issuer into
|
|
219
|
+
// `unusable`, which is a regression dressed as strictness.
|
|
220
|
+
`${origin}${path}/.well-known/oauth-authorization-server`,
|
|
221
|
+
`${origin}/.well-known/openid-configuration${path}`,
|
|
222
|
+
`${origin}${path}/.well-known/openid-configuration`,
|
|
223
|
+
];
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Climbs the discovery chain to the endpoints. NEVER throws: a silent server,
|
|
227
|
+
* a 404 on the metadata or a dead network are ordinary outcomes, not
|
|
228
|
+
* exceptions. `reason` tells the caller what to do next — "leave this server
|
|
229
|
+
* alone" (`no-oauth`) is not the same decision as "report a problem"
|
|
230
|
+
* (`unusable`, `closed-door`, `unreachable`).
|
|
231
|
+
*/
|
|
232
|
+
export async function discoverMcpOAuthOutcome(serverUrl, deps = {}) {
|
|
233
|
+
const fail = (reason) => ({
|
|
234
|
+
endpoints: null,
|
|
235
|
+
reason,
|
|
236
|
+
});
|
|
237
|
+
const f = deps.fetchImpl ?? globalThis.fetch;
|
|
238
|
+
const timeout = deps.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
239
|
+
const signal = () => AbortSignal.timeout(timeout);
|
|
240
|
+
try {
|
|
241
|
+
// 1. Get the challenge, unless the caller already has one.
|
|
242
|
+
let status;
|
|
243
|
+
let header;
|
|
244
|
+
if (deps.knownChallenge) {
|
|
245
|
+
status = deps.knownChallenge.status;
|
|
246
|
+
header = deps.knownChallenge.wwwAuthenticate;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
// An unauthenticated request is enough, and it has the same shape as
|
|
250
|
+
// the real handshake so that a server inspecting `clientInfo` sees the
|
|
251
|
+
// same identity in both places.
|
|
252
|
+
const challenge = await f(serverUrl, requestInit(signal(), {
|
|
253
|
+
method: 'POST',
|
|
254
|
+
headers: {
|
|
255
|
+
'Content-Type': 'application/json',
|
|
256
|
+
Accept: 'application/json, text/event-stream',
|
|
257
|
+
},
|
|
258
|
+
body: JSON.stringify({
|
|
259
|
+
jsonrpc: '2.0',
|
|
260
|
+
id: 1,
|
|
261
|
+
method: 'initialize',
|
|
262
|
+
params: {
|
|
263
|
+
protocolVersion: '2025-06-18',
|
|
264
|
+
capabilities: {},
|
|
265
|
+
clientInfo: { name: pkg.name, version: pkg.version },
|
|
266
|
+
},
|
|
267
|
+
}),
|
|
268
|
+
}));
|
|
269
|
+
status = challenge.status;
|
|
270
|
+
header = challenge.headers.get('www-authenticate');
|
|
271
|
+
}
|
|
272
|
+
// Neither 401 nor 403: this server refuses nothing, there is no OAuth to
|
|
273
|
+
// discover. The 403 counts — a real MCP server was measured closing its
|
|
274
|
+
// door that way, and reading only the 401 would condemn it to never being
|
|
275
|
+
// authorized.
|
|
276
|
+
if (status !== 401 && status !== 403)
|
|
277
|
+
return fail('no-oauth');
|
|
278
|
+
// Bound to the origin of the server being asked, see the module header.
|
|
279
|
+
const announced = parseResourceMetadataUrl(header, serverUrl);
|
|
280
|
+
// A challenge that POINTS AT metadata we refuse is a hostile signal, not
|
|
281
|
+
// an oversight: we do not go looking elsewhere to be accommodating.
|
|
282
|
+
if (!announced && header && /resource_metadata/i.test(header))
|
|
283
|
+
return fail('unusable');
|
|
284
|
+
const resource = usableUrl(canonicalMcpUrl(serverUrl));
|
|
285
|
+
// Never the URL as received: `httpsUrl(serverUrl)` returned the string
|
|
286
|
+
// VERBATIM, query string included, so a hosted server's identifier
|
|
287
|
+
// (`?api_key=`) travelled as the `resource=` parameter of the
|
|
288
|
+
// authorization URL — into the user's BROWSER, their history, and the
|
|
289
|
+
// access logs of a THIRD-PARTY authorization server named by the MCP
|
|
290
|
+
// server itself — then into the POST to the token endpoint, then
|
|
291
|
+
// persisted. `canonicalMcpUrl` is also exactly what RFC 8707 expects of a
|
|
292
|
+
// resource identifier (scheme, host, path; neither query nor fragment),
|
|
293
|
+
// so closing the leak and conforming are the same gesture here.
|
|
294
|
+
if (!resource)
|
|
295
|
+
return fail('unusable');
|
|
296
|
+
// 2. Protected resource metadata (RFC 9728).
|
|
297
|
+
const prmUrl = announced ?? derivedResourceMetadataUrl(serverUrl);
|
|
298
|
+
const prm = prmUrl ? await readJson(f, prmUrl, signal()) : null;
|
|
299
|
+
let issuer;
|
|
300
|
+
let scopesSupported;
|
|
301
|
+
if (prm) {
|
|
302
|
+
// The resource announced must be THE ONE WE ARE ASKING (RFC 9728 §3.3).
|
|
303
|
+
// Otherwise we would go and negotiate a token at a third party and bring
|
|
304
|
+
// it back here — the heart of the mix-up.
|
|
305
|
+
const declared = typeof prm.resource === 'string' ? prm.resource : null;
|
|
306
|
+
if (declared && !sameMcpServer(declared, serverUrl))
|
|
307
|
+
return fail('unusable');
|
|
308
|
+
// "Nothing announced" (`closed-door`, no advice beyond "this server is
|
|
309
|
+
// shut") and "something IS announced but we refuse it" (`unusable`) are
|
|
310
|
+
// different outcomes; merging them would have the caller give the wrong
|
|
311
|
+
// advice.
|
|
312
|
+
const servers = Array.isArray(prm.authorization_servers) ? prm.authorization_servers : [];
|
|
313
|
+
if (servers.length === 0)
|
|
314
|
+
return fail('closed-door');
|
|
315
|
+
issuer = issuerId(servers[0]);
|
|
316
|
+
if (!issuer)
|
|
317
|
+
return fail('unusable');
|
|
318
|
+
scopesSupported = readScopes(prm.scopes_supported);
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
// No PRM. Plenty of MCP servers publish RFC 8414 metadata on their own
|
|
322
|
+
// origin and skip RFC 9728 entirely; treating that as a closed door
|
|
323
|
+
// would refuse to log into servers that are perfectly usable. The
|
|
324
|
+
// fallback issuer is the server's OWN origin, which is a trusted value
|
|
325
|
+
// — the URL the user chose — so it does not reopen the mix-up.
|
|
326
|
+
issuer = issuerId(mcpOrigin(serverUrl));
|
|
327
|
+
if (!issuer)
|
|
328
|
+
return fail('closed-door');
|
|
329
|
+
scopesSupported = [];
|
|
330
|
+
}
|
|
331
|
+
// 3. Authorization server metadata (RFC 8414), at whichever of the
|
|
332
|
+
// standard locations this deployment uses.
|
|
333
|
+
let as = null;
|
|
334
|
+
for (const candidate of authorizationServerMetadataUrls(issuer)) {
|
|
335
|
+
as = await readJson(f, candidate, signal());
|
|
336
|
+
if (as)
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
if (!as)
|
|
340
|
+
return fail(prm ? 'unusable' : 'closed-door');
|
|
341
|
+
// RFC 8414 §3.3: the `issuer` returned must be identical to the one we
|
|
342
|
+
// inserted the well-known path into. Second link of the anti mix-up chain:
|
|
343
|
+
// without it, a document served by another issuer would pass for the one
|
|
344
|
+
// we aimed at.
|
|
345
|
+
if (issuerId(as.issuer) !== issuer)
|
|
346
|
+
return fail('unusable');
|
|
347
|
+
const authorizationEndpoint = usableUrl(as.authorization_endpoint);
|
|
348
|
+
const tokenEndpoint = usableUrl(as.token_endpoint);
|
|
349
|
+
if (!authorizationEndpoint || !tokenEndpoint)
|
|
350
|
+
return fail('unusable');
|
|
351
|
+
// Treated uniformly with the two above: usable or absent, nothing else,
|
|
352
|
+
// never a special case for this optional field.
|
|
353
|
+
const registrationEndpoint = usableUrl(as.registration_endpoint);
|
|
354
|
+
// PKCE S256 required. Never `plain`: a public client without S256 has NO
|
|
355
|
+
// protection left on the code exchange.
|
|
356
|
+
const methods = Array.isArray(as.code_challenge_methods_supported)
|
|
357
|
+
? as.code_challenge_methods_supported
|
|
358
|
+
: [];
|
|
359
|
+
if (!methods.includes('S256'))
|
|
360
|
+
return fail('unusable');
|
|
361
|
+
return {
|
|
362
|
+
endpoints: {
|
|
363
|
+
authorizationEndpoint,
|
|
364
|
+
tokenEndpoint,
|
|
365
|
+
registrationEndpoint,
|
|
366
|
+
resource,
|
|
367
|
+
issuer,
|
|
368
|
+
issParameterSupported: as.authorization_response_iss_parameter_supported === true,
|
|
369
|
+
scopesSupported,
|
|
370
|
+
},
|
|
371
|
+
reason: 'ok',
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
return fail('unreachable');
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
/** A metadata document, or `null` when it is absent or unreadable. */
|
|
379
|
+
async function readJson(f, url, signal) {
|
|
380
|
+
try {
|
|
381
|
+
const res = await f(url, requestInit(signal));
|
|
382
|
+
if (!res.ok)
|
|
383
|
+
return null;
|
|
384
|
+
return (await res.json());
|
|
385
|
+
}
|
|
386
|
+
catch {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* The scopes the PRM advertises, filtered to non-empty strings. An absent or
|
|
392
|
+
* malformed value means "none", never an exception.
|
|
393
|
+
*/
|
|
394
|
+
function readScopes(value) {
|
|
395
|
+
if (!Array.isArray(value))
|
|
396
|
+
return [];
|
|
397
|
+
return value.filter((v) => typeof v === 'string' && v.trim().length > 0);
|
|
398
|
+
}
|
|
399
|
+
//# sourceMappingURL=discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../../src/mcp/auth/discovery.ts"],"names":[],"mappings":"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"}
|