fastmcp 4.16.14 → 4.17.1
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/dist/FastMCP.cjs +3 -3
- package/dist/FastMCP.d.cts +3 -2
- package/dist/FastMCP.d.ts +3 -2
- package/dist/FastMCP.js +2 -2
- package/dist/{OAuthProvider-Bvt5a7yr.d.cts → OAuthProvider-Dy1PbWAT.d.cts} +50 -1
- package/dist/{OAuthProvider-Bvt5a7yr.d.ts → OAuthProvider-Dy1PbWAT.d.ts} +50 -1
- package/dist/auth/index.cjs +2 -2
- package/dist/auth/index.d.cts +2 -2
- package/dist/auth/index.d.ts +2 -2
- package/dist/auth/index.js +1 -1
- package/dist/{chunk-YZWUEW3A.cjs → chunk-E3HXGE2O.cjs} +270 -24
- package/dist/chunk-E3HXGE2O.cjs.map +1 -0
- package/dist/{chunk-IUDWO3A2.js → chunk-KWRV7CHJ.js} +261 -15
- package/dist/chunk-KWRV7CHJ.js.map +1 -0
- package/dist/{chunk-KEGPQRWF.cjs → chunk-SYZRGVQX.cjs} +1 -1
- package/dist/chunk-SYZRGVQX.cjs.map +1 -0
- package/dist/{chunk-GABSX3WB.js → chunk-UODI5E6U.js} +1 -1
- package/dist/chunk-UODI5E6U.js.map +1 -0
- package/dist/examples/custom-routes.cjs +3 -3
- package/dist/examples/custom-routes.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-GABSX3WB.js.map +0 -1
- package/dist/chunk-IUDWO3A2.js.map +0 -1
- package/dist/chunk-KEGPQRWF.cjs.map +0 -1
- package/dist/chunk-YZWUEW3A.cjs.map +0 -1
|
@@ -72,9 +72,11 @@ var proxyDcrClientStorageSchema = _zod.z.object({
|
|
|
72
72
|
callbackUrl: _zod.z.string(),
|
|
73
73
|
clientId: _zod.z.string(),
|
|
74
74
|
clientSecret: _zod.z.string().optional(),
|
|
75
|
+
expiresAt: storedDateSchema.optional(),
|
|
75
76
|
metadata: dcrClientMetadataSchema.optional(),
|
|
76
77
|
redirectUris: _zod.z.array(_zod.z.string()),
|
|
77
|
-
registeredAt: storedDateSchema
|
|
78
|
+
registeredAt: storedDateSchema,
|
|
79
|
+
source: _zod.z.literal("cimd").optional()
|
|
78
80
|
});
|
|
79
81
|
var upstreamTokenSetStorageSchema = _zod.z.object({
|
|
80
82
|
accessToken: _zod.z.string(),
|
|
@@ -172,10 +174,18 @@ var OAuthProxyStateStore = class {
|
|
|
172
174
|
`${STORAGE_KEY_PREFIX.transaction}${transactionId}`
|
|
173
175
|
);
|
|
174
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* A client carrying `expiresAt` is a cached resolution rather than a
|
|
179
|
+
* permanent registration (CIMD), so a lapsed copy is dropped and reported as
|
|
180
|
+
* a miss — the caller re-resolves it from the source of truth.
|
|
181
|
+
*/
|
|
175
182
|
async getRegisteredClientByClientId(clientId) {
|
|
176
183
|
const cached = this.registeredClientsByClientId.get(clientId);
|
|
177
184
|
if (cached) {
|
|
178
|
-
|
|
185
|
+
if (!cached.expiresAt || !this.isExpired(cached.expiresAt)) {
|
|
186
|
+
return cached;
|
|
187
|
+
}
|
|
188
|
+
this.registeredClientsByClientId.delete(clientId);
|
|
179
189
|
}
|
|
180
190
|
const stored = await this.tokenStorage.get(
|
|
181
191
|
`${STORAGE_KEY_PREFIX.client}${clientId}`
|
|
@@ -184,6 +194,10 @@ var OAuthProxyStateStore = class {
|
|
|
184
194
|
if (!parsed.success) {
|
|
185
195
|
return null;
|
|
186
196
|
}
|
|
197
|
+
if (parsed.data.expiresAt && this.isExpired(parsed.data.expiresAt)) {
|
|
198
|
+
await this.tokenStorage.delete(`${STORAGE_KEY_PREFIX.client}${clientId}`);
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
187
201
|
this.cacheRegisteredClient(parsed.data);
|
|
188
202
|
return parsed.data;
|
|
189
203
|
}
|
|
@@ -205,12 +219,6 @@ var OAuthProxyStateStore = class {
|
|
|
205
219
|
}
|
|
206
220
|
return parsed.data;
|
|
207
221
|
}
|
|
208
|
-
async isTransactionCallbackRegistered(transaction) {
|
|
209
|
-
const registeredClient = await this.getRegisteredClientByClientId(
|
|
210
|
-
transaction.clientId
|
|
211
|
-
);
|
|
212
|
-
return _nullishCoalesce(_optionalChain([registeredClient, 'optionalAccess', _ => _.redirectUris, 'access', _2 => _2.includes, 'call', _3 => _3(transaction.clientCallbackUrl)]), () => ( false));
|
|
213
|
-
}
|
|
214
222
|
/**
|
|
215
223
|
* Record that an authorization code has been redeemed, so a later attempt
|
|
216
224
|
* gets "already used" rather than looking like an unknown code. Keyed and
|
|
@@ -233,7 +241,8 @@ var OAuthProxyStateStore = class {
|
|
|
233
241
|
async saveRegisteredClient(client) {
|
|
234
242
|
await this.tokenStorage.save(
|
|
235
243
|
`${STORAGE_KEY_PREFIX.client}${client.clientId}`,
|
|
236
|
-
client
|
|
244
|
+
client,
|
|
245
|
+
client.expiresAt ? this.getTtlSeconds(client.expiresAt) : void 0
|
|
237
246
|
);
|
|
238
247
|
}
|
|
239
248
|
async saveTransaction(transaction) {
|
|
@@ -276,6 +285,173 @@ var DEFAULT_AUTHORIZATION_CODE_TTL = 300;
|
|
|
276
285
|
var DEFAULT_TRANSACTION_TTL = 600;
|
|
277
286
|
var DEFAULT_UPSTREAM_REQUEST_TIMEOUT_MS = 1e4;
|
|
278
287
|
|
|
288
|
+
// src/auth/utils/cimd.ts
|
|
289
|
+
var CIMD_FETCH_TIMEOUT_MS = 5e3;
|
|
290
|
+
var CIMD_CLIENT_TTL_MS = 15 * 60 * 1e3;
|
|
291
|
+
var CIMD_MAX_RESPONSE_BYTES = 65536;
|
|
292
|
+
async function resolveCimdClient(clientId, requestedRedirectUri, validateRedirectUri) {
|
|
293
|
+
let url;
|
|
294
|
+
try {
|
|
295
|
+
url = new URL(clientId);
|
|
296
|
+
} catch (e) {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
if (url.protocol !== "https:" || url.search) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
if (isPrivateOrLoopbackHost(url.hostname)) {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
let response;
|
|
306
|
+
try {
|
|
307
|
+
response = await fetch(clientId, {
|
|
308
|
+
redirect: "error",
|
|
309
|
+
signal: AbortSignal.timeout(CIMD_FETCH_TIMEOUT_MS)
|
|
310
|
+
});
|
|
311
|
+
} catch (e2) {
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
if (!response.ok) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
const text = await readBoundedText(response);
|
|
318
|
+
if (text === null) {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
let parsed;
|
|
322
|
+
try {
|
|
323
|
+
parsed = JSON.parse(text);
|
|
324
|
+
} catch (e3) {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
const doc = parsed;
|
|
331
|
+
if (doc.client_id !== clientId) {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
if (!isStringArray(doc.redirect_uris) || doc.redirect_uris.length === 0) {
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
const redirectUris = doc.redirect_uris;
|
|
338
|
+
if (requestedRedirectUri && !redirectUris.includes(requestedRedirectUri)) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
if (!redirectUris.every((uri) => validateRedirectUri(uri))) {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
const metadata = {
|
|
345
|
+
client_name: optionalString(doc.client_name),
|
|
346
|
+
client_uri: optionalString(doc.client_uri),
|
|
347
|
+
logo_uri: optionalString(doc.logo_uri),
|
|
348
|
+
policy_uri: optionalString(doc.policy_uri),
|
|
349
|
+
scope: optionalString(doc.scope),
|
|
350
|
+
tos_uri: optionalString(doc.tos_uri)
|
|
351
|
+
};
|
|
352
|
+
return {
|
|
353
|
+
callbackUrl: redirectUris[0],
|
|
354
|
+
clientId,
|
|
355
|
+
// CIMD is a public-client mechanism secured by PKCE; there is no secret.
|
|
356
|
+
clientSecret: void 0,
|
|
357
|
+
expiresAt: new Date(Date.now() + CIMD_CLIENT_TTL_MS),
|
|
358
|
+
metadata,
|
|
359
|
+
redirectUris,
|
|
360
|
+
registeredAt: /* @__PURE__ */ new Date(),
|
|
361
|
+
source: "cimd"
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
function expandIPv6(host) {
|
|
365
|
+
const sides = host.split("::");
|
|
366
|
+
if (sides.length > 2) {
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
const head = sides[0] ? sides[0].split(":") : [];
|
|
370
|
+
const tail = sides.length === 2 && sides[1] ? sides[1].split(":") : [];
|
|
371
|
+
if (sides.length === 1 && head.length !== 8) {
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
const missing = 8 - head.length - tail.length;
|
|
375
|
+
if (missing < 0) {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
const parts = [...head, ...Array(missing).fill("0"), ...tail];
|
|
379
|
+
if (parts.length !== 8) {
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
const groups = parts.map((g) => Number.parseInt(g || "0", 16));
|
|
383
|
+
return groups.every((g) => !Number.isNaN(g)) ? groups : null;
|
|
384
|
+
}
|
|
385
|
+
function isPrivateOrLoopbackHost(hostname) {
|
|
386
|
+
const host = hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
387
|
+
if (host === "localhost" || host === "0.0.0.0") {
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
if (host.includes(":")) {
|
|
391
|
+
return isPrivateOrLoopbackIPv6(host);
|
|
392
|
+
}
|
|
393
|
+
return host === "127.0.0.1" || /^169\.254\./.test(host) || /^10\./.test(host) || /^192\.168\./.test(host) || /^172\.(1[6-9]|2\d|3[01])\./.test(host);
|
|
394
|
+
}
|
|
395
|
+
function isPrivateOrLoopbackIPv6(host) {
|
|
396
|
+
const groups = expandIPv6(host);
|
|
397
|
+
if (!groups) {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
if (groups.every((g) => g === 0)) {
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
if (groups.slice(0, 7).every((g) => g === 0) && groups[7] === 1) {
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
const [first] = groups;
|
|
407
|
+
if ((first & 65472) === 65152) {
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
if ((first & 65024) === 64512) {
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
if (groups[0] === 0 && groups[1] === 0 && groups[2] === 0 && groups[3] === 0 && groups[4] === 0 && (groups[5] === 0 || groups[5] === 65535)) {
|
|
414
|
+
const ipv4 = `${groups[6] >> 8}.${groups[6] & 255}.${groups[7] >> 8}.${groups[7] & 255}`;
|
|
415
|
+
return isPrivateOrLoopbackHost(ipv4);
|
|
416
|
+
}
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
function isStringArray(value) {
|
|
420
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
421
|
+
}
|
|
422
|
+
function optionalString(value) {
|
|
423
|
+
return typeof value === "string" ? value : void 0;
|
|
424
|
+
}
|
|
425
|
+
async function readBoundedText(response) {
|
|
426
|
+
const contentLength = response.headers.get("content-length");
|
|
427
|
+
if (contentLength && Number(contentLength) > CIMD_MAX_RESPONSE_BYTES) {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
const reader = _optionalChain([response, 'access', _ => _.body, 'optionalAccess', _2 => _2.getReader, 'call', _3 => _3()]);
|
|
431
|
+
if (!reader) {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
const chunks = [];
|
|
435
|
+
let totalBytes = 0;
|
|
436
|
+
try {
|
|
437
|
+
for (; ; ) {
|
|
438
|
+
const { done, value } = await reader.read();
|
|
439
|
+
if (done) {
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
totalBytes += value.byteLength;
|
|
443
|
+
if (totalBytes > CIMD_MAX_RESPONSE_BYTES) {
|
|
444
|
+
await reader.cancel();
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
447
|
+
chunks.push(value);
|
|
448
|
+
}
|
|
449
|
+
} catch (e4) {
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
453
|
+
}
|
|
454
|
+
|
|
279
455
|
// src/auth/utils/claimsExtractor.ts
|
|
280
456
|
var ClaimsExtractor = (_class = class {
|
|
281
457
|
|
|
@@ -413,7 +589,7 @@ var ClaimsExtractor = (_class = class {
|
|
|
413
589
|
const stringified = JSON.stringify(value);
|
|
414
590
|
const maxSize = _nullishCoalesce(this.config.maxClaimValueSize, () => ( 2e3));
|
|
415
591
|
return stringified.length <= maxSize;
|
|
416
|
-
} catch (
|
|
592
|
+
} catch (e5) {
|
|
417
593
|
return false;
|
|
418
594
|
}
|
|
419
595
|
}
|
|
@@ -694,7 +870,7 @@ var ConsentManager = class {
|
|
|
694
870
|
return null;
|
|
695
871
|
}
|
|
696
872
|
return data;
|
|
697
|
-
} catch (
|
|
873
|
+
} catch (e6) {
|
|
698
874
|
return null;
|
|
699
875
|
}
|
|
700
876
|
}
|
|
@@ -1155,9 +1331,13 @@ var OAuthProxy = (_class4 = class {
|
|
|
1155
1331
|
__init7() {this.ownedTokenStorage = null}
|
|
1156
1332
|
/**
|
|
1157
1333
|
* Keyed by proxy-issued client_id for authorize/token-exchange lookups and
|
|
1158
|
-
* for the defence-in-depth callback checks. A registration never changes
|
|
1334
|
+
* for the defence-in-depth callback checks. A DCR registration never changes
|
|
1159
1335
|
* after it is written, so caching it locally cannot go stale; it is also
|
|
1160
1336
|
* persisted, so another instance can hydrate it.
|
|
1337
|
+
*
|
|
1338
|
+
* A CIMD client is not a registration but a snapshot of a document that can
|
|
1339
|
+
* change under us, so it is cached with an `expiresAt` and re-resolved once
|
|
1340
|
+
* that passes.
|
|
1161
1341
|
*/
|
|
1162
1342
|
__init8() {this.registeredClientsByClientId = /* @__PURE__ */ new Map()}
|
|
1163
1343
|
|
|
@@ -1167,6 +1347,7 @@ var OAuthProxy = (_class4 = class {
|
|
|
1167
1347
|
allowPlainPkce: true,
|
|
1168
1348
|
authorizationCodeTtl: DEFAULT_AUTHORIZATION_CODE_TTL,
|
|
1169
1349
|
consentRequired: true,
|
|
1350
|
+
enableCimd: false,
|
|
1170
1351
|
enableTokenSwap: true,
|
|
1171
1352
|
// Enabled by default for security
|
|
1172
1353
|
redirectPath: "/oauth/callback",
|
|
@@ -1223,7 +1404,10 @@ var OAuthProxy = (_class4 = class {
|
|
|
1223
1404
|
"Only 'code' response type is supported"
|
|
1224
1405
|
);
|
|
1225
1406
|
}
|
|
1226
|
-
const registeredClient = await this.
|
|
1407
|
+
const registeredClient = await this.resolveClient(
|
|
1408
|
+
params.client_id,
|
|
1409
|
+
params.redirect_uri
|
|
1410
|
+
);
|
|
1227
1411
|
if (!registeredClient) {
|
|
1228
1412
|
throw new OAuthProxyError("invalid_client", "Unknown client_id");
|
|
1229
1413
|
}
|
|
@@ -1245,6 +1429,20 @@ var OAuthProxy = (_class4 = class {
|
|
|
1245
1429
|
"code_challenge_method 'plain' is not supported; use S256"
|
|
1246
1430
|
);
|
|
1247
1431
|
}
|
|
1432
|
+
if (registeredClient.source === "cimd") {
|
|
1433
|
+
if (!params.code_challenge) {
|
|
1434
|
+
throw new OAuthProxyError(
|
|
1435
|
+
"invalid_request",
|
|
1436
|
+
"code_challenge is required for Client ID Metadata Document clients"
|
|
1437
|
+
);
|
|
1438
|
+
}
|
|
1439
|
+
if (params.code_challenge_method !== "S256") {
|
|
1440
|
+
throw new OAuthProxyError(
|
|
1441
|
+
"invalid_request",
|
|
1442
|
+
"code_challenge_method must be S256 for Client ID Metadata Document clients"
|
|
1443
|
+
);
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1248
1446
|
const transaction = await this.createTransaction(params);
|
|
1249
1447
|
if (this.config.consentRequired && !transaction.consentGiven) {
|
|
1250
1448
|
return this.consentManager.createConsentResponse(
|
|
@@ -1275,7 +1473,7 @@ var OAuthProxy = (_class4 = class {
|
|
|
1275
1473
|
"Only authorization_code grant type is supported"
|
|
1276
1474
|
);
|
|
1277
1475
|
}
|
|
1278
|
-
const registeredClient = await this.
|
|
1476
|
+
const registeredClient = await this.resolveClient(request.client_id);
|
|
1279
1477
|
if (!registeredClient) {
|
|
1280
1478
|
throw new OAuthProxyError("invalid_client", "Unknown client_id");
|
|
1281
1479
|
}
|
|
@@ -1297,6 +1495,12 @@ var OAuthProxy = (_class4 = class {
|
|
|
1297
1495
|
if (clientCode.clientId !== request.client_id) {
|
|
1298
1496
|
throw new OAuthProxyError("invalid_client", "Client ID mismatch");
|
|
1299
1497
|
}
|
|
1498
|
+
if (registeredClient.source === "cimd" && !clientCode.codeChallenge) {
|
|
1499
|
+
throw new OAuthProxyError(
|
|
1500
|
+
"invalid_grant",
|
|
1501
|
+
"PKCE is required for Client ID Metadata Document clients"
|
|
1502
|
+
);
|
|
1503
|
+
}
|
|
1300
1504
|
if (clientCode.codeChallenge) {
|
|
1301
1505
|
if (!request.code_verifier) {
|
|
1302
1506
|
throw new OAuthProxyError(
|
|
@@ -1363,6 +1567,7 @@ var OAuthProxy = (_class4 = class {
|
|
|
1363
1567
|
getAuthorizationServerMetadata() {
|
|
1364
1568
|
return {
|
|
1365
1569
|
authorizationEndpoint: `${this.config.baseUrl}/oauth/authorize`,
|
|
1570
|
+
...this.config.enableCimd ? { clientIdMetadataDocumentSupported: true } : {},
|
|
1366
1571
|
codeChallengeMethodsSupported: this.config.allowPlainPkce ? ["S256", "plain"] : ["S256"],
|
|
1367
1572
|
grantTypesSupported: ["authorization_code", "refresh_token"],
|
|
1368
1573
|
issuer: this.config.baseUrl,
|
|
@@ -1398,7 +1603,7 @@ var OAuthProxy = (_class4 = class {
|
|
|
1398
1603
|
if (!transaction) {
|
|
1399
1604
|
throw new OAuthProxyError("invalid_request", "Invalid or expired state");
|
|
1400
1605
|
}
|
|
1401
|
-
if (!await this.
|
|
1606
|
+
if (!await this.isTransactionCallbackRegistered(transaction)) {
|
|
1402
1607
|
throw new OAuthProxyError(
|
|
1403
1608
|
"invalid_request",
|
|
1404
1609
|
"Transaction callback URL is not registered"
|
|
@@ -1444,7 +1649,7 @@ var OAuthProxy = (_class4 = class {
|
|
|
1444
1649
|
}
|
|
1445
1650
|
if (action === "deny") {
|
|
1446
1651
|
await this.stateStore.deleteTransaction(transactionId);
|
|
1447
|
-
if (!await this.
|
|
1652
|
+
if (!await this.isTransactionCallbackRegistered(transaction)) {
|
|
1448
1653
|
throw new OAuthProxyError(
|
|
1449
1654
|
"invalid_request",
|
|
1450
1655
|
"Transaction callback URL is not registered"
|
|
@@ -1641,7 +1846,7 @@ var OAuthProxy = (_class4 = class {
|
|
|
1641
1846
|
const error = await tokenResponse.json();
|
|
1642
1847
|
errorCode = error.error || "server_error";
|
|
1643
1848
|
errorDescription = error.error_description;
|
|
1644
|
-
} catch (
|
|
1849
|
+
} catch (e7) {
|
|
1645
1850
|
errorDescription = `Upstream returned HTTP ${tokenResponse.status} ${tokenResponse.statusText}`;
|
|
1646
1851
|
}
|
|
1647
1852
|
throw new OAuthProxyError(errorCode, errorDescription);
|
|
@@ -1805,7 +2010,7 @@ var OAuthProxy = (_class4 = class {
|
|
|
1805
2010
|
const error = await tokenResponse.json();
|
|
1806
2011
|
errorCode = error.error || "invalid_grant";
|
|
1807
2012
|
errorDescription = error.error_description;
|
|
1808
|
-
} catch (
|
|
2013
|
+
} catch (e8) {
|
|
1809
2014
|
errorDescription = `Upstream returned HTTP ${tokenResponse.status} ${tokenResponse.statusText}`;
|
|
1810
2015
|
}
|
|
1811
2016
|
throw new OAuthProxyError(errorCode, errorDescription);
|
|
@@ -2030,6 +2235,21 @@ var OAuthProxy = (_class4 = class {
|
|
|
2030
2235
|
}
|
|
2031
2236
|
return response;
|
|
2032
2237
|
}
|
|
2238
|
+
/**
|
|
2239
|
+
* Defence in depth for the callback and consent-denial redirects: the
|
|
2240
|
+
* transaction's stored callback URL must still be registered for its client.
|
|
2241
|
+
*
|
|
2242
|
+
* Resolution goes through {@link resolveClient}, so a CIMD registration whose
|
|
2243
|
+
* cached copy lapsed mid-flow is re-fetched rather than mistaken for one that
|
|
2244
|
+
* was revoked.
|
|
2245
|
+
*/
|
|
2246
|
+
async isTransactionCallbackRegistered(transaction) {
|
|
2247
|
+
const registeredClient = await this.resolveClient(
|
|
2248
|
+
transaction.clientId,
|
|
2249
|
+
transaction.clientCallbackUrl
|
|
2250
|
+
);
|
|
2251
|
+
return _nullishCoalesce(_optionalChain([registeredClient, 'optionalAccess', _17 => _17.redirectUris, 'access', _18 => _18.includes, 'call', _19 => _19(transaction.clientCallbackUrl)]), () => ( false));
|
|
2252
|
+
}
|
|
2033
2253
|
/**
|
|
2034
2254
|
* Match URI against pattern (supports wildcards)
|
|
2035
2255
|
*/
|
|
@@ -2140,7 +2360,7 @@ var OAuthProxy = (_class4 = class {
|
|
|
2140
2360
|
const error = await tokenResponse.json();
|
|
2141
2361
|
errorCode = error.error || "invalid_grant";
|
|
2142
2362
|
errorDescription = error.error_description || "Upstream refresh failed";
|
|
2143
|
-
} catch (
|
|
2363
|
+
} catch (e9) {
|
|
2144
2364
|
errorDescription = `Upstream returned HTTP ${tokenResponse.status} ${tokenResponse.statusText}`;
|
|
2145
2365
|
}
|
|
2146
2366
|
throw new OAuthProxyError(errorCode, errorDescription);
|
|
@@ -2157,6 +2377,32 @@ var OAuthProxy = (_class4 = class {
|
|
|
2157
2377
|
tokenType: tokens.token_type || "Bearer"
|
|
2158
2378
|
};
|
|
2159
2379
|
}
|
|
2380
|
+
/**
|
|
2381
|
+
* Resolve the client behind a `client_id`: a stored registration if there is
|
|
2382
|
+
* one, otherwise — when CIMD is enabled — the client's metadata document.
|
|
2383
|
+
*
|
|
2384
|
+
* A resolved CIMD client is cached like a DCR registration but carries the
|
|
2385
|
+
* `expiresAt` the resolver stamped on it, so the store drops it once it
|
|
2386
|
+
* lapses and the next call re-fetches the document. Every lookup goes
|
|
2387
|
+
* through here, so a lapse always means "re-resolve", never "unknown
|
|
2388
|
+
* client" mid-flow.
|
|
2389
|
+
*/
|
|
2390
|
+
async resolveClient(clientId, requestedRedirectUri) {
|
|
2391
|
+
const registeredClient = await this.stateStore.getRegisteredClientByClientId(clientId);
|
|
2392
|
+
if (registeredClient || !this.config.enableCimd) {
|
|
2393
|
+
return registeredClient;
|
|
2394
|
+
}
|
|
2395
|
+
const resolved = await resolveCimdClient(
|
|
2396
|
+
clientId,
|
|
2397
|
+
requestedRedirectUri,
|
|
2398
|
+
(uri) => this.validateRedirectUri(uri)
|
|
2399
|
+
);
|
|
2400
|
+
if (resolved) {
|
|
2401
|
+
this.stateStore.cacheRegisteredClient(resolved);
|
|
2402
|
+
await this.stateStore.saveRegisteredClient(resolved);
|
|
2403
|
+
}
|
|
2404
|
+
return resolved;
|
|
2405
|
+
}
|
|
2160
2406
|
/**
|
|
2161
2407
|
* Put a claimed refresh-token mapping back after a rotation failed, keeping
|
|
2162
2408
|
* whatever lifetime it had left.
|
|
@@ -2225,7 +2471,7 @@ var OAuthProxy = (_class4 = class {
|
|
|
2225
2471
|
let parsed;
|
|
2226
2472
|
try {
|
|
2227
2473
|
parsed = new URL(uri);
|
|
2228
|
-
} catch (
|
|
2474
|
+
} catch (e10) {
|
|
2229
2475
|
return false;
|
|
2230
2476
|
}
|
|
2231
2477
|
if (parsed.username !== "" || parsed.password !== "") {
|
|
@@ -2290,7 +2536,7 @@ var AuthProvider = class {
|
|
|
2290
2536
|
if (!request) {
|
|
2291
2537
|
return void 0;
|
|
2292
2538
|
}
|
|
2293
|
-
const authHeader = _optionalChain([request, 'access',
|
|
2539
|
+
const authHeader = _optionalChain([request, 'access', _20 => _20.headers, 'optionalAccess', _21 => _21.authorization]);
|
|
2294
2540
|
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
2295
2541
|
return void 0;
|
|
2296
2542
|
}
|
|
@@ -2526,7 +2772,7 @@ var DiskStore = (_class5 = class {
|
|
|
2526
2772
|
console.warn(`Failed to read/parse file ${file}, deleting:`, error);
|
|
2527
2773
|
try {
|
|
2528
2774
|
await _promises.rm.call(void 0, _path.join.call(void 0, this.directory, file));
|
|
2529
|
-
} catch (
|
|
2775
|
+
} catch (e11) {
|
|
2530
2776
|
}
|
|
2531
2777
|
}
|
|
2532
2778
|
}
|
|
@@ -2606,7 +2852,7 @@ var DiskStore = (_class5 = class {
|
|
|
2606
2852
|
await this.ensureDirectory();
|
|
2607
2853
|
const files = await _promises.readdir.call(void 0, this.directory);
|
|
2608
2854
|
return files.filter((f) => f.endsWith(this.fileExtension)).length;
|
|
2609
|
-
} catch (
|
|
2855
|
+
} catch (e12) {
|
|
2610
2856
|
return 0;
|
|
2611
2857
|
}
|
|
2612
2858
|
}
|
|
@@ -2830,4 +3076,4 @@ Original error: ${error.message}`
|
|
|
2830
3076
|
|
|
2831
3077
|
|
|
2832
3078
|
exports.getAuthSession = getAuthSession; exports.requireAll = requireAll; exports.requireAny = requireAny; exports.requireAuth = requireAuth; exports.requireRole = requireRole; exports.requireScopes = requireScopes; exports.DEFAULT_ACCESS_TOKEN_TTL = DEFAULT_ACCESS_TOKEN_TTL; exports.DEFAULT_ACCESS_TOKEN_TTL_NO_REFRESH = DEFAULT_ACCESS_TOKEN_TTL_NO_REFRESH; exports.DEFAULT_REFRESH_TOKEN_TTL = DEFAULT_REFRESH_TOKEN_TTL; exports.DEFAULT_AUTHORIZATION_CODE_TTL = DEFAULT_AUTHORIZATION_CODE_TTL; exports.DEFAULT_TRANSACTION_TTL = DEFAULT_TRANSACTION_TTL; exports.ConsentManager = ConsentManager; exports.JWTIssuer = JWTIssuer; exports.PKCEUtils = PKCEUtils; exports.EncryptedTokenStorage = EncryptedTokenStorage; exports.MemoryTokenStorage = MemoryTokenStorage; exports.OAuthProxy = OAuthProxy; exports.OAuthProxyError = OAuthProxyError; exports.AuthProvider = AuthProvider; exports.AzureProvider = AzureProvider; exports.GitHubProvider = GitHubProvider; exports.GoogleProvider = GoogleProvider; exports.OAuthProvider = OAuthProvider; exports.DiskStore = DiskStore; exports.JWKSVerifier = JWKSVerifier;
|
|
2833
|
-
//# sourceMappingURL=chunk-
|
|
3079
|
+
//# sourceMappingURL=chunk-E3HXGE2O.cjs.map
|