@ni-c/mcp-hub 0.9.2 → 0.11.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/CHANGELOG.md +619 -0
- package/README.md +189 -64
- package/dist/admin.js +232 -8
- package/dist/admin.js.map +1 -1
- package/dist/auth/address.js +89 -0
- package/dist/auth/address.js.map +1 -0
- package/dist/auth/api-tokens.js +27 -0
- package/dist/auth/api-tokens.js.map +1 -0
- package/dist/auth/cimd.js +367 -0
- package/dist/auth/cimd.js.map +1 -0
- package/dist/auth/consent-page.js +4 -7
- package/dist/auth/consent-page.js.map +1 -1
- package/dist/auth/headers.js +22 -1
- package/dist/auth/headers.js.map +1 -1
- package/dist/auth/login-page.js +4 -7
- package/dist/auth/login-page.js.map +1 -1
- package/dist/auth/oidc/adapter.js +135 -0
- package/dist/auth/oidc/adapter.js.map +1 -0
- package/dist/auth/oidc/interactions.js +187 -0
- package/dist/auth/oidc/interactions.js.map +1 -0
- package/dist/auth/oidc/mount.js +144 -0
- package/dist/auth/oidc/mount.js.map +1 -0
- package/dist/auth/oidc/provider.js +440 -0
- package/dist/auth/oidc/provider.js.map +1 -0
- package/dist/auth/oidc/quirks.js +234 -0
- package/dist/auth/oidc/quirks.js.map +1 -0
- package/dist/auth/oidc/verifier.js +121 -0
- package/dist/auth/oidc/verifier.js.map +1 -0
- package/dist/auth/page.js +22 -0
- package/dist/auth/page.js.map +1 -1
- package/dist/auth/pinned-fetch.js +129 -0
- package/dist/auth/pinned-fetch.js.map +1 -0
- package/dist/auth/protected-resource.js +41 -0
- package/dist/auth/protected-resource.js.map +1 -0
- package/dist/auth/rate-limit.js +156 -0
- package/dist/auth/rate-limit.js.map +1 -0
- package/dist/auth/redirect-uri.js +90 -0
- package/dist/auth/redirect-uri.js.map +1 -0
- package/dist/auth/registration.js +146 -0
- package/dist/auth/registration.js.map +1 -0
- package/dist/auth/session.js +43 -0
- package/dist/auth/session.js.map +1 -0
- package/dist/auth/signed-token.js +49 -0
- package/dist/auth/signed-token.js.map +1 -0
- package/dist/auth/store.js +516 -5
- package/dist/auth/store.js.map +1 -1
- package/dist/auth/text.js +51 -0
- package/dist/auth/text.js.map +1 -0
- package/dist/config.js +184 -5
- package/dist/config.js.map +1 -1
- package/dist/docker-proxy/policy.js +1 -0
- package/dist/docker-proxy/policy.js.map +1 -1
- package/dist/docker-proxy/server.js +1 -0
- package/dist/docker-proxy/server.js.map +1 -1
- package/dist/elicitation.js +0 -0
- package/dist/elicitation.js.map +1 -0
- package/dist/forward.js +0 -0
- package/dist/forward.js.map +1 -0
- package/dist/health.js +22 -3
- package/dist/health.js.map +1 -1
- package/dist/hub.js +337 -29
- package/dist/hub.js.map +1 -1
- package/dist/index.js +218 -23
- package/dist/index.js.map +1 -1
- package/dist/limits.js +13 -1
- package/dist/limits.js.map +1 -1
- package/dist/mcp-limits.js +14 -2
- package/dist/mcp-limits.js.map +1 -1
- package/dist/proxy.js +262 -34
- package/dist/proxy.js.map +1 -1
- package/dist/stdio.js +97 -7
- package/dist/stdio.js.map +1 -1
- package/dist/subscriptions.js +236 -0
- package/dist/subscriptions.js.map +1 -0
- package/dist/supervisor.js +472 -28
- package/dist/supervisor.js.map +1 -1
- package/dist/timings.js +61 -0
- package/dist/timings.js.map +1 -0
- package/dist/tool-filter.js +59 -0
- package/dist/tool-filter.js.map +1 -0
- package/dist/transports/docker.js.map +1 -1
- package/dist/transports/stream.js +33 -20
- package/dist/transports/stream.js.map +1 -1
- package/dist/upstream/auth.js +435 -0
- package/dist/upstream/auth.js.map +1 -0
- package/dist/upstream/login.js +94 -0
- package/dist/upstream/login.js.map +1 -0
- package/dist/upstream/provider.js +286 -0
- package/dist/upstream/provider.js.map +1 -0
- package/dist/upstream/routes.js +97 -0
- package/dist/upstream/routes.js.map +1 -0
- package/package.json +23 -7
- package/dist/auth/provider.js +0 -380
- package/dist/auth/provider.js.map +0 -1
- package/dist/auth/routes.js +0 -223
- package/dist/auth/routes.js.map +0 -1
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import { isPrivateAddress, resolvePublicAddress } from './address.js';
|
|
2
|
+
import { guardedRequest } from './pinned-fetch.js';
|
|
3
|
+
import { isSafeRedirectUri } from './redirect-uri.js';
|
|
4
|
+
import { clampDisplayName, logSafe } from './text.js';
|
|
5
|
+
// Lives in address.ts now — re-exported because it is part of this module's
|
|
6
|
+
// established surface and the test suite reaches for it here.
|
|
7
|
+
export { isPrivateAddress };
|
|
8
|
+
/**
|
|
9
|
+
* OAuth Client ID Metadata Documents (CIMD,
|
|
10
|
+
* draft-ietf-oauth-client-id-metadata-document-00), the registration mechanism
|
|
11
|
+
* the MCP specification prefers over RFC 7591 dynamic registration.
|
|
12
|
+
*
|
|
13
|
+
* The client hosts its own metadata at a stable HTTPS URL and uses that URL as
|
|
14
|
+
* its client_id; the authorization server fetches and validates the document
|
|
15
|
+
* instead of handing out credentials. Nothing is persisted here — the document
|
|
16
|
+
* is the source of truth and is re-fetched when the cache entry expires, so a
|
|
17
|
+
* client that changes its redirect URIs does not need to re-register.
|
|
18
|
+
*
|
|
19
|
+
* Everything in this file runs against a URL an unauthenticated caller chose,
|
|
20
|
+
* so the fetch is treated as hostile: no redirects, no private addresses, the
|
|
21
|
+
* checked address pinned into the connection, a hard size cap and a short
|
|
22
|
+
* timeout.
|
|
23
|
+
*/
|
|
24
|
+
/** Draft §6.6 recommends 5 kB; anything larger is not a client metadata document. */
|
|
25
|
+
const MAX_DOCUMENT_BYTES = 5 * 1024;
|
|
26
|
+
/** A key set is legitimately larger than a metadata document, but not by much:
|
|
27
|
+
* a handful of RSA keys is a couple of kilobytes. */
|
|
28
|
+
const MAX_JWKS_BYTES = 64 * 1024;
|
|
29
|
+
const FETCH_TIMEOUT_MS = 5_000;
|
|
30
|
+
const MAX_CACHE_ENTRIES = 200;
|
|
31
|
+
const MIN_TTL_MS = 60_000;
|
|
32
|
+
const MAX_TTL_MS = 24 * 3600_000;
|
|
33
|
+
const DEFAULT_TTL_MS = 3600_000;
|
|
34
|
+
/** How long a rejected client_id is remembered, so a bad URL cannot be used to
|
|
35
|
+
* make the hub hammer a third party once per authorization request. */
|
|
36
|
+
const NEGATIVE_TTL_MS = 30_000;
|
|
37
|
+
/**
|
|
38
|
+
* The per-URL negative cache alone does not bound how often one host can be
|
|
39
|
+
* fetched: the query string is part of the client_id, so `?n=1`, `?n=2` and so
|
|
40
|
+
* on are all distinct identifiers pointing at the same server. Failures are
|
|
41
|
+
* therefore also counted per origin.
|
|
42
|
+
*/
|
|
43
|
+
const MAX_ORIGIN_FAILURES = 10;
|
|
44
|
+
const ORIGIN_FAILURE_WINDOW_MS = 30_000;
|
|
45
|
+
const MAX_TRACKED_ORIGINS = 200;
|
|
46
|
+
const ASSERTION_SAFE_METHODS = new Set(['none', 'private_key_jwt']);
|
|
47
|
+
let fetchOverride;
|
|
48
|
+
/**
|
|
49
|
+
* Tests serve their metadata documents from a stub here. Client IDs are https
|
|
50
|
+
* URLs by specification and stay that way — the stub is what lets the suite
|
|
51
|
+
* exercise the real fetch path without a certificate authority. Passing
|
|
52
|
+
* undefined restores the global fetch.
|
|
53
|
+
*/
|
|
54
|
+
export function setCimdFetch(fn) {
|
|
55
|
+
fetchOverride = fn;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Draft §4.1: the identifier must be an https URL with a path component, no
|
|
59
|
+
* fragment, no credentials and no dot-segments. A `client_id` that fails this
|
|
60
|
+
* is not a metadata document URL at all and is looked up as a locally
|
|
61
|
+
* registered client instead — which is what keeps DCR working beside CIMD.
|
|
62
|
+
*/
|
|
63
|
+
export function isClientIdMetadataUrl(clientId) {
|
|
64
|
+
let url;
|
|
65
|
+
try {
|
|
66
|
+
url = new URL(clientId);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
if (url.protocol !== 'https:')
|
|
72
|
+
return false;
|
|
73
|
+
if (url.hash || url.username || url.password)
|
|
74
|
+
return false;
|
|
75
|
+
if (url.pathname === '' || url.pathname === '/')
|
|
76
|
+
return false;
|
|
77
|
+
// Dot segments are checked against the string the client sent, not the
|
|
78
|
+
// parsed URL: the URL parser resolves `/a/../b` away, and a percent-encoded
|
|
79
|
+
// `%2e%2e` survives parsing entirely while still traversing on the server
|
|
80
|
+
// that ultimately serves the document.
|
|
81
|
+
const rawPath = clientId.slice(url.origin.length).split(/[?#]/)[0];
|
|
82
|
+
return !rawPath.split('/').some(segment => /^(?:\.|%2e){1,2}$/i.test(segment));
|
|
83
|
+
}
|
|
84
|
+
export class CimdResolver {
|
|
85
|
+
options;
|
|
86
|
+
cache = new Map();
|
|
87
|
+
/** Collapses concurrent lookups of the same client_id into one fetch. */
|
|
88
|
+
inFlight = new Map();
|
|
89
|
+
/** Recent failures per origin, so distinct client_ids on one host cannot be
|
|
90
|
+
* used to keep fetching it. */
|
|
91
|
+
originFailures = new Map();
|
|
92
|
+
allowedOrigins;
|
|
93
|
+
constructor(options = {}) {
|
|
94
|
+
this.options = options;
|
|
95
|
+
this.allowedOrigins = new Set(options.allowedOrigins ?? []);
|
|
96
|
+
}
|
|
97
|
+
/** Undefined for every rejection; the reason is logged, never returned to
|
|
98
|
+
* the client, so an attacker cannot map out the admission policy. */
|
|
99
|
+
async resolve(clientId) {
|
|
100
|
+
const cached = this.cache.get(clientId);
|
|
101
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
102
|
+
// Refresh recency so the eviction below drops genuinely cold entries.
|
|
103
|
+
this.cache.delete(clientId);
|
|
104
|
+
this.cache.set(clientId, cached);
|
|
105
|
+
return cached.client;
|
|
106
|
+
}
|
|
107
|
+
const pending = this.inFlight.get(clientId);
|
|
108
|
+
if (pending)
|
|
109
|
+
return pending;
|
|
110
|
+
const promise = this.load(clientId).finally(() => this.inFlight.delete(clientId));
|
|
111
|
+
this.inFlight.set(clientId, promise);
|
|
112
|
+
return promise;
|
|
113
|
+
}
|
|
114
|
+
async load(clientId) {
|
|
115
|
+
try {
|
|
116
|
+
const url = new URL(clientId);
|
|
117
|
+
if (this.allowedOrigins.size > 0 && !this.allowedOrigins.has(url.origin)) {
|
|
118
|
+
return this.reject(clientId, `origin ${url.origin} is not in CIMD_ALLOWED_ORIGINS`);
|
|
119
|
+
}
|
|
120
|
+
if (this.originIsExhausted(url.origin)) {
|
|
121
|
+
// Counting this one too would keep extending the window for as long as
|
|
122
|
+
// the flood lasts, so the block could never lift.
|
|
123
|
+
return this.reject(clientId, `origin ${url.origin} failed too often recently`, false);
|
|
124
|
+
}
|
|
125
|
+
const pinned = await this.assertPublicHost(url.hostname);
|
|
126
|
+
const { document, ttl } = await this.fetchDocument(url, pinned);
|
|
127
|
+
const client = validateDocument(document, clientId);
|
|
128
|
+
this.remember(clientId, client, ttl);
|
|
129
|
+
console.log(`mcp-hub: fetched client metadata document ${logSafe(clientId)} (${logSafe(client.client_name ?? 'unnamed')})`);
|
|
130
|
+
return client;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
return this.reject(clientId, error.message);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
reject(clientId, reason, countAgainstOrigin = true) {
|
|
137
|
+
console.warn(`mcp-hub: refused client metadata document ${logSafe(clientId)}: ${logSafe(reason)}`);
|
|
138
|
+
this.remember(clientId, undefined, NEGATIVE_TTL_MS);
|
|
139
|
+
if (countAgainstOrigin)
|
|
140
|
+
this.recordOriginFailure(clientId);
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
/** True once one origin has failed often enough that further lookups are
|
|
144
|
+
* refused without a request until the window passes. */
|
|
145
|
+
originIsExhausted(origin) {
|
|
146
|
+
const entry = this.originFailures.get(origin);
|
|
147
|
+
if (!entry || entry.resetAt <= Date.now())
|
|
148
|
+
return false;
|
|
149
|
+
return entry.count >= MAX_ORIGIN_FAILURES;
|
|
150
|
+
}
|
|
151
|
+
recordOriginFailure(clientId) {
|
|
152
|
+
let origin;
|
|
153
|
+
try {
|
|
154
|
+
origin = new URL(clientId).origin;
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return; // not a URL, so there is no origin to hold responsible
|
|
158
|
+
}
|
|
159
|
+
const now = Date.now();
|
|
160
|
+
const entry = this.originFailures.get(origin);
|
|
161
|
+
if (!entry || entry.resetAt <= now) {
|
|
162
|
+
this.originFailures.set(origin, { count: 1, resetAt: now + ORIGIN_FAILURE_WINDOW_MS });
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
entry.count++;
|
|
166
|
+
}
|
|
167
|
+
if (this.originFailures.size > MAX_TRACKED_ORIGINS) {
|
|
168
|
+
for (const [key, value] of this.originFailures) {
|
|
169
|
+
if (value.resetAt <= now)
|
|
170
|
+
this.originFailures.delete(key);
|
|
171
|
+
}
|
|
172
|
+
while (this.originFailures.size > MAX_TRACKED_ORIGINS) {
|
|
173
|
+
const oldest = this.originFailures.keys().next();
|
|
174
|
+
if (oldest.done)
|
|
175
|
+
break;
|
|
176
|
+
this.originFailures.delete(oldest.value);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
remember(clientId, client, ttl) {
|
|
181
|
+
this.cache.delete(clientId);
|
|
182
|
+
this.cache.set(clientId, { client, expiresAt: Date.now() + ttl });
|
|
183
|
+
// Insertion order is recency order because a hit re-inserts; the oldest
|
|
184
|
+
// key is therefore the least recently used one.
|
|
185
|
+
while (this.cache.size > MAX_CACHE_ENTRIES) {
|
|
186
|
+
const oldest = this.cache.keys().next();
|
|
187
|
+
if (oldest.done)
|
|
188
|
+
break;
|
|
189
|
+
this.cache.delete(oldest.value);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Draft §6.5: refuse every private or loopback address, so a client_id cannot
|
|
194
|
+
* be aimed at the hub's own network or a cloud metadata endpoint. Returns the
|
|
195
|
+
* address the connection is then pinned to.
|
|
196
|
+
*/
|
|
197
|
+
assertPublicHost(hostname) {
|
|
198
|
+
return resolvePublicAddress(hostname, this.options.allowPrivateAddresses);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* The same guarded GET the metadata fetch uses, for the one other outbound
|
|
202
|
+
* request a client_id can trigger: the jwks_uri of a private_key_jwt client.
|
|
203
|
+
* Shaped for jose's customFetch hook, which reads only the status and the
|
|
204
|
+
* JSON body.
|
|
205
|
+
*/
|
|
206
|
+
safeFetch = async (input, init) => {
|
|
207
|
+
const url = new URL(input instanceof Request ? input.url : String(input));
|
|
208
|
+
if (url.protocol !== 'https:' && !this.options.allowPrivateAddresses)
|
|
209
|
+
throw new Error(`${url.origin} is not https`);
|
|
210
|
+
const pinned = await this.assertPublicHost(url.hostname);
|
|
211
|
+
return this.fetchCapped(url, pinned, MAX_JWKS_BYTES, headersFrom(init));
|
|
212
|
+
};
|
|
213
|
+
/**
|
|
214
|
+
* One GET, with every guard applied and the body capped before anyone gets
|
|
215
|
+
* to look at it. The cap is what makes this safe to hand to jose, which
|
|
216
|
+
* would otherwise buffer a key set of any size the client cares to send.
|
|
217
|
+
*/
|
|
218
|
+
async fetchCapped(url, pinned, maxBytes, headers) {
|
|
219
|
+
const override = this.options.fetchImpl ?? fetchOverride;
|
|
220
|
+
if (!override && pinned) {
|
|
221
|
+
return guardedRequest(url, { pinnedAddress: pinned, headers, timeoutMs: FETCH_TIMEOUT_MS, maxBytes });
|
|
222
|
+
}
|
|
223
|
+
const response = await (override ?? fetch)(url, {
|
|
224
|
+
method: 'GET',
|
|
225
|
+
headers,
|
|
226
|
+
redirect: 'error',
|
|
227
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
228
|
+
});
|
|
229
|
+
return capResponse(response, maxBytes);
|
|
230
|
+
}
|
|
231
|
+
async fetchDocument(url, pinned) {
|
|
232
|
+
const response = await this.fetchCapped(url, pinned, MAX_DOCUMENT_BYTES, { Accept: 'application/json' });
|
|
233
|
+
if (!response.ok)
|
|
234
|
+
throw new Error(`HTTP ${response.status}`);
|
|
235
|
+
const contentType = response.headers.get('content-type')?.split(';')[0].trim().toLowerCase() ?? '';
|
|
236
|
+
if (contentType !== 'application/json' && !contentType.endsWith('+json')) {
|
|
237
|
+
throw new Error(`content type ${contentType || 'missing'} is not JSON`);
|
|
238
|
+
}
|
|
239
|
+
let document;
|
|
240
|
+
try {
|
|
241
|
+
document = JSON.parse(await response.text());
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
throw new Error('body is not valid JSON');
|
|
245
|
+
}
|
|
246
|
+
return { document, ttl: cacheTtl(response.headers.get('cache-control')) };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/** Re-reads a response through the byte cap and hands back an equivalent one.
|
|
250
|
+
* A failed status is passed straight through: nothing reads its body. */
|
|
251
|
+
async function capResponse(response, limit) {
|
|
252
|
+
if (!response.ok)
|
|
253
|
+
return response;
|
|
254
|
+
const body = await readCapped(response, limit);
|
|
255
|
+
const headers = new Headers(response.headers);
|
|
256
|
+
// Both would now describe the original transfer rather than this body.
|
|
257
|
+
headers.delete('content-encoding');
|
|
258
|
+
headers.delete('content-length');
|
|
259
|
+
return new Response(body, { status: response.status, headers });
|
|
260
|
+
}
|
|
261
|
+
function headersFrom(init) {
|
|
262
|
+
const result = {};
|
|
263
|
+
const headers = init?.headers;
|
|
264
|
+
if (!headers)
|
|
265
|
+
return result;
|
|
266
|
+
if (headers instanceof Headers)
|
|
267
|
+
for (const [key, value] of headers)
|
|
268
|
+
result[key] = value;
|
|
269
|
+
else if (Array.isArray(headers))
|
|
270
|
+
for (const [key, value] of headers)
|
|
271
|
+
result[key] = value;
|
|
272
|
+
else
|
|
273
|
+
Object.assign(result, headers);
|
|
274
|
+
return result;
|
|
275
|
+
}
|
|
276
|
+
/** Reads at most `limit` bytes and refuses anything longer, without buffering
|
|
277
|
+
* a response an attacker made arbitrarily large. */
|
|
278
|
+
async function readCapped(response, limit) {
|
|
279
|
+
const declared = Number(response.headers.get('content-length'));
|
|
280
|
+
if (Number.isFinite(declared) && declared > limit)
|
|
281
|
+
throw new Error(`document exceeds ${limit} bytes`);
|
|
282
|
+
const reader = response.body?.getReader();
|
|
283
|
+
if (!reader)
|
|
284
|
+
throw new Error('response has no body');
|
|
285
|
+
const chunks = [];
|
|
286
|
+
let size = 0;
|
|
287
|
+
try {
|
|
288
|
+
for (;;) {
|
|
289
|
+
const { done, value } = await reader.read();
|
|
290
|
+
if (done)
|
|
291
|
+
break;
|
|
292
|
+
size += value.byteLength;
|
|
293
|
+
if (size > limit)
|
|
294
|
+
throw new Error(`document exceeds ${limit} bytes`);
|
|
295
|
+
chunks.push(value);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
finally {
|
|
299
|
+
await reader.cancel().catch(() => { });
|
|
300
|
+
}
|
|
301
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
302
|
+
}
|
|
303
|
+
/** Draft §5: respect the client's cache headers, but never longer than a day
|
|
304
|
+
* and never so short that every authorization request refetches. */
|
|
305
|
+
function cacheTtl(cacheControl) {
|
|
306
|
+
if (cacheControl && /(^|,)\s*(no-store|no-cache)\s*(,|$)/i.test(cacheControl))
|
|
307
|
+
return MIN_TTL_MS;
|
|
308
|
+
const maxAge = cacheControl?.match(/(?:^|,)\s*max-age\s*=\s*(\d+)/i);
|
|
309
|
+
if (!maxAge)
|
|
310
|
+
return DEFAULT_TTL_MS;
|
|
311
|
+
return Math.min(MAX_TTL_MS, Math.max(MIN_TTL_MS, Number(maxAge[1]) * 1000));
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Turns a fetched document into the client record the rest of the OAuth stack
|
|
315
|
+
* already understands. Throws with a reason for every rejection.
|
|
316
|
+
*
|
|
317
|
+
* No client_secret is ever produced: the SDK's client authentication middleware
|
|
318
|
+
* demands a secret exactly when the stored record has one, so a record without
|
|
319
|
+
* it is what makes a public client work — and the draft forbids symmetric
|
|
320
|
+
* secrets for CIMD clients outright.
|
|
321
|
+
*/
|
|
322
|
+
export function validateDocument(document, clientId) {
|
|
323
|
+
if (!document || typeof document !== 'object' || Array.isArray(document))
|
|
324
|
+
throw new Error('document is not a JSON object');
|
|
325
|
+
const metadata = document;
|
|
326
|
+
// Simple string comparison, per the draft: a document that could claim any
|
|
327
|
+
// identity would let one origin impersonate another.
|
|
328
|
+
if (metadata.client_id !== clientId)
|
|
329
|
+
throw new Error('client_id does not match the document URL');
|
|
330
|
+
// The name goes on the consent page, so it is held to what a name can be:
|
|
331
|
+
// one line, and short enough not to displace what sits below it.
|
|
332
|
+
const clientName = clampDisplayName(metadata.client_name);
|
|
333
|
+
if (clientName === undefined)
|
|
334
|
+
throw new Error('client_name is missing');
|
|
335
|
+
if ('client_secret' in metadata || 'client_secret_expires_at' in metadata) {
|
|
336
|
+
throw new Error('document carries a client_secret');
|
|
337
|
+
}
|
|
338
|
+
const authMethod = metadata.token_endpoint_auth_method;
|
|
339
|
+
if (authMethod !== undefined) {
|
|
340
|
+
if (typeof authMethod !== 'string' || !ASSERTION_SAFE_METHODS.has(authMethod)) {
|
|
341
|
+
throw new Error(`token_endpoint_auth_method ${String(authMethod)} needs a shared secret`);
|
|
342
|
+
}
|
|
343
|
+
if (authMethod === 'private_key_jwt' && !metadata.jwks_uri && !metadata.jwks) {
|
|
344
|
+
throw new Error('private_key_jwt without jwks or jwks_uri');
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
const redirectUris = metadata.redirect_uris;
|
|
348
|
+
if (!Array.isArray(redirectUris) || redirectUris.length === 0)
|
|
349
|
+
throw new Error('redirect_uris is missing or empty');
|
|
350
|
+
for (const uri of redirectUris) {
|
|
351
|
+
if (typeof uri !== 'string')
|
|
352
|
+
throw new Error('redirect_uris contains a non-string entry');
|
|
353
|
+
// Same rule the MCP specification states for every client: https, or a
|
|
354
|
+
// loopback address for native clients. A metadata document does not get
|
|
355
|
+
// the private-use schemes dynamic registration allows.
|
|
356
|
+
if (!isSafeRedirectUri(uri, { allowPrivateUseSchemes: false })) {
|
|
357
|
+
throw new Error(`redirect_uri ${uri} is neither https nor loopback`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return {
|
|
361
|
+
...metadata,
|
|
362
|
+
client_id: clientId,
|
|
363
|
+
client_name: clientName,
|
|
364
|
+
redirect_uris: redirectUris
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
//# sourceMappingURL=cimd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cimd.js","sourceRoot":"","sources":["../../src/auth/cimd.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
import { escapeHtml, renderPage } from './page.js';
|
|
1
|
+
import { escapeHtml, renderPage, renderIdentity } from './page.js';
|
|
2
2
|
/**
|
|
3
3
|
* Shown when a signed-in user is asked to authorize a client they have not
|
|
4
4
|
* confirmed before. This is the only thing standing between a live session
|
|
5
5
|
* cookie and a code, so it must never be submitted on the user's behalf:
|
|
6
6
|
* the CSRF token ties the form to their session.
|
|
7
7
|
*/
|
|
8
|
-
export function renderConsentPage(requestToken, csrfToken, redirectUri,
|
|
8
|
+
export function renderConsentPage(requestToken, csrfToken, redirectUri, identity = {}) {
|
|
9
9
|
return renderPage('mcp-hub authorization', `<form method="post" action="consent">
|
|
10
10
|
<h1>Authorize access?</h1>
|
|
11
|
-
<p>${clientName ? `<strong>${escapeHtml(clientName)}</strong> is asking` : 'An application is asking'} for access.</p>
|
|
12
|
-
|
|
13
|
-
<code class="target">${escapeHtml(resource ?? 'Every server on this hub')}</code>
|
|
14
|
-
<p class="label">Codes will be sent to</p>
|
|
15
|
-
<code class="target">${escapeHtml(redirectUri)}</code>
|
|
11
|
+
<p>${identity.clientName ? `<strong>${escapeHtml(identity.clientName)}</strong> is asking` : 'An application is asking'} for access.</p>
|
|
12
|
+
${renderIdentity(redirectUri, identity)}
|
|
16
13
|
<p>Only continue if you started this yourself. The name above is chosen by the application; the address is not.</p>
|
|
17
14
|
<input type="hidden" name="request" value="${escapeHtml(requestToken)}">
|
|
18
15
|
<input type="hidden" name="csrf" value="${escapeHtml(csrfToken)}">
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consent-page.js","sourceRoot":"","sources":["../../src/auth/consent-page.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"consent-page.js","sourceRoot":"","sources":["../../src/auth/consent-page.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGnE;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,YAAoB,EAAE,SAAiB,EAAE,WAAmB,EAAE,QAAQ,GAAmB,EAAE;IAC3H,OAAO,UAAU,CACf,uBAAuB,EACvB;;OAEG,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,UAAU,CAAC,QAAQ,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC,CAAC,0BAA0B;EACvH,cAAc,CAAC,WAAW,EAAE,QAAQ,CAAC;;+CAEQ,UAAU,CAAC,YAAY,CAAC;4CAC3B,UAAU,CAAC,SAAS,CAAC;;;QAGzD,CACL,CAAC;AACJ,CAAC"}
|
package/dist/auth/headers.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* sits there with no visible error, so the pages have to name the one origin
|
|
8
8
|
* their flow legitimately ends on.
|
|
9
9
|
*/
|
|
10
|
-
|
|
10
|
+
function contentSecurityPolicy(formActionOrigins = []) {
|
|
11
11
|
const formAction = ["'self'", ...formActionOrigins].join(' ');
|
|
12
12
|
return `default-src 'none'; style-src 'unsafe-inline'; form-action ${formAction}; frame-ancestors 'none'; base-uri 'none'`;
|
|
13
13
|
}
|
|
@@ -30,4 +30,25 @@ export function allowFormActionTo(res, redirectUri) {
|
|
|
30
30
|
return;
|
|
31
31
|
res.set('Content-Security-Policy', contentSecurityPolicy([origin]));
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* The headers every page and token response of the auth surface carries.
|
|
35
|
+
*
|
|
36
|
+
* Shared rather than repeated: the mounted authorization server is a Koa app
|
|
37
|
+
* the Express router never enters, so these have to be put in front of it
|
|
38
|
+
* explicitly -- and two copies of the list would drift the moment one of them
|
|
39
|
+
* gained a header.
|
|
40
|
+
*
|
|
41
|
+
* RFC 6749 §5.1 requires `no-store` on token responses; the login form has no
|
|
42
|
+
* business being cached either. The CSP and the legacy frame header keep an
|
|
43
|
+
* attacker from clickjacking approval or password entry. This application has
|
|
44
|
+
* no legitimate framing use case.
|
|
45
|
+
*/
|
|
46
|
+
export const authSecurityHeaders = (_req, res, next) => {
|
|
47
|
+
res.set('Cache-Control', 'no-store');
|
|
48
|
+
res.set('Content-Security-Policy', contentSecurityPolicy());
|
|
49
|
+
res.set('X-Frame-Options', 'DENY');
|
|
50
|
+
res.set('X-Content-Type-Options', 'nosniff');
|
|
51
|
+
res.set('Referrer-Policy', 'no-referrer');
|
|
52
|
+
next();
|
|
53
|
+
};
|
|
33
54
|
//# sourceMappingURL=headers.js.map
|
package/dist/auth/headers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headers.js","sourceRoot":"","sources":["../../src/auth/headers.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,
|
|
1
|
+
{"version":3,"file":"headers.js","sourceRoot":"","sources":["../../src/auth/headers.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,SAAS,qBAAqB,CAAC,iBAAiB,GAAsB,EAAE;IACtE,MAAM,UAAU,GAAG,CAAC,QAAQ,EAAE,GAAG,iBAAiB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9D,OAAO,8DAA8D,UAAU,2CAA2C,CAAC;AAC7H,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAa,EAAE,WAAmB;IAClE,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,4EAA4E;IACtF,CAAC;IACD,uEAAuE;IACvE,qEAAqE;IACrE,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO;IAC9B,GAAG,CAAC,GAAG,CAAC,yBAAyB,EAAE,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACtE,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAmB,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACrE,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;IACrC,GAAG,CAAC,GAAG,CAAC,yBAAyB,EAAE,qBAAqB,EAAE,CAAC,CAAC;IAC5D,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;IACnC,GAAG,CAAC,GAAG,CAAC,wBAAwB,EAAE,SAAS,CAAC,CAAC;IAC7C,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,aAAa,CAAC,CAAC;IAC1C,IAAI,EAAE,CAAC;AACT,CAAC,CAAC"}
|
package/dist/auth/login-page.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
import { escapeHtml, renderPage } from './page.js';
|
|
1
|
+
import { escapeHtml, renderPage, renderIdentity } from './page.js';
|
|
2
2
|
/**
|
|
3
3
|
* Entering the password here is what approves the client, so the page names
|
|
4
4
|
* both the client and where its codes would be sent — the client name is
|
|
5
5
|
* self-declared at registration and anyone can register, the URL cannot lie.
|
|
6
6
|
*/
|
|
7
|
-
export function renderLoginPage(requestToken, redirectUri,
|
|
7
|
+
export function renderLoginPage(requestToken, redirectUri, identity = {}, errorMessage) {
|
|
8
8
|
return renderPage('mcp-hub login', `<form method="post" action="login">
|
|
9
9
|
<h1>mcp-hub</h1>
|
|
10
|
-
<p>${clientName ? `Authorize <strong>${escapeHtml(clientName)}</strong>` : 'Authorization required'}</p>
|
|
11
|
-
|
|
12
|
-
<code class="target">${escapeHtml(resource ?? 'Every server on this hub')}</code>
|
|
13
|
-
<p class="label">Codes will be sent to</p>
|
|
14
|
-
<code class="target">${escapeHtml(redirectUri)}</code>
|
|
10
|
+
<p>${identity.clientName ? `Authorize <strong>${escapeHtml(identity.clientName)}</strong>` : 'Authorization required'}</p>
|
|
11
|
+
${renderIdentity(redirectUri, identity)}
|
|
15
12
|
<input type="password" name="password" placeholder="Password" autofocus required autocomplete="current-password">
|
|
16
13
|
<input type="hidden" name="request" value="${escapeHtml(requestToken)}">
|
|
17
14
|
${errorMessage ? `<p class="error">${escapeHtml(errorMessage)}</p>` : ''}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login-page.js","sourceRoot":"","sources":["../../src/auth/login-page.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"login-page.js","sourceRoot":"","sources":["../../src/auth/login-page.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGnE;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,YAAoB,EAAE,WAAmB,EAAE,QAAQ,GAAmB,EAAE,EAAE,YAAqB;IAC7H,OAAO,UAAU,CACf,eAAe,EACf;;OAEG,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,qBAAqB,UAAU,CAAC,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,wBAAwB;EACrH,cAAc,CAAC,WAAW,EAAE,QAAQ,CAAC;;+CAEQ,UAAU,CAAC,YAAY,CAAC;IACnE,YAAY,CAAC,CAAC,CAAC,oBAAoB,UAAU,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;;QAElE,CACL,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { errors } from 'oidc-provider';
|
|
2
|
+
import { isClientIdMetadataUrl } from '../cimd.js';
|
|
3
|
+
import { withoutPhantomSecret } from './quirks.js';
|
|
4
|
+
/**
|
|
5
|
+
* oidc-provider's ten models, all backed by the one `AuthStore` the hub already
|
|
6
|
+
* serialises every other write through.
|
|
7
|
+
*
|
|
8
|
+
* The store is not replaced by this and does not shrink: API tokens, upstream
|
|
9
|
+
* credentials and the cross-process lock the admin CLI shares are mcp-hub's own
|
|
10
|
+
* concepts and have nothing to do with the authorization server. What this adds
|
|
11
|
+
* is a flat artifact slot the library owns the shape of.
|
|
12
|
+
*
|
|
13
|
+
* Two behaviours are deliberately in the store rather than here:
|
|
14
|
+
*
|
|
15
|
+
* - the `revokedBefore` cutoff, because `find()` returning falsy IS the
|
|
16
|
+
* revocation as far as the library is concerned (base_model.js treats any
|
|
17
|
+
* falsy return as "not found"), and
|
|
18
|
+
* - `consumed`, because a consumed authorization code has to be
|
|
19
|
+
* distinguishable from an unknown one or replay detection cannot work.
|
|
20
|
+
*/
|
|
21
|
+
export function createOidcAdapter(store, cimd) {
|
|
22
|
+
return (model) => (model === 'Client' ? clientAdapter(store, cimd) : artifactAdapter(store, model));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Clients are NOT stored in the generic artifact slot.
|
|
26
|
+
*
|
|
27
|
+
* `mcp-hub-admin clients list|prune|revoke`, the ceiling on unapproved clients,
|
|
28
|
+
* the activity stamps behind ageing registrations out, and the approval records
|
|
29
|
+
* all read `state.clients`. Putting oidc-provider's clients anywhere else would
|
|
30
|
+
* leave the admin CLI looking at an empty hub while the authorization server
|
|
31
|
+
* happily served a full one — two stores, one truth, no error message.
|
|
32
|
+
*
|
|
33
|
+
* The shapes already agree: both are RFC 7591 metadata keyed by `client_id`.
|
|
34
|
+
*/
|
|
35
|
+
function clientAdapter(store, cimd) {
|
|
36
|
+
return {
|
|
37
|
+
async upsert(id, payload) {
|
|
38
|
+
// A public client's record must not carry a secret; see withoutPhantomSecret.
|
|
39
|
+
const record = { ...withoutPhantomSecret(payload), client_id: id };
|
|
40
|
+
// A NEW client goes through addClient, which is what enforces the ceiling
|
|
41
|
+
// on unapproved registrations and creates the lifecycle entry the
|
|
42
|
+
// activity stamps and `mcp-hub-admin clients prune` are built on. Writing
|
|
43
|
+
// it with saveClient would leave a client nothing ages out.
|
|
44
|
+
if (store.getClient(id)) {
|
|
45
|
+
store.updateClient(record);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
// False means the hub is full of clients that were all confirmed, and
|
|
49
|
+
// dropping one of those would take a connector somebody uses offline.
|
|
50
|
+
// Refusing the newcomer is the failure an operator can see and fix, so it
|
|
51
|
+
// has to surface rather than be swallowed into a successful-looking
|
|
52
|
+
// registration that stored nothing.
|
|
53
|
+
if (!store.addClient(record)) {
|
|
54
|
+
const full = new errors.OIDCProviderError(400, 'too_many_requests');
|
|
55
|
+
full.error_description = 'the hub is not accepting new client registrations right now';
|
|
56
|
+
throw full;
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
/**
|
|
60
|
+
* Client ID Metadata Documents are resolved HERE, deliberately.
|
|
61
|
+
*
|
|
62
|
+
* models/client.js consults the adapter BEFORE its own metadata-document
|
|
63
|
+
* fetcher, so returning the document from here means oidc-provider's
|
|
64
|
+
* fetcher is never reached — and the hub keeps a materially stricter
|
|
65
|
+
* policy than the library's: DNS pinning against rebinding, an origin
|
|
66
|
+
* allowlist, per-origin failure counting so distinct client_ids on one host
|
|
67
|
+
* cannot be used to hammer it, and negative caching. oidc-provider has none
|
|
68
|
+
* of those. `allowFetch` is wired to refuse, so the built-in path stays
|
|
69
|
+
* closed even if this returns nothing.
|
|
70
|
+
*
|
|
71
|
+
* Nothing is written: a CIMD client is not registered, which is the point
|
|
72
|
+
* of it — the client reinstalls and is still the same client.
|
|
73
|
+
*/
|
|
74
|
+
async find(id) {
|
|
75
|
+
const registered = store.getClient(id);
|
|
76
|
+
if (registered)
|
|
77
|
+
return registered;
|
|
78
|
+
if (cimd && isClientIdMetadataUrl(id)) {
|
|
79
|
+
return (await cimd.resolve(id));
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
},
|
|
83
|
+
async findByUid() {
|
|
84
|
+
return undefined;
|
|
85
|
+
},
|
|
86
|
+
async findByUserCode() {
|
|
87
|
+
return undefined;
|
|
88
|
+
},
|
|
89
|
+
async consume() {
|
|
90
|
+
// Clients are not single-use.
|
|
91
|
+
},
|
|
92
|
+
async destroy(id) {
|
|
93
|
+
store.deleteClient(id);
|
|
94
|
+
},
|
|
95
|
+
async revokeByGrantId() {
|
|
96
|
+
// A grant belongs to a client; revoking it never deletes the client.
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function artifactAdapter(store, model) {
|
|
101
|
+
return {
|
|
102
|
+
async upsert(id, payload, expiresIn) {
|
|
103
|
+
store.oidcUpsert(model, id, payload, expiresIn);
|
|
104
|
+
},
|
|
105
|
+
async find(id) {
|
|
106
|
+
return store.oidcFind(model, id);
|
|
107
|
+
},
|
|
108
|
+
async findByUid(uid) {
|
|
109
|
+
return store.oidcFindBy(model, 'uid', uid);
|
|
110
|
+
},
|
|
111
|
+
/**
|
|
112
|
+
* Device flow only, and the hub does not enable it. Returning undefined is
|
|
113
|
+
* the honest answer for a model that is never written; throwing would turn
|
|
114
|
+
* a disabled feature into a 500 if it were ever reached.
|
|
115
|
+
*/
|
|
116
|
+
async findByUserCode() {
|
|
117
|
+
return undefined;
|
|
118
|
+
},
|
|
119
|
+
async consume(id) {
|
|
120
|
+
store.oidcConsume(model, id);
|
|
121
|
+
},
|
|
122
|
+
async destroy(id) {
|
|
123
|
+
store.oidcDestroy(model, id);
|
|
124
|
+
},
|
|
125
|
+
/**
|
|
126
|
+
* Crosses model boundaries by design: revoking a grant has to take the
|
|
127
|
+
* access tokens, refresh tokens and codes minted under it with it, and the
|
|
128
|
+
* library calls this on exactly one of the models.
|
|
129
|
+
*/
|
|
130
|
+
async revokeByGrantId(grantId) {
|
|
131
|
+
store.oidcRevokeByGrantId(grantId);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/auth/oidc/adapter.ts"],"names":[],"mappings":"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"}
|