@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.
Files changed (96) hide show
  1. package/CHANGELOG.md +619 -0
  2. package/README.md +189 -64
  3. package/dist/admin.js +232 -8
  4. package/dist/admin.js.map +1 -1
  5. package/dist/auth/address.js +89 -0
  6. package/dist/auth/address.js.map +1 -0
  7. package/dist/auth/api-tokens.js +27 -0
  8. package/dist/auth/api-tokens.js.map +1 -0
  9. package/dist/auth/cimd.js +367 -0
  10. package/dist/auth/cimd.js.map +1 -0
  11. package/dist/auth/consent-page.js +4 -7
  12. package/dist/auth/consent-page.js.map +1 -1
  13. package/dist/auth/headers.js +22 -1
  14. package/dist/auth/headers.js.map +1 -1
  15. package/dist/auth/login-page.js +4 -7
  16. package/dist/auth/login-page.js.map +1 -1
  17. package/dist/auth/oidc/adapter.js +135 -0
  18. package/dist/auth/oidc/adapter.js.map +1 -0
  19. package/dist/auth/oidc/interactions.js +187 -0
  20. package/dist/auth/oidc/interactions.js.map +1 -0
  21. package/dist/auth/oidc/mount.js +144 -0
  22. package/dist/auth/oidc/mount.js.map +1 -0
  23. package/dist/auth/oidc/provider.js +440 -0
  24. package/dist/auth/oidc/provider.js.map +1 -0
  25. package/dist/auth/oidc/quirks.js +234 -0
  26. package/dist/auth/oidc/quirks.js.map +1 -0
  27. package/dist/auth/oidc/verifier.js +121 -0
  28. package/dist/auth/oidc/verifier.js.map +1 -0
  29. package/dist/auth/page.js +22 -0
  30. package/dist/auth/page.js.map +1 -1
  31. package/dist/auth/pinned-fetch.js +129 -0
  32. package/dist/auth/pinned-fetch.js.map +1 -0
  33. package/dist/auth/protected-resource.js +41 -0
  34. package/dist/auth/protected-resource.js.map +1 -0
  35. package/dist/auth/rate-limit.js +156 -0
  36. package/dist/auth/rate-limit.js.map +1 -0
  37. package/dist/auth/redirect-uri.js +90 -0
  38. package/dist/auth/redirect-uri.js.map +1 -0
  39. package/dist/auth/registration.js +146 -0
  40. package/dist/auth/registration.js.map +1 -0
  41. package/dist/auth/session.js +43 -0
  42. package/dist/auth/session.js.map +1 -0
  43. package/dist/auth/signed-token.js +49 -0
  44. package/dist/auth/signed-token.js.map +1 -0
  45. package/dist/auth/store.js +516 -5
  46. package/dist/auth/store.js.map +1 -1
  47. package/dist/auth/text.js +51 -0
  48. package/dist/auth/text.js.map +1 -0
  49. package/dist/config.js +184 -5
  50. package/dist/config.js.map +1 -1
  51. package/dist/docker-proxy/policy.js +1 -0
  52. package/dist/docker-proxy/policy.js.map +1 -1
  53. package/dist/docker-proxy/server.js +1 -0
  54. package/dist/docker-proxy/server.js.map +1 -1
  55. package/dist/elicitation.js +0 -0
  56. package/dist/elicitation.js.map +1 -0
  57. package/dist/forward.js +0 -0
  58. package/dist/forward.js.map +1 -0
  59. package/dist/health.js +22 -3
  60. package/dist/health.js.map +1 -1
  61. package/dist/hub.js +337 -29
  62. package/dist/hub.js.map +1 -1
  63. package/dist/index.js +218 -23
  64. package/dist/index.js.map +1 -1
  65. package/dist/limits.js +13 -1
  66. package/dist/limits.js.map +1 -1
  67. package/dist/mcp-limits.js +14 -2
  68. package/dist/mcp-limits.js.map +1 -1
  69. package/dist/proxy.js +262 -34
  70. package/dist/proxy.js.map +1 -1
  71. package/dist/stdio.js +97 -7
  72. package/dist/stdio.js.map +1 -1
  73. package/dist/subscriptions.js +236 -0
  74. package/dist/subscriptions.js.map +1 -0
  75. package/dist/supervisor.js +472 -28
  76. package/dist/supervisor.js.map +1 -1
  77. package/dist/timings.js +61 -0
  78. package/dist/timings.js.map +1 -0
  79. package/dist/tool-filter.js +59 -0
  80. package/dist/tool-filter.js.map +1 -0
  81. package/dist/transports/docker.js.map +1 -1
  82. package/dist/transports/stream.js +33 -20
  83. package/dist/transports/stream.js.map +1 -1
  84. package/dist/upstream/auth.js +435 -0
  85. package/dist/upstream/auth.js.map +1 -0
  86. package/dist/upstream/login.js +94 -0
  87. package/dist/upstream/login.js.map +1 -0
  88. package/dist/upstream/provider.js +286 -0
  89. package/dist/upstream/provider.js.map +1 -0
  90. package/dist/upstream/routes.js +97 -0
  91. package/dist/upstream/routes.js.map +1 -0
  92. package/package.json +23 -7
  93. package/dist/auth/provider.js +0 -380
  94. package/dist/auth/provider.js.map +0 -1
  95. package/dist/auth/routes.js +0 -223
  96. 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, clientName, resource) {
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
- <p class="label">Requested access</p>
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;AAEnD;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,YAAoB,EAAE,SAAiB,EAAE,WAAmB,EAAE,UAAmB,EAAE,QAAiB;IACpI,OAAO,UAAU,CACf,uBAAuB,EACvB;;OAEG,UAAU,CAAC,CAAC,CAAC,WAAW,UAAU,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC,CAAC,0BAA0B;;yBAE9E,UAAU,CAAC,QAAQ,IAAI,0BAA0B,CAAC;;yBAElD,UAAU,CAAC,WAAW,CAAC;;+CAED,UAAU,CAAC,YAAY,CAAC;4CAC3B,UAAU,CAAC,SAAS,CAAC;;;QAGzD,CACL,CAAC;AACJ,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"}
@@ -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
- export function contentSecurityPolicy(formActionOrigins = []) {
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
@@ -1 +1 @@
1
- {"version":3,"file":"headers.js","sourceRoot":"","sources":["../../src/auth/headers.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CAAC,iBAAiB,GAAsB,EAAE;IAC7E,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"}
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"}
@@ -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, clientName, errorMessage, resource) {
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
- <p class="label">Requested access</p>
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;AAEnD;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,YAAoB,EAAE,WAAmB,EAAE,UAAmB,EAAE,YAAqB,EAAE,QAAiB;IACtI,OAAO,UAAU,CACf,eAAe,EACf;;OAEG,UAAU,CAAC,CAAC,CAAC,qBAAqB,UAAU,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,wBAAwB;;yBAE5E,UAAU,CAAC,QAAQ,IAAI,0BAA0B,CAAC;;yBAElD,UAAU,CAAC,WAAW,CAAC;;+CAED,UAAU,CAAC,YAAY,CAAC;IACnE,YAAY,CAAC,CAAC,CAAC,oBAAoB,UAAU,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;;QAElE,CACL,CAAC;AACJ,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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"}