@frontmcp/auth 1.8.2 → 1.8.3

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 (49) hide show
  1. package/authorities/authorities.engine.d.ts +24 -2
  2. package/authorities/authorities.engine.d.ts.map +1 -1
  3. package/authorities/authorities.validation.d.ts +27 -0
  4. package/authorities/authorities.validation.d.ts.map +1 -0
  5. package/authorities/index.d.ts +1 -0
  6. package/authorities/index.d.ts.map +1 -1
  7. package/authorization/dcr-client.registry.d.ts +13 -0
  8. package/authorization/dcr-client.registry.d.ts.map +1 -1
  9. package/esm/index.mjs +497 -85
  10. package/esm/package.json +5 -5
  11. package/index.d.ts +4 -4
  12. package/index.d.ts.map +1 -1
  13. package/index.js +581 -160
  14. package/jwks/jwks.service.d.ts.map +1 -1
  15. package/options/app-auth.schema.d.ts +4 -0
  16. package/options/app-auth.schema.d.ts.map +1 -1
  17. package/options/interfaces.d.ts +48 -7
  18. package/options/interfaces.d.ts.map +1 -1
  19. package/options/orchestrated.schema.d.ts +8 -0
  20. package/options/orchestrated.schema.d.ts.map +1 -1
  21. package/options/schema.d.ts +4 -0
  22. package/options/schema.d.ts.map +1 -1
  23. package/options/shared.schemas.d.ts +2 -0
  24. package/options/shared.schemas.d.ts.map +1 -1
  25. package/package.json +5 -5
  26. package/session/authorization.store.d.ts +19 -3
  27. package/session/authorization.store.d.ts.map +1 -1
  28. package/session/federated-auth.session.d.ts +16 -0
  29. package/session/federated-auth.session.d.ts.map +1 -1
  30. package/session/index.d.ts +4 -0
  31. package/session/index.d.ts.map +1 -1
  32. package/session/pending-login.d.ts +23 -0
  33. package/session/pending-login.d.ts.map +1 -0
  34. package/session/signin-binding.d.ts +39 -0
  35. package/session/signin-binding.d.ts.map +1 -0
  36. package/ui/base-layout.d.ts +26 -0
  37. package/ui/base-layout.d.ts.map +1 -1
  38. package/ui/index.d.ts +2 -2
  39. package/ui/index.d.ts.map +1 -1
  40. package/ui/local-login.helper.d.ts +9 -2
  41. package/ui/local-login.helper.d.ts.map +1 -1
  42. package/ui/templates.d.ts +5 -4
  43. package/ui/templates.d.ts.map +1 -1
  44. package/utils/index.d.ts +2 -0
  45. package/utils/index.d.ts.map +1 -1
  46. package/utils/redirect-uri.utils.d.ts +16 -0
  47. package/utils/redirect-uri.utils.d.ts.map +1 -0
  48. package/utils/scope-grant.utils.d.ts +11 -0
  49. package/utils/scope-grant.utils.d.ts.map +1 -0
package/index.js CHANGED
@@ -248,6 +248,7 @@ __export(index_exports, {
248
248
  AuthoritiesProfileRegistry: () => AuthoritiesProfileRegistry,
249
249
  AuthorityDeniedError: () => AuthorityDeniedError,
250
250
  AuthorizationBase: () => AuthorizationBase,
251
+ BUILT_IN_AUTH_PAGE_CSP: () => BUILT_IN_AUTH_PAGE_CSP,
251
252
  CDN: () => CDN,
252
253
  CREDENTIALS_ACCESSOR: () => CREDENTIALS_ACCESSOR,
253
254
  CimdClientIdMismatchError: () => CimdClientIdMismatchError,
@@ -262,6 +263,7 @@ __export(index_exports, {
262
263
  CredentialProviderAlreadyRegisteredError: () => CredentialProviderAlreadyRegisteredError,
263
264
  CredentialStorageError: () => CredentialStorageError,
264
265
  CredentialsAccessorImpl: () => CredentialsAccessorImpl,
266
+ DEFAULT_ALLOWED_SCOPES: () => DEFAULT_ALLOWED_SCOPES,
265
267
  DEFAULT_INCREMENTAL_TICKET_TTL_MS: () => DEFAULT_INCREMENTAL_TICKET_TTL_MS,
266
268
  DEFAULT_RESUME_TTL_MS: () => DEFAULT_RESUME_TTL_MS,
267
269
  DEFAULT_THEME: () => DEFAULT_THEME,
@@ -269,9 +271,9 @@ __export(index_exports, {
269
271
  EagerCredentialLoader: () => EagerCredentialLoader,
270
272
  ElicitationSecretRequiredError: () => ElicitationSecretRequiredError,
271
273
  EncryptedRedisVault: () => EncryptedRedisVault,
272
- EncryptedStorageError: () => import_utils24.EncryptedStorageError,
274
+ EncryptedStorageError: () => import_utils26.EncryptedStorageError,
273
275
  EncryptedStorageSecureStoreBackend: () => EncryptedStorageSecureStoreBackend,
274
- EncryptedTypedStorage: () => import_utils24.EncryptedTypedStorage,
276
+ EncryptedTypedStorage: () => import_utils26.EncryptedTypedStorage,
275
277
  EncryptionContextNotSetError: () => EncryptionContextNotSetError,
276
278
  EncryptionKeyNotConfiguredError: () => EncryptionKeyNotConfiguredError,
277
279
  FetchCredentialMiddleware: () => FetchCredentialMiddleware,
@@ -300,6 +302,9 @@ __export(index_exports, {
300
302
  RedisAuthorizationStore: () => RedisAuthorizationStore,
301
303
  RedisSessionStore: () => RedisSessionStore,
302
304
  SECURE_STORE_ACCESSOR: () => SECURE_STORE_ACCESSOR,
305
+ SIGNIN_BINDING_COOKIE_PREFIX: () => SIGNIN_BINDING_COOKIE_PREFIX,
306
+ SIGNIN_BINDING_MAX_AGE_SECONDS: () => SIGNIN_BINDING_MAX_AGE_SECONDS,
307
+ SIGNIN_BINDING_SECURE_COOKIE_PREFIX: () => SIGNIN_BINDING_SECURE_COOKIE_PREFIX,
303
308
  ScopeDeniedError: () => ScopeDeniedError,
304
309
  SecureStoreAccessorImpl: () => SecureStoreAccessorImpl,
305
310
  SessionCredentialVault: () => SessionCredentialVault,
@@ -319,7 +324,7 @@ __export(index_exports, {
319
324
  TokenVault: () => TokenVault,
320
325
  TransparentAuthorization: () => TransparentAuthorization,
321
326
  TransportIdGenerator: () => TransportIdGenerator,
322
- TypedStorage: () => import_utils23.TypedStorage,
327
+ TypedStorage: () => import_utils25.TypedStorage,
323
328
  VaultEncryption: () => VaultEncryption,
324
329
  VaultLoadError: () => VaultLoadError,
325
330
  VaultNotFoundError: () => VaultNotFoundError,
@@ -375,6 +380,7 @@ __export(index_exports, {
375
380
  buildToolConsentPage: () => buildToolConsentPage,
376
381
  buildUnauthorizedHeader: () => buildUnauthorizedHeader,
377
382
  buildWwwAuthenticate: () => buildWwwAuthenticate,
383
+ builtInAuthPageHeaders: () => builtInAuthPageHeaders,
378
384
  centeredCardLayout: () => centeredCardLayout,
379
385
  checkSsrfProtection: () => checkSsrfProtection,
380
386
  cimdCacheConfigSchema: () => cimdCacheConfigSchema,
@@ -394,6 +400,7 @@ __export(index_exports, {
394
400
  createLayout: () => createLayout,
395
401
  createSecureStore: () => createSecureStore,
396
402
  createSessionCredentialVault: () => createSessionCredentialVault,
403
+ createSigninBinding: () => createSigninBinding,
397
404
  createTokenStorageAdapter: () => createTokenStorageAdapter,
398
405
  credentialProviderConfigSchema: () => credentialProviderConfigSchema,
399
406
  credentialSchema: () => credentialSchema,
@@ -401,9 +408,9 @@ __export(index_exports, {
401
408
  credentialTypeSchema: () => credentialTypeSchema,
402
409
  customCredentialSchema: () => customCredentialSchema,
403
410
  decodeJwtPayloadSafe: () => decodeJwtPayloadSafe,
404
- decryptAesGcm: () => import_utils34.decryptAesGcm,
411
+ decryptAesGcm: () => import_utils36.decryptAesGcm,
405
412
  decryptSessionJson: () => decryptSessionJson,
406
- decryptValue: () => import_utils34.decryptValue,
413
+ decryptValue: () => import_utils36.decryptValue,
407
414
  defaultSessionRateLimiter: () => defaultSessionRateLimiter,
408
415
  deriveAuthorizationId: () => deriveAuthorizationId,
409
416
  deriveExpectedAudience: () => deriveExpectedAudience,
@@ -411,9 +418,9 @@ __export(index_exports, {
411
418
  deriveTypedUser: () => deriveTypedUser,
412
419
  detectAuthProviders: () => detectAuthProviders,
413
420
  detectedAuthProviderSchema: () => detectedAuthProviderSchema,
414
- encryptAesGcm: () => import_utils34.encryptAesGcm,
421
+ encryptAesGcm: () => import_utils36.encryptAesGcm,
415
422
  encryptJson: () => encryptJson,
416
- encryptValue: () => import_utils34.encryptValue,
423
+ encryptValue: () => import_utils36.encryptValue,
417
424
  encryptedBlobSchema: () => encryptedBlobSchema,
418
425
  encryptedDataSchema: () => encryptedDataSchema,
419
426
  encryptedVaultEntrySchema: () => encryptedVaultEntrySchema,
@@ -426,6 +433,8 @@ __export(index_exports, {
426
433
  federatedLoginStateSchema: () => federatedLoginStateSchema,
427
434
  federatedProviderItemSchema: () => federatedProviderItemSchema,
428
435
  federatedSelectionSchema: () => federatedSelectionSchema,
436
+ findAuthoritiesProfileProblems: () => findAuthoritiesProfileProblems,
437
+ findAuthoritiesRuleProblems: () => findAuthoritiesRuleProblems,
429
438
  flatRemoteProviderFields: () => flatRemoteProviderFields,
430
439
  fromSessionRecord: () => fromSessionRecord,
431
440
  generateAuthorizationCode: () => generateAuthorizationCode,
@@ -437,15 +446,17 @@ __export(index_exports, {
437
446
  getProviderApps: () => getProviderApps,
438
447
  getProviderScopes: () => getProviderScopes,
439
448
  getTokenSignatureFingerprint: () => getTokenSignatureFingerprint,
449
+ grantScopes: () => grantScopes,
440
450
  hasOnlyLocalhostRedirectUris: () => hasOnlyLocalhostRedirectUris,
441
451
  headerApplier: () => headerApplier,
442
- hkdfSha256: () => import_utils34.hkdfSha256,
452
+ hkdfSha256: () => import_utils36.hkdfSha256,
443
453
  incrementalAuthConfigSchema: () => incrementalAuthConfigSchema,
444
454
  isAnonymousSubject: () => isAnonymousSubject,
445
455
  isCimdClientId: () => isCimdClientId,
446
456
  isFrontMcpCredentials: () => isFrontMcpCredentials,
447
457
  isJwt: () => isJwt,
448
458
  isLocalMode: () => isLocalMode,
459
+ isLoopbackRedirectUri: () => isLoopbackRedirectUri,
449
460
  isOrchestratedLocal: () => isOrchestratedLocal,
450
461
  isOrchestratedMode: () => isOrchestratedMode,
451
462
  isOrchestratedRemote: () => isOrchestratedRemote,
@@ -454,7 +465,7 @@ __export(index_exports, {
454
465
  isRedisTokenStorage: () => isRedisTokenStorage,
455
466
  isRemoteMode: () => isRemoteMode,
456
467
  isSessionComplete: () => isSessionComplete,
457
- isSignedSession: () => import_utils16.isSignedData,
468
+ isSignedSession: () => import_utils18.isSignedData,
458
469
  isSoonExpiring: () => isSoonExpiring,
459
470
  isSoonExpiringProvider: () => isSoonExpiringProvider,
460
471
  isSqliteTokenStorage: () => isSqliteTokenStorage,
@@ -475,6 +486,7 @@ __export(index_exports, {
475
486
  noopLogger: () => noopLogger,
476
487
  normalizeIssuer: () => normalizeIssuer,
477
488
  oauthCredentialSchema: () => oauthCredentialSchema,
489
+ openPendingLogin: () => openPendingLogin,
478
490
  orchestratedLocalSchema: () => orchestratedLocalSchema,
479
491
  orchestratedRemoteSchema: () => orchestratedRemoteSchema,
480
492
  parseAuthOptions: () => parseAuthOptions,
@@ -502,6 +514,7 @@ __export(index_exports, {
502
514
  resolveRequiredScopes: () => resolveRequiredScopes,
503
515
  resourceIdRefSchema: () => resourceIdRefSchema,
504
516
  safeDecrypt: () => safeDecrypt,
517
+ sealPendingLogin: () => sealPendingLogin,
505
518
  secureStoreConfigSchema: () => secureStoreConfigSchema,
506
519
  secureStoreCustomBackendSchema: () => secureStoreCustomBackendSchema,
507
520
  secureStoreEncryptionSchema: () => secureStoreEncryptionSchema,
@@ -512,6 +525,8 @@ __export(index_exports, {
512
525
  signCredentialResumeToken: () => signCredentialResumeToken,
513
526
  signIncrementalAuthTicket: () => signIncrementalAuthTicket,
514
527
  signSession: () => signSession,
528
+ signinBindingCookieName: () => signinBindingCookieName,
529
+ signinBindingMatches: () => signinBindingMatches,
515
530
  skippedAppBehaviorSchema: () => skippedAppBehaviorSchema,
516
531
  sseTransportStateSchema: () => sseTransportStateSchema,
517
532
  sshKeyCredentialSchema: () => sshKeyCredentialSchema,
@@ -544,7 +559,8 @@ __export(index_exports, {
544
559
  verifyOrParseSession: () => verifyOrParseSession,
545
560
  verifyPkce: () => verifyPkce,
546
561
  verifySession: () => verifySession,
547
- wideLayout: () => wideLayout
562
+ wideLayout: () => wideLayout,
563
+ withoutSecretFieldValues: () => withoutSecretFieldValues
548
564
  });
549
565
  module.exports = __toCommonJS(index_exports);
550
566
 
@@ -1021,7 +1037,9 @@ var JwksService = class {
1021
1037
  // Pin to asymmetric algorithms so a symmetric key in the provider JWKS
1022
1038
  // can't be used to accept an HMAC-signed token, and `none` is refused.
1023
1039
  algorithms: [...TRANSPARENT_JWT_ALGS],
1024
- ...issuerConstraint ? { issuer: issuerConstraint } : {}
1040
+ ...issuerConstraint ? { issuer: issuerConstraint } : {},
1041
+ // A bearer token without `exp` would be valid forever (#272).
1042
+ requiredClaims: ["exp"]
1025
1043
  });
1026
1044
  return {
1027
1045
  ok: true,
@@ -1124,7 +1142,10 @@ var JwksService = class {
1124
1142
  }
1125
1143
  }
1126
1144
  const nowSec = Math.floor(Date.now() / 1e3);
1127
- if (payload.exp !== void 0 && (typeof payload.exp !== "number" || !Number.isFinite(payload.exp) || payload.exp < nowSec)) {
1145
+ if (payload.exp === void 0) {
1146
+ return { ok: false, error: "missing_exp" };
1147
+ }
1148
+ if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp) || payload.exp <= nowSec) {
1128
1149
  return { ok: false, error: "token_expired" };
1129
1150
  }
1130
1151
  if (payload.nbf !== void 0 && (typeof payload.nbf !== "number" || !Number.isFinite(payload.nbf) || payload.nbf > nowSec)) {
@@ -1380,6 +1401,26 @@ var CDN = {
1380
1401
  stylesheet: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
1381
1402
  }
1382
1403
  };
1404
+ var BUILT_IN_AUTH_PAGE_CSP = [
1405
+ "default-src 'self'",
1406
+ "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net",
1407
+ "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
1408
+ "font-src 'self' https://fonts.gstatic.com",
1409
+ "img-src 'self' data: https:",
1410
+ "connect-src 'self'",
1411
+ "object-src 'none'",
1412
+ "base-uri 'none'",
1413
+ "frame-ancestors 'none'"
1414
+ ].join("; ");
1415
+ function builtInAuthPageHeaders(options = {}) {
1416
+ return {
1417
+ ...options.csp === false ? {} : { "Content-Security-Policy": BUILT_IN_AUTH_PAGE_CSP },
1418
+ "X-Frame-Options": "DENY",
1419
+ "X-Content-Type-Options": "nosniff",
1420
+ "Referrer-Policy": "same-origin",
1421
+ "Cache-Control": "no-store"
1422
+ };
1423
+ }
1383
1424
  var DEFAULT_THEME = {
1384
1425
  colors: {
1385
1426
  primary: "#3b82f6",
@@ -1617,7 +1658,7 @@ function buildIncrementalAuthPage(params) {
1617
1658
  </div>
1618
1659
  </div>
1619
1660
 
1620
- <form method="GET" action="${escapeHtml3(callbackPath)}" class="flex gap-3 pt-4 border-t border-gray-100">
1661
+ <form method="POST" action="${escapeHtml3(callbackPath)}" class="flex gap-3 pt-4 border-t border-gray-100">
1621
1662
  <input type="hidden" name="pending_auth_id" value="${escapeHtml3(sessionHint)}">
1622
1663
  <button type="button" onclick="window.close()"
1623
1664
  class="flex-1 px-4 py-2.5 text-gray-600 hover:text-gray-900 hover:bg-gray-100 font-medium rounded-lg transition-colors">
@@ -1663,7 +1704,7 @@ function buildFederatedLoginPage(params) {
1663
1704
  ${escapeHtml3(clientName)} uses multiple authentication providers. Select which ones you want to authorize.
1664
1705
  </p>
1665
1706
 
1666
- <form method="GET" action="${escapeHtml3(callbackPath)}" id="federated-form">
1707
+ <form method="POST" action="${escapeHtml3(callbackPath)}" id="federated-form">
1667
1708
  <input type="hidden" name="pending_auth_id" value="${escapeHtml3(pendingAuthId)}">
1668
1709
  <input type="hidden" name="federated" value="true">
1669
1710
 
@@ -1808,7 +1849,7 @@ function buildToolConsentPage(params) {
1808
1849
  ${errorBanner}
1809
1850
  ${userInfo}
1810
1851
 
1811
- <form method="GET" action="${escapeHtml3(callbackPath)}" id="consent-form">
1852
+ <form method="POST" action="${escapeHtml3(callbackPath)}" id="consent-form">
1812
1853
  <input type="hidden" name="csrf" value="${escapeHtml3(csrfToken)}">
1813
1854
  <input type="hidden" name="pending_auth_id" value="${escapeHtml3(pendingAuthId)}">
1814
1855
  <input type="hidden" name="consent_submitted" value="1">
@@ -1906,7 +1947,7 @@ function buildLoginPage(params) {
1906
1947
 
1907
1948
  ${scopesHtml}
1908
1949
 
1909
- <form method="GET" action="${escapeHtml3(callbackPath)}">
1950
+ <form method="POST" action="${escapeHtml3(callbackPath)}">
1910
1951
  <input type="hidden" name="pending_auth_id" value="${escapeHtml3(pendingAuthId)}">
1911
1952
  ${fieldsHtml}
1912
1953
 
@@ -2003,7 +2044,17 @@ function toLoginExtraFields(fields, values) {
2003
2044
  value: values?.[name]
2004
2045
  }));
2005
2046
  }
2006
- function renderLocalLoginPage(login, ctx, values) {
2047
+ function withoutSecretFieldValues(values, fields) {
2048
+ if (!values) return void 0;
2049
+ const kept = {};
2050
+ for (const [name, value] of Object.entries(values)) {
2051
+ if (fields?.[name]?.type === "password") continue;
2052
+ kept[name] = value;
2053
+ }
2054
+ return kept;
2055
+ }
2056
+ function renderLocalLoginPage(login, ctx, submitted) {
2057
+ const values = withoutSecretFieldValues(submitted, login?.fields);
2007
2058
  if (login?.render) {
2008
2059
  return login.render({ ...ctx, values });
2009
2060
  }
@@ -2110,7 +2161,8 @@ function buildPendingRecord(params) {
2110
2161
  priorAuthorizedAppIds: params.priorAuthorizedAppIds,
2111
2162
  federatedLogin: params.federatedLogin,
2112
2163
  consent: params.consent,
2113
- authUiCsrf: params.authUiCsrf
2164
+ authUiCsrf: params.authUiCsrf,
2165
+ signinBinding: params.signinBinding
2114
2166
  };
2115
2167
  }
2116
2168
  function buildRefreshTokenRecord(params) {
@@ -2340,8 +2392,58 @@ var RedisAuthorizationStore = class {
2340
2392
  }
2341
2393
  };
2342
2394
 
2343
- // libs/auth/src/session/storage-authorization.store.ts
2395
+ // libs/auth/src/session/pending-login.ts
2344
2396
  var import_utils6 = require("@frontmcp/utils");
2397
+ var SALT = new TextEncoder().encode("frontmcp-pending-login");
2398
+ function keyFor(secret, pendingAuthId) {
2399
+ return (0, import_utils6.hkdfSha256)(secret, SALT, new TextEncoder().encode(`pending-login:${pendingAuthId}`), 32);
2400
+ }
2401
+ function sealPendingLogin(secret, pendingAuthId, state) {
2402
+ const iv = (0, import_utils6.randomBytes)(12);
2403
+ const { ciphertext, tag } = (0, import_utils6.encryptAesGcm)(
2404
+ keyFor(secret, pendingAuthId),
2405
+ new TextEncoder().encode(JSON.stringify(state)),
2406
+ iv
2407
+ );
2408
+ return [iv, tag, ciphertext].map((part) => (0, import_utils6.base64urlEncode)(part)).join(".");
2409
+ }
2410
+ function openPendingLogin(secret, pendingAuthId, sealed) {
2411
+ try {
2412
+ const [iv, tag, ciphertext] = sealed.split(".").map((part) => (0, import_utils6.base64urlDecode)(part));
2413
+ if (!iv || !tag || !ciphertext) return void 0;
2414
+ const plaintext = (0, import_utils6.decryptAesGcm)(keyFor(secret, pendingAuthId), ciphertext, iv, tag);
2415
+ const state = JSON.parse(new TextDecoder().decode(plaintext));
2416
+ return typeof state?.sub === "string" && state.sub.length > 0 ? state : void 0;
2417
+ } catch {
2418
+ return void 0;
2419
+ }
2420
+ }
2421
+
2422
+ // libs/auth/src/session/signin-binding.ts
2423
+ var import_utils7 = require("@frontmcp/utils");
2424
+ var SIGNIN_BINDING_COOKIE_PREFIX = "frontmcp_signin_";
2425
+ var SIGNIN_BINDING_SECURE_COOKIE_PREFIX = `__Host-${SIGNIN_BINDING_COOKIE_PREFIX}`;
2426
+ var SIGNIN_BINDING_MAX_AGE_SECONDS = 30 * 60;
2427
+ function signinBindingCookieName(pendingAuthId, options = {}) {
2428
+ const prefix = options.secure ? SIGNIN_BINDING_SECURE_COOKIE_PREFIX : SIGNIN_BINDING_COOKIE_PREFIX;
2429
+ return `${prefix}${(0, import_utils7.sha256Hex)(`signin:${pendingAuthId}`).slice(0, 16)}`;
2430
+ }
2431
+ function createSigninBinding(pendingAuthId, options = {}) {
2432
+ const value = (0, import_utils7.base64urlEncode)((0, import_utils7.randomBytes)(32));
2433
+ return { cookieName: signinBindingCookieName(pendingAuthId, options), value, hash: (0, import_utils7.sha256Hex)(value) };
2434
+ }
2435
+ function signinBindingMatches(cookieHeader, pendingAuthId, expectedHash, options = {}) {
2436
+ if (!expectedHash) return false;
2437
+ const value = (0, import_utils7.getCookie)(cookieHeader, signinBindingCookieName(pendingAuthId, options));
2438
+ if (!value) return false;
2439
+ const encoder = new TextEncoder();
2440
+ const actual = encoder.encode((0, import_utils7.sha256Hex)(value));
2441
+ const expected = encoder.encode(expectedHash);
2442
+ return actual.length === expected.length && (0, import_utils7.timingSafeEqual)(actual, expected);
2443
+ }
2444
+
2445
+ // libs/auth/src/session/storage-authorization.store.ts
2446
+ var import_utils8 = require("@frontmcp/utils");
2345
2447
  var StorageAuthorizationStore = class {
2346
2448
  codeStore;
2347
2449
  pendingStore;
@@ -2352,9 +2454,9 @@ var StorageAuthorizationStore = class {
2352
2454
  this.namespace = options.namespace ?? "oauth";
2353
2455
  this.storageIsNamespaced = this.isNamespacedStorage(storage);
2354
2456
  const base = this.storageIsNamespaced ? storage.namespace(this.namespace) : storage;
2355
- this.codeStore = new import_utils6.TypedStorage(base);
2356
- this.pendingStore = new import_utils6.TypedStorage(base);
2357
- this.refreshStore = new import_utils6.TypedStorage(base);
2457
+ this.codeStore = new import_utils8.TypedStorage(base);
2458
+ this.pendingStore = new import_utils8.TypedStorage(base);
2459
+ this.refreshStore = new import_utils8.TypedStorage(base);
2358
2460
  }
2359
2461
  // ============================================
2360
2462
  // Record builders (pure; shared with in-memory store)
@@ -2727,11 +2829,11 @@ var authorizationVaultEntrySchema = import_lazy_zod2.z.object({
2727
2829
  });
2728
2830
 
2729
2831
  // libs/auth/src/session/session-credential-vault.ts
2730
- var import_utils8 = require("@frontmcp/utils");
2832
+ var import_utils10 = require("@frontmcp/utils");
2731
2833
 
2732
2834
  // libs/auth/src/session/vault-encryption.ts
2733
2835
  var import_lazy_zod3 = require("@frontmcp/lazy-zod");
2734
- var import_utils7 = require("@frontmcp/utils");
2836
+ var import_utils9 = require("@frontmcp/utils");
2735
2837
  var encryptedDataSchema = import_lazy_zod3.z.object({
2736
2838
  /** Version for future algorithm changes */
2737
2839
  v: import_lazy_zod3.z.literal(1),
@@ -2771,7 +2873,7 @@ var VaultEncryption = class {
2771
2873
  ];
2772
2874
  const ikm = new TextEncoder().encode(ikmParts.join(""));
2773
2875
  const infoBytes = new TextEncoder().encode(this.hkdfInfo);
2774
- const key = await (0, import_utils7.hkdfSha256)(ikm, new Uint8Array(0), infoBytes, 32);
2876
+ const key = await (0, import_utils9.hkdfSha256)(ikm, new Uint8Array(0), infoBytes, 32);
2775
2877
  return key;
2776
2878
  }
2777
2879
  /**
@@ -2798,7 +2900,7 @@ var VaultEncryption = class {
2798
2900
  ];
2799
2901
  const ikm = new TextEncoder().encode(ikmParts.join(""));
2800
2902
  const infoBytes = new TextEncoder().encode(this.hkdfInfo);
2801
- const key = await (0, import_utils7.hkdfSha256)(ikm, new Uint8Array(0), infoBytes, 32);
2903
+ const key = await (0, import_utils9.hkdfSha256)(ikm, new Uint8Array(0), infoBytes, 32);
2802
2904
  return key;
2803
2905
  }
2804
2906
  /**
@@ -2812,14 +2914,14 @@ var VaultEncryption = class {
2812
2914
  if (key.length !== 32) {
2813
2915
  throw new Error("Encryption key must be 32 bytes");
2814
2916
  }
2815
- const iv = (0, import_utils7.randomBytes)(12);
2816
- const { ciphertext, tag } = await (0, import_utils7.encryptAesGcm)(key, new TextEncoder().encode(plaintext), iv);
2917
+ const iv = (0, import_utils9.randomBytes)(12);
2918
+ const { ciphertext, tag } = await (0, import_utils9.encryptAesGcm)(key, new TextEncoder().encode(plaintext), iv);
2817
2919
  return {
2818
2920
  v: 1,
2819
2921
  alg: "aes-256-gcm",
2820
- iv: (0, import_utils7.base64urlEncode)(iv),
2821
- ct: (0, import_utils7.base64urlEncode)(ciphertext),
2822
- tag: (0, import_utils7.base64urlEncode)(tag)
2922
+ iv: (0, import_utils9.base64urlEncode)(iv),
2923
+ ct: (0, import_utils9.base64urlEncode)(ciphertext),
2924
+ tag: (0, import_utils9.base64urlEncode)(tag)
2823
2925
  };
2824
2926
  }
2825
2927
  /**
@@ -2839,11 +2941,11 @@ var VaultEncryption = class {
2839
2941
  throw new Error("Invalid encrypted data format");
2840
2942
  }
2841
2943
  const { iv, ct, tag } = parsed.data;
2842
- const ivBuffer = (0, import_utils7.base64urlDecode)(iv);
2843
- const ciphertext = (0, import_utils7.base64urlDecode)(ct);
2844
- const tagBuffer = (0, import_utils7.base64urlDecode)(tag);
2944
+ const ivBuffer = (0, import_utils9.base64urlDecode)(iv);
2945
+ const ciphertext = (0, import_utils9.base64urlDecode)(ct);
2946
+ const tagBuffer = (0, import_utils9.base64urlDecode)(tag);
2845
2947
  try {
2846
- const plaintext = await (0, import_utils7.decryptAesGcm)(key, ciphertext, ivBuffer, tagBuffer);
2948
+ const plaintext = await (0, import_utils9.decryptAesGcm)(key, ciphertext, ivBuffer, tagBuffer);
2847
2949
  return new TextDecoder().decode(plaintext);
2848
2950
  } catch (_error) {
2849
2951
  throw new Error("Decryption failed: invalid key or corrupted data");
@@ -2983,7 +3085,7 @@ var SessionCredentialVault = class {
2983
3085
  * it expires via TTL when configured.
2984
3086
  */
2985
3087
  async rotateVault(sub) {
2986
- const vaultId = (0, import_utils8.randomUUID)();
3088
+ const vaultId = (0, import_utils10.randomUUID)();
2987
3089
  await this.storage.set(this.indexKey(sub), vaultId, this.credentialSetOpts);
2988
3090
  return vaultId;
2989
3091
  }
@@ -3132,19 +3234,19 @@ var SessionCredentialVault = class {
3132
3234
  }
3133
3235
  };
3134
3236
  function resolveVaultPepper(logger) {
3135
- const fromEnv = (0, import_utils8.getEnv)("VAULT_SECRET") ?? (0, import_utils8.getEnv)("JWT_SECRET");
3237
+ const fromEnv = (0, import_utils10.getEnv)("VAULT_SECRET") ?? (0, import_utils10.getEnv)("JWT_SECRET");
3136
3238
  if (fromEnv && fromEnv.trim().length > 0) return fromEnv;
3137
3239
  logger.warn(
3138
3240
  "SessionCredentialVault: neither VAULT_SECRET nor JWT_SECRET is set; using a random per-process pepper. Stored credentials will NOT survive a restart and cannot be shared across instances. Set VAULT_SECRET (or JWT_SECRET) for persistent, multi-instance credential storage."
3139
3241
  );
3140
- return (0, import_utils8.randomUUID)() + (0, import_utils8.randomUUID)();
3242
+ return (0, import_utils10.randomUUID)() + (0, import_utils10.randomUUID)();
3141
3243
  }
3142
3244
  function errMsg(err) {
3143
3245
  return err instanceof Error ? err.message : String(err);
3144
3246
  }
3145
3247
 
3146
3248
  // libs/auth/src/session/token-storage.factory.ts
3147
- var import_utils9 = require("@frontmcp/utils");
3249
+ var import_utils11 = require("@frontmcp/utils");
3148
3250
  function isRedisTokenStorage(config) {
3149
3251
  return typeof config === "object" && config !== null && "redis" in config && !!config.redis;
3150
3252
  }
@@ -3156,7 +3258,7 @@ function isPersistentTokenStorage(config) {
3156
3258
  }
3157
3259
  async function createTokenStorageAdapter(config) {
3158
3260
  if (config === void 0 || config === "memory") {
3159
- const adapter = new import_utils9.MemoryStorageAdapter();
3261
+ const adapter = new import_utils11.MemoryStorageAdapter();
3160
3262
  await adapter.connect();
3161
3263
  return adapter;
3162
3264
  }
@@ -3173,14 +3275,14 @@ async function createTokenStorageAdapter(config) {
3173
3275
  }
3174
3276
  if (isRedisTokenStorage(config)) {
3175
3277
  const { host, port, password, db, tls, keyPrefix } = config.redis;
3176
- const adapter = new import_utils9.RedisStorageAdapter({
3278
+ const adapter = new import_utils11.RedisStorageAdapter({
3177
3279
  config: { host, port, password, db, tls },
3178
3280
  keyPrefix: keyPrefix ?? ""
3179
3281
  });
3180
3282
  await adapter.connect();
3181
3283
  return adapter;
3182
3284
  }
3183
- const fallback = new import_utils9.MemoryStorageAdapter();
3285
+ const fallback = new import_utils11.MemoryStorageAdapter();
3184
3286
  await fallback.connect();
3185
3287
  return fallback;
3186
3288
  }
@@ -3200,7 +3302,7 @@ async function createSessionCredentialVault(options) {
3200
3302
  }
3201
3303
 
3202
3304
  // libs/auth/src/session/credential-resume-link.ts
3203
- var import_utils10 = require("@frontmcp/utils");
3305
+ var import_utils12 = require("@frontmcp/utils");
3204
3306
  var DEFAULT_RESUME_TTL_MS = 6e5;
3205
3307
  function signCredentialResumeToken(input, secret) {
3206
3308
  const payload = {
@@ -3209,7 +3311,7 @@ function signCredentialResumeToken(input, secret) {
3209
3311
  ...input.context !== void 0 ? { context: input.context } : {},
3210
3312
  exp: Date.now() + (input.ttlMs ?? DEFAULT_RESUME_TTL_MS)
3211
3313
  };
3212
- const signedJson = (0, import_utils10.signData)(payload, { secret });
3314
+ const signedJson = (0, import_utils12.signData)(payload, { secret });
3213
3315
  return Buffer.from(signedJson, "utf8").toString("base64url");
3214
3316
  }
3215
3317
  function verifyCredentialResumeToken(token, secret, now = Date.now()) {
@@ -3219,7 +3321,7 @@ function verifyCredentialResumeToken(token, secret, now = Date.now()) {
3219
3321
  } catch {
3220
3322
  return null;
3221
3323
  }
3222
- const payload = (0, import_utils10.verifyData)(signedJson, { secret });
3324
+ const payload = (0, import_utils12.verifyData)(signedJson, { secret });
3223
3325
  if (!payload) return null;
3224
3326
  if (typeof payload.sub !== "string" || typeof payload.key !== "string" || !Number.isFinite(payload.exp)) {
3225
3327
  return null;
@@ -3238,7 +3340,7 @@ function buildCredentialResumeUrl(basePath, token) {
3238
3340
  }
3239
3341
 
3240
3342
  // libs/auth/src/session/incremental-auth-ticket.ts
3241
- var import_utils11 = require("@frontmcp/utils");
3343
+ var import_utils13 = require("@frontmcp/utils");
3242
3344
  var DEFAULT_INCREMENTAL_TICKET_TTL_MS = 3e5;
3243
3345
  function signIncrementalAuthTicket(input, secret) {
3244
3346
  const payload = {
@@ -3246,10 +3348,10 @@ function signIncrementalAuthTicket(input, secret) {
3246
3348
  appId: input.appId,
3247
3349
  ...input.toolId !== void 0 ? { toolId: input.toolId } : {},
3248
3350
  ...input.priorAppIds !== void 0 ? { priorAppIds: input.priorAppIds } : {},
3249
- jti: (0, import_utils11.randomUUID)(),
3351
+ jti: (0, import_utils13.randomUUID)(),
3250
3352
  exp: Date.now() + (input.ttlMs ?? DEFAULT_INCREMENTAL_TICKET_TTL_MS)
3251
3353
  };
3252
- const signedJson = (0, import_utils11.signData)(payload, { secret });
3354
+ const signedJson = (0, import_utils13.signData)(payload, { secret });
3253
3355
  return Buffer.from(signedJson, "utf8").toString("base64url");
3254
3356
  }
3255
3357
  function verifyIncrementalAuthTicket(token, secret, now = Date.now()) {
@@ -3259,7 +3361,7 @@ function verifyIncrementalAuthTicket(token, secret, now = Date.now()) {
3259
3361
  } catch {
3260
3362
  return null;
3261
3363
  }
3262
- const payload = (0, import_utils11.verifyData)(signedJson, { secret });
3364
+ const payload = (0, import_utils13.verifyData)(signedJson, { secret });
3263
3365
  if (!payload) return null;
3264
3366
  if (typeof payload.sub !== "string" || payload.sub.length === 0) return null;
3265
3367
  if (typeof payload.appId !== "string" || payload.appId.length === 0) return null;
@@ -3439,7 +3541,7 @@ function errMsg2(err) {
3439
3541
  var SECURE_STORE_ACCESSOR = /* @__PURE__ */ Symbol.for("frontmcp:SECURE_STORE_ACCESSOR");
3440
3542
 
3441
3543
  // libs/auth/src/session/secure-store-accessor.impl.ts
3442
- var import_utils12 = require("@frontmcp/utils");
3544
+ var import_utils14 = require("@frontmcp/utils");
3443
3545
  var GLOBAL_NAMESPACE = "global";
3444
3546
  var SecureStoreAccessorImpl = class {
3445
3547
  backend;
@@ -3468,12 +3570,12 @@ var SecureStoreAccessorImpl = class {
3468
3570
  return GLOBAL_NAMESPACE;
3469
3571
  case "session": {
3470
3572
  const sid = this.resolveSessionId();
3471
- return sid ? `s:${(0, import_utils12.sha256Hex)(sid).slice(0, 32)}` : void 0;
3573
+ return sid ? `s:${(0, import_utils14.sha256Hex)(sid).slice(0, 32)}` : void 0;
3472
3574
  }
3473
3575
  case "user":
3474
3576
  default: {
3475
3577
  const sub = this.resolveSub();
3476
- return sub ? `u:${(0, import_utils12.sha256Hex)(sub).slice(0, 32)}` : void 0;
3578
+ return sub ? `u:${(0, import_utils14.sha256Hex)(sub).slice(0, 32)}` : void 0;
3477
3579
  }
3478
3580
  }
3479
3581
  }
@@ -3514,7 +3616,7 @@ function errMsg3(err) {
3514
3616
  }
3515
3617
 
3516
3618
  // libs/auth/src/session/secure-store.factory.ts
3517
- var import_utils13 = require("@frontmcp/utils");
3619
+ var import_utils15 = require("@frontmcp/utils");
3518
3620
  function isCustomBackendConfig(config) {
3519
3621
  return typeof config === "object" && config !== null && "backend" in config && !!config.backend;
3520
3622
  }
@@ -3563,7 +3665,7 @@ async function createSecureStore(options) {
3563
3665
  if (storage) {
3564
3666
  adapter = storage;
3565
3667
  } else if (kind === "memory") {
3566
- adapter = new import_utils13.MemoryStorageAdapter();
3668
+ adapter = new import_utils15.MemoryStorageAdapter();
3567
3669
  await adapter.connect();
3568
3670
  } else {
3569
3671
  adapter = await createTokenStorageAdapter(toTokenStorageConfig(config));
@@ -3577,7 +3679,7 @@ async function createSecureStore(options) {
3577
3679
  }
3578
3680
 
3579
3681
  // libs/auth/src/session/token.vault.ts
3580
- var import_utils14 = require("@frontmcp/utils");
3682
+ var import_utils16 = require("@frontmcp/utils");
3581
3683
  var TokenVault = class {
3582
3684
  /** Active key used for new encryptions */
3583
3685
  active;
@@ -3606,14 +3708,14 @@ var TokenVault = class {
3606
3708
  this.keys.set(k.kid, k.key);
3607
3709
  }
3608
3710
  async encrypt(plaintext, opts) {
3609
- const iv = (0, import_utils14.randomBytes)(12);
3610
- const { ciphertext, tag } = await (0, import_utils14.encryptAesGcm)(this.active.key, new TextEncoder().encode(plaintext), iv);
3711
+ const iv = (0, import_utils16.randomBytes)(12);
3712
+ const { ciphertext, tag } = await (0, import_utils16.encryptAesGcm)(this.active.key, new TextEncoder().encode(plaintext), iv);
3611
3713
  return {
3612
3714
  alg: "A256GCM",
3613
3715
  kid: this.active.kid,
3614
- iv: (0, import_utils14.base64urlEncode)(iv),
3615
- tag: (0, import_utils14.base64urlEncode)(tag),
3616
- data: (0, import_utils14.base64urlEncode)(ciphertext),
3716
+ iv: (0, import_utils16.base64urlEncode)(iv),
3717
+ tag: (0, import_utils16.base64urlEncode)(tag),
3718
+ data: (0, import_utils16.base64urlEncode)(ciphertext),
3617
3719
  exp: opts?.exp,
3618
3720
  meta: opts?.meta
3619
3721
  };
@@ -3627,16 +3729,16 @@ var TokenVault = class {
3627
3729
  }
3628
3730
  const key = this.keys.get(blob.kid);
3629
3731
  if (!key) throw new Error(`vault_unknown_kid:${blob.kid}`);
3630
- const iv = (0, import_utils14.base64urlDecode)(blob.iv);
3631
- const tag = (0, import_utils14.base64urlDecode)(blob.tag);
3632
- const data = (0, import_utils14.base64urlDecode)(blob.data);
3633
- const plaintext = await (0, import_utils14.decryptAesGcm)(key, data, iv, tag);
3732
+ const iv = (0, import_utils16.base64urlDecode)(blob.iv);
3733
+ const tag = (0, import_utils16.base64urlDecode)(blob.tag);
3734
+ const data = (0, import_utils16.base64urlDecode)(blob.data);
3735
+ const plaintext = await (0, import_utils16.decryptAesGcm)(key, data, iv, tag);
3634
3736
  return new TextDecoder().decode(plaintext);
3635
3737
  }
3636
3738
  };
3637
3739
 
3638
3740
  // libs/auth/src/session/index.ts
3639
- var import_utils34 = require("@frontmcp/utils");
3741
+ var import_utils36 = require("@frontmcp/utils");
3640
3742
 
3641
3743
  // libs/auth/src/session/transport-session.types.ts
3642
3744
  var import_lazy_zod4 = require("@frontmcp/lazy-zod");
@@ -3732,10 +3834,10 @@ var redisConfigSchema = import_lazy_zod4.z.object({
3732
3834
  });
3733
3835
 
3734
3836
  // libs/auth/src/session/session-crypto.ts
3735
- var import_utils16 = require("@frontmcp/utils");
3837
+ var import_utils18 = require("@frontmcp/utils");
3736
3838
 
3737
3839
  // libs/auth/src/errors/auth-internal.error.ts
3738
- var import_utils15 = require("@frontmcp/utils");
3840
+ var import_utils17 = require("@frontmcp/utils");
3739
3841
  var AuthInternalError = class extends Error {
3740
3842
  /**
3741
3843
  * Unique error ID for tracking in logs.
@@ -3757,7 +3859,7 @@ var AuthInternalError = class extends Error {
3757
3859
  super(message);
3758
3860
  this.name = this.constructor.name;
3759
3861
  this.code = code;
3760
- this.errorId = `err_${(0, import_utils15.bytesToHex)((0, import_utils15.randomBytes)(8))}`;
3862
+ this.errorId = `err_${(0, import_utils17.bytesToHex)((0, import_utils17.randomBytes)(8))}`;
3761
3863
  Error.captureStackTrace(this, this.constructor);
3762
3864
  }
3763
3865
  /**
@@ -3896,9 +3998,9 @@ var AuthFlowError = class extends AuthInternalError {
3896
3998
 
3897
3999
  // libs/auth/src/session/session-crypto.ts
3898
4000
  function getSigningSecret(config) {
3899
- const secret = config?.secret || (0, import_utils16.getEnv)("MCP_SESSION_SECRET");
4001
+ const secret = config?.secret || (0, import_utils18.getEnv)("MCP_SESSION_SECRET");
3900
4002
  if (!secret) {
3901
- if ((0, import_utils16.isProduction)()) {
4003
+ if ((0, import_utils18.isProduction)()) {
3902
4004
  throw new SessionSecretRequiredError("session signing");
3903
4005
  }
3904
4006
  console.warn("[SessionCrypto] MCP_SESSION_SECRET not set. Using insecure default for development only.");
@@ -3910,13 +4012,13 @@ function resolveConfig(config) {
3910
4012
  return { secret: getSigningSecret(config) };
3911
4013
  }
3912
4014
  function signSession(session, config) {
3913
- return (0, import_utils16.signData)(session, resolveConfig(config));
4015
+ return (0, import_utils18.signData)(session, resolveConfig(config));
3914
4016
  }
3915
4017
  function verifySession(signedData, config) {
3916
- return (0, import_utils16.verifyData)(signedData, resolveConfig(config));
4018
+ return (0, import_utils18.verifyData)(signedData, resolveConfig(config));
3917
4019
  }
3918
4020
  function verifyOrParseSession(data, config) {
3919
- return (0, import_utils16.verifyOrParseData)(data, resolveConfig(config));
4021
+ return (0, import_utils18.verifyOrParseData)(data, resolveConfig(config));
3920
4022
  }
3921
4023
 
3922
4024
  // libs/auth/src/session/session-rate-limiter.ts
@@ -4043,7 +4145,7 @@ var SessionRateLimiter = class {
4043
4145
  var defaultSessionRateLimiter = new SessionRateLimiter();
4044
4146
 
4045
4147
  // libs/auth/src/session/session.transport.ts
4046
- var import_utils17 = require("@frontmcp/utils");
4148
+ var import_utils19 = require("@frontmcp/utils");
4047
4149
  var TransportIdGenerator = class {
4048
4150
  /**
4049
4151
  * Create a transport session ID.
@@ -4052,7 +4154,7 @@ var TransportIdGenerator = class {
4052
4154
  * @returns A transport session ID (UUID without dashes)
4053
4155
  */
4054
4156
  static createId() {
4055
- return (0, import_utils17.randomUUID)().replace(/-/g, "");
4157
+ return (0, import_utils19.randomUUID)().replace(/-/g, "");
4056
4158
  }
4057
4159
  };
4058
4160
 
@@ -4101,7 +4203,7 @@ var TinyTtlCache = class {
4101
4203
  };
4102
4204
 
4103
4205
  // libs/auth/src/session/utils/auth-token.utils.ts
4104
- var import_utils18 = require("@frontmcp/utils");
4206
+ var import_utils20 = require("@frontmcp/utils");
4105
4207
  function isJwt(token) {
4106
4208
  if (!token) return false;
4107
4209
  return token.split(".").length === 3;
@@ -4111,7 +4213,7 @@ function getTokenSignatureFingerprint(token) {
4111
4213
  const sig = token.split(".")[2];
4112
4214
  if (sig) return sig;
4113
4215
  }
4114
- return (0, import_utils18.sha256Base64url)(token);
4216
+ return (0, import_utils20.sha256Base64url)(token);
4115
4217
  }
4116
4218
  function extractClaimValue(claims, key, validator) {
4117
4219
  const value = claims[key];
@@ -4143,26 +4245,26 @@ function extractBearerToken(header) {
4143
4245
  }
4144
4246
 
4145
4247
  // libs/auth/src/session/utils/session-crypto.utils.ts
4146
- var import_utils19 = require("@frontmcp/utils");
4248
+ var import_utils21 = require("@frontmcp/utils");
4147
4249
  var cachedKey = null;
4148
4250
  function getKey() {
4149
4251
  if (cachedKey) return cachedKey;
4150
- const secret = (0, import_utils19.getEnv)("MCP_SESSION_SECRET");
4252
+ const secret = (0, import_utils21.getEnv)("MCP_SESSION_SECRET");
4151
4253
  if (!secret) {
4152
- if ((0, import_utils19.isProduction)()) {
4254
+ if ((0, import_utils21.isProduction)()) {
4153
4255
  throw new SessionSecretRequiredError("session ID encryption");
4154
4256
  }
4155
4257
  console.warn(
4156
4258
  "[SessionIdUtils] Using machine ID as session encryption secret - NOT SECURE FOR PRODUCTION. Set MCP_SESSION_SECRET environment variable for secure session encryption."
4157
4259
  );
4158
4260
  }
4159
- const base = secret || (0, import_utils19.getMachineId)();
4160
- cachedKey = (0, import_utils19.sha256)(new TextEncoder().encode(base));
4261
+ const base = secret || (0, import_utils21.getMachineId)();
4262
+ cachedKey = (0, import_utils21.sha256)(new TextEncoder().encode(base));
4161
4263
  return cachedKey;
4162
4264
  }
4163
4265
  function encryptJson(obj) {
4164
4266
  const key = getKey();
4165
- const encrypted = (0, import_utils19.encryptValue)(obj, key);
4267
+ const encrypted = (0, import_utils21.encryptValue)(obj, key);
4166
4268
  return `${encrypted.iv}.${encrypted.tag}.${encrypted.data}`;
4167
4269
  }
4168
4270
  function decryptSessionJson(sessionId) {
@@ -4171,7 +4273,7 @@ function decryptSessionJson(sessionId) {
4171
4273
  const [ivB64, tagB64, ctB64] = parts;
4172
4274
  if (!ivB64 || !tagB64 || !ctB64) return null;
4173
4275
  const key = getKey();
4174
- return (0, import_utils19.decryptValue)({ alg: "A256GCM", iv: ivB64, tag: tagB64, data: ctB64 }, key);
4276
+ return (0, import_utils21.decryptValue)({ alg: "A256GCM", iv: ivB64, tag: tagB64, data: ctB64 }, key);
4175
4277
  }
4176
4278
  function safeDecrypt(sessionId) {
4177
4279
  try {
@@ -4185,11 +4287,11 @@ function resetCachedKey() {
4185
4287
  }
4186
4288
 
4187
4289
  // libs/auth/src/session/storage/index.ts
4188
- var import_utils23 = require("@frontmcp/utils");
4189
- var import_utils24 = require("@frontmcp/utils");
4290
+ var import_utils25 = require("@frontmcp/utils");
4291
+ var import_utils26 = require("@frontmcp/utils");
4190
4292
 
4191
4293
  // libs/auth/src/session/storage/storage-token-store.ts
4192
- var import_utils20 = require("@frontmcp/utils");
4294
+ var import_utils22 = require("@frontmcp/utils");
4193
4295
  var StorageTokenStore = class {
4194
4296
  storage;
4195
4297
  namespace;
@@ -4201,13 +4303,13 @@ var StorageTokenStore = class {
4201
4303
  this.defaultTtlSeconds = options.defaultTtlSeconds;
4202
4304
  this.storageIsNamespaced = "namespace" in storage && typeof storage.namespace === "function";
4203
4305
  const namespacedStorage = this.storageIsNamespaced ? storage.namespace(this.namespace) : storage;
4204
- this.storage = new import_utils20.TypedStorage(namespacedStorage);
4306
+ this.storage = new import_utils22.TypedStorage(namespacedStorage);
4205
4307
  }
4206
4308
  /**
4207
4309
  * Allocate a new unique ID for a token record.
4208
4310
  */
4209
4311
  allocId() {
4210
- return (0, import_utils20.randomUUID)();
4312
+ return (0, import_utils22.randomUUID)();
4211
4313
  }
4212
4314
  /**
4213
4315
  * Store an encrypted token blob.
@@ -4270,7 +4372,7 @@ var StorageTokenStore = class {
4270
4372
  };
4271
4373
 
4272
4374
  // libs/auth/src/session/storage/storage-authorization-vault.ts
4273
- var import_utils21 = require("@frontmcp/utils");
4375
+ var import_utils23 = require("@frontmcp/utils");
4274
4376
  var StorageAuthorizationVault = class {
4275
4377
  storage;
4276
4378
  namespace;
@@ -4279,7 +4381,7 @@ var StorageAuthorizationVault = class {
4279
4381
  this.namespace = options.namespace ?? "vault";
4280
4382
  this.pendingAuthTtlMs = options.pendingAuthTtlMs ?? 10 * 60 * 1e3;
4281
4383
  const namespacedStorage = this.isNamespacedStorage(storage) ? storage.namespace(this.namespace) : storage;
4282
- this.storage = new import_utils21.TypedStorage(namespacedStorage, {
4384
+ this.storage = new import_utils23.TypedStorage(namespacedStorage, {
4283
4385
  schema: options.validateOnRead ? authorizationVaultEntrySchema : void 0,
4284
4386
  throwOnInvalid: false
4285
4387
  });
@@ -4290,7 +4392,7 @@ var StorageAuthorizationVault = class {
4290
4392
  async create(params) {
4291
4393
  const now = Date.now();
4292
4394
  const entry = {
4293
- id: (0, import_utils21.randomUUID)(),
4395
+ id: (0, import_utils23.randomUUID)(),
4294
4396
  userSub: params.userSub,
4295
4397
  userEmail: params.userEmail,
4296
4398
  userName: params.userName,
@@ -4364,7 +4466,7 @@ var StorageAuthorizationVault = class {
4364
4466
  }
4365
4467
  const now = Date.now();
4366
4468
  const pendingAuth = {
4367
- id: (0, import_utils21.randomUUID)(),
4469
+ id: (0, import_utils23.randomUUID)(),
4368
4470
  appId: params.appId,
4369
4471
  toolId: params.toolId,
4370
4472
  authUrl: params.authUrl,
@@ -4568,11 +4670,11 @@ var StorageAuthorizationVault = class {
4568
4670
  };
4569
4671
 
4570
4672
  // libs/auth/src/session/storage/in-memory-authorization-vault.ts
4571
- var import_utils22 = require("@frontmcp/utils");
4673
+ var import_utils24 = require("@frontmcp/utils");
4572
4674
  var InMemoryAuthorizationVault = class extends StorageAuthorizationVault {
4573
4675
  memoryAdapter;
4574
4676
  constructor(options = {}) {
4575
- const memoryAdapter = new import_utils22.MemoryStorageAdapter();
4677
+ const memoryAdapter = new import_utils24.MemoryStorageAdapter();
4576
4678
  super(memoryAdapter, {
4577
4679
  namespace: options.namespace ?? "vault",
4578
4680
  pendingAuthTtlMs: options.pendingAuthTtlMs
@@ -4593,7 +4695,7 @@ var InMemoryAuthorizationVault = class extends StorageAuthorizationVault {
4593
4695
  };
4594
4696
 
4595
4697
  // libs/auth/src/session/redis-session.store.ts
4596
- var import_utils25 = require("@frontmcp/utils");
4698
+ var import_utils27 = require("@frontmcp/utils");
4597
4699
  var RedisSessionStore = class {
4598
4700
  storage;
4599
4701
  keyPrefix;
@@ -4615,14 +4717,14 @@ var RedisSessionStore = class {
4615
4717
  });
4616
4718
  }
4617
4719
  if ("redis" in config && config.redis) {
4618
- this.storage = new import_utils25.RedisStorageAdapter({
4720
+ this.storage = new import_utils27.RedisStorageAdapter({
4619
4721
  client: config.redis,
4620
4722
  keyPrefix: this.keyPrefix
4621
4723
  });
4622
4724
  this.externalInstance = true;
4623
4725
  } else {
4624
4726
  const redisConfig = config;
4625
- this.storage = new import_utils25.RedisStorageAdapter({
4727
+ this.storage = new import_utils27.RedisStorageAdapter({
4626
4728
  config: {
4627
4729
  host: redisConfig.host,
4628
4730
  port: redisConfig.port ?? 6379,
@@ -4804,7 +4906,7 @@ var RedisSessionStore = class {
4804
4906
  * Allocate a new session ID
4805
4907
  */
4806
4908
  allocId() {
4807
- return (0, import_utils25.randomUUID)();
4909
+ return (0, import_utils27.randomUUID)();
4808
4910
  }
4809
4911
  /**
4810
4912
  * Disconnect from Redis (only if we created the connection)
@@ -4840,7 +4942,7 @@ var RedisSessionStore = class {
4840
4942
  };
4841
4943
 
4842
4944
  // libs/auth/src/session/vercel-kv-session.store.ts
4843
- var import_utils26 = require("@frontmcp/utils");
4945
+ var import_utils28 = require("@frontmcp/utils");
4844
4946
  var VercelKvSessionStore = class {
4845
4947
  storage;
4846
4948
  keyPrefix;
@@ -4860,7 +4962,7 @@ var VercelKvSessionStore = class {
4860
4962
  maxRequests: this.security.rateLimiting?.maxRequests
4861
4963
  });
4862
4964
  }
4863
- this.storage = new import_utils26.VercelKvStorageAdapter({
4965
+ this.storage = new import_utils28.VercelKvStorageAdapter({
4864
4966
  url: config.url,
4865
4967
  token: config.token,
4866
4968
  keyPrefix: this.keyPrefix
@@ -5034,7 +5136,7 @@ var VercelKvSessionStore = class {
5034
5136
  * Allocate a new session ID
5035
5137
  */
5036
5138
  allocId() {
5037
- return (0, import_utils26.randomUUID)();
5139
+ return (0, import_utils28.randomUUID)();
5038
5140
  }
5039
5141
  /**
5040
5142
  * Disconnect from Vercel KV
@@ -5063,7 +5165,7 @@ var VercelKvSessionStore = class {
5063
5165
  };
5064
5166
 
5065
5167
  // libs/auth/src/session/orchestrated-token.store.ts
5066
- var import_utils27 = require("@frontmcp/utils");
5168
+ var import_utils29 = require("@frontmcp/utils");
5067
5169
  var InMemoryOrchestratedTokenStore = class {
5068
5170
  /** Token storage: Map<compositeKey, ProviderTokenRecord> */
5069
5171
  tokens = /* @__PURE__ */ new Map();
@@ -5105,7 +5207,7 @@ var InMemoryOrchestratedTokenStore = class {
5105
5207
  }
5106
5208
  const info = new TextEncoder().encode(`orchestrated-token:${compositeKey}`);
5107
5209
  const salt = new TextEncoder().encode("frontmcp-token-store");
5108
- const derivedKey = (0, import_utils27.hkdfSha256)(this.encryptionKey, salt, info, 32);
5210
+ const derivedKey = (0, import_utils29.hkdfSha256)(this.encryptionKey, salt, info, 32);
5109
5211
  this.derivedKeys.set(compositeKey, derivedKey);
5110
5212
  return derivedKey;
5111
5213
  }
@@ -5115,8 +5217,8 @@ var InMemoryOrchestratedTokenStore = class {
5115
5217
  async encryptRecord(compositeKey, record) {
5116
5218
  const key = await this.deriveKeyForRecord(compositeKey);
5117
5219
  const plaintext = JSON.stringify(record);
5118
- const iv = (0, import_utils27.randomBytes)(12);
5119
- const { ciphertext, tag } = (0, import_utils27.encryptAesGcm)(key, new TextEncoder().encode(plaintext), iv);
5220
+ const iv = (0, import_utils29.randomBytes)(12);
5221
+ const { ciphertext, tag } = (0, import_utils29.encryptAesGcm)(key, new TextEncoder().encode(plaintext), iv);
5120
5222
  return JSON.stringify({
5121
5223
  iv: Buffer.from(iv).toString("base64url"),
5122
5224
  tag: Buffer.from(tag).toString("base64url"),
@@ -5132,7 +5234,7 @@ var InMemoryOrchestratedTokenStore = class {
5132
5234
  const ivBytes = Buffer.from(iv, "base64url");
5133
5235
  const tagBytes = Buffer.from(tag, "base64url");
5134
5236
  const ciphertextBytes = Buffer.from(data, "base64url");
5135
- const plaintext = (0, import_utils27.decryptAesGcm)(key, ciphertextBytes, ivBytes, tagBytes);
5237
+ const plaintext = (0, import_utils29.decryptAesGcm)(key, ciphertextBytes, ivBytes, tagBytes);
5136
5238
  return JSON.parse(new TextDecoder().decode(plaintext));
5137
5239
  }
5138
5240
  /**
@@ -5334,7 +5436,7 @@ var InMemoryOrchestratedTokenStore = class {
5334
5436
  };
5335
5437
 
5336
5438
  // libs/auth/src/session/orchestrated-token.crypto.ts
5337
- var import_utils28 = require("@frontmcp/utils");
5439
+ var import_utils30 = require("@frontmcp/utils");
5338
5440
  var HKDF_SALT = new TextEncoder().encode("frontmcp-token-store");
5339
5441
  function deriveKeyForRecord(masterKey, compositeKey, cache) {
5340
5442
  const cached = cache.get(compositeKey);
@@ -5342,14 +5444,14 @@ function deriveKeyForRecord(masterKey, compositeKey, cache) {
5342
5444
  return cached;
5343
5445
  }
5344
5446
  const info = new TextEncoder().encode(`orchestrated-token:${compositeKey}`);
5345
- const derived = (0, import_utils28.hkdfSha256)(masterKey, HKDF_SALT, info, 32);
5447
+ const derived = (0, import_utils30.hkdfSha256)(masterKey, HKDF_SALT, info, 32);
5346
5448
  cache.set(compositeKey, derived);
5347
5449
  return derived;
5348
5450
  }
5349
5451
  function encryptRecord(key, record) {
5350
5452
  const plaintext = JSON.stringify(record);
5351
- const iv = (0, import_utils28.randomBytes)(12);
5352
- const { ciphertext, tag } = (0, import_utils28.encryptAesGcm)(key, new TextEncoder().encode(plaintext), iv);
5453
+ const iv = (0, import_utils30.randomBytes)(12);
5454
+ const { ciphertext, tag } = (0, import_utils30.encryptAesGcm)(key, new TextEncoder().encode(plaintext), iv);
5353
5455
  return JSON.stringify({
5354
5456
  iv: Buffer.from(iv).toString("base64url"),
5355
5457
  tag: Buffer.from(tag).toString("base64url"),
@@ -5361,7 +5463,7 @@ function decryptRecord(key, encrypted) {
5361
5463
  const ivBytes = Buffer.from(iv, "base64url");
5362
5464
  const tagBytes = Buffer.from(tag, "base64url");
5363
5465
  const ciphertextBytes = Buffer.from(data, "base64url");
5364
- const plaintext = (0, import_utils28.decryptAesGcm)(key, ciphertextBytes, ivBytes, tagBytes);
5466
+ const plaintext = (0, import_utils30.decryptAesGcm)(key, ciphertextBytes, ivBytes, tagBytes);
5365
5467
  return JSON.parse(new TextDecoder().decode(plaintext));
5366
5468
  }
5367
5469
 
@@ -5540,7 +5642,7 @@ var StorageOrchestratedTokenStore = class {
5540
5642
  };
5541
5643
 
5542
5644
  // libs/auth/src/session/federated-auth.session.ts
5543
- var import_utils29 = require("@frontmcp/utils");
5645
+ var import_utils31 = require("@frontmcp/utils");
5544
5646
  function toSessionRecord(session) {
5545
5647
  return {
5546
5648
  ...session,
@@ -5615,7 +5717,7 @@ var InMemoryFederatedAuthSessionStore = class {
5615
5717
  createSession(params) {
5616
5718
  const now = Date.now();
5617
5719
  return {
5618
- id: (0, import_utils29.randomUUID)(),
5720
+ id: (0, import_utils31.randomUUID)(),
5619
5721
  pendingAuthId: params.pendingAuthId,
5620
5722
  clientId: params.clientId,
5621
5723
  redirectUri: params.redirectUri,
@@ -5627,6 +5729,7 @@ var InMemoryFederatedAuthSessionStore = class {
5627
5729
  providerQueue: [...params.providerIds],
5628
5730
  completedProviders: /* @__PURE__ */ new Map(),
5629
5731
  skippedProviders: [],
5732
+ signinBinding: params.signinBinding,
5630
5733
  createdAt: now,
5631
5734
  expiresAt: now + this.sessionTtlMs
5632
5735
  };
@@ -5647,7 +5750,7 @@ var InMemoryFederatedAuthSessionStore = class {
5647
5750
  function createFederatedAuthSession(params, ttlMs = 15 * 60 * 1e3) {
5648
5751
  const now = Date.now();
5649
5752
  return {
5650
- id: (0, import_utils29.randomUUID)(),
5753
+ id: (0, import_utils31.randomUUID)(),
5651
5754
  pendingAuthId: params.pendingAuthId,
5652
5755
  clientId: params.clientId,
5653
5756
  redirectUri: params.redirectUri,
@@ -5659,6 +5762,7 @@ function createFederatedAuthSession(params, ttlMs = 15 * 60 * 1e3) {
5659
5762
  providerQueue: [...params.providerIds],
5660
5763
  completedProviders: /* @__PURE__ */ new Map(),
5661
5764
  skippedProviders: [],
5765
+ signinBinding: params.signinBinding,
5662
5766
  createdAt: now,
5663
5767
  expiresAt: now + ttlMs
5664
5768
  };
@@ -5704,7 +5808,7 @@ function startNextProvider(session, pkce, state) {
5704
5808
  }
5705
5809
 
5706
5810
  // libs/auth/src/session/storage-federated-auth.session.ts
5707
- var import_utils30 = require("@frontmcp/utils");
5811
+ var import_utils32 = require("@frontmcp/utils");
5708
5812
  var StorageFederatedAuthSessionStore = class {
5709
5813
  storage;
5710
5814
  namespace;
@@ -5713,7 +5817,7 @@ var StorageFederatedAuthSessionStore = class {
5713
5817
  this.namespace = options.namespace ?? "fed";
5714
5818
  this.storageIsNamespaced = this.isNamespacedStorage(storage);
5715
5819
  const namespacedStorage = this.storageIsNamespaced ? storage.namespace(this.namespace) : storage;
5716
- this.storage = new import_utils30.TypedStorage(namespacedStorage);
5820
+ this.storage = new import_utils32.TypedStorage(namespacedStorage);
5717
5821
  }
5718
5822
  async store(session) {
5719
5823
  await this.put(session);
@@ -5784,7 +5888,7 @@ var InMemoryConsentStore = class {
5784
5888
  };
5785
5889
 
5786
5890
  // libs/auth/src/session/storage-consent.store.ts
5787
- var import_utils31 = require("@frontmcp/utils");
5891
+ var import_utils33 = require("@frontmcp/utils");
5788
5892
  var StorageConsentStore = class {
5789
5893
  storage;
5790
5894
  namespace;
@@ -5793,7 +5897,7 @@ var StorageConsentStore = class {
5793
5897
  this.namespace = options.namespace ?? "remembered-consent";
5794
5898
  this.storageIsNamespaced = this.isNamespacedStorage(storage);
5795
5899
  const namespacedStorage = this.storageIsNamespaced ? storage.namespace(this.namespace) : storage;
5796
- this.storage = new import_utils31.TypedStorage(namespacedStorage);
5900
+ this.storage = new import_utils33.TypedStorage(namespacedStorage);
5797
5901
  }
5798
5902
  async get(userSub, clientId) {
5799
5903
  return this.storage.get(this.key(userSub, clientId));
@@ -5815,7 +5919,7 @@ var StorageConsentStore = class {
5815
5919
 
5816
5920
  // libs/auth/src/session/encrypted-authorization-vault.ts
5817
5921
  var import_lazy_zod5 = require("@frontmcp/lazy-zod");
5818
- var import_utils32 = require("@frontmcp/utils");
5922
+ var import_utils34 = require("@frontmcp/utils");
5819
5923
  var redisVaultEntrySchema = import_lazy_zod5.z.object({
5820
5924
  /** Vault ID */
5821
5925
  id: import_lazy_zod5.z.string(),
@@ -5840,7 +5944,7 @@ var redisVaultEntrySchema = import_lazy_zod5.z.object({
5840
5944
  /** Encrypted sensitive data blob */
5841
5945
  encrypted: encryptedDataSchema
5842
5946
  });
5843
- var encryptionContextStorage = new import_utils32.AsyncLocalStorage();
5947
+ var encryptionContextStorage = new import_utils34.AsyncLocalStorage();
5844
5948
  var EncryptedRedisVault = class {
5845
5949
  constructor(redis, encryption, namespace = "vault:") {
5846
5950
  this.redis = redis;
@@ -5976,7 +6080,7 @@ var EncryptedRedisVault = class {
5976
6080
  async create(params) {
5977
6081
  const now = Date.now();
5978
6082
  const entry = {
5979
- id: (0, import_utils32.randomUUID)(),
6083
+ id: (0, import_utils34.randomUUID)(),
5980
6084
  userSub: params.userSub,
5981
6085
  userEmail: params.userEmail,
5982
6086
  userName: params.userName,
@@ -6035,7 +6139,7 @@ var EncryptedRedisVault = class {
6035
6139
  }
6036
6140
  const now = Date.now();
6037
6141
  const pendingAuth = {
6038
- id: (0, import_utils32.randomUUID)(),
6142
+ id: (0, import_utils34.randomUUID)(),
6039
6143
  appId: params.appId,
6040
6144
  toolId: params.toolId,
6041
6145
  authUrl: params.authUrl,
@@ -6213,7 +6317,7 @@ function createEncryptedVault(redis, config = {}) {
6213
6317
  }
6214
6318
 
6215
6319
  // libs/auth/src/session/token.refresh.ts
6216
- var import_utils33 = require("@frontmcp/utils");
6320
+ var import_utils35 = require("@frontmcp/utils");
6217
6321
  function toEpochSeconds(exp) {
6218
6322
  if (exp == null) return void 0;
6219
6323
  if (exp instanceof Date) return Math.floor(exp.getTime() / 1e3);
@@ -6235,7 +6339,7 @@ function tryJwtExp(token) {
6235
6339
  const parts = token.split(".");
6236
6340
  if (parts.length < 2) return void 0;
6237
6341
  try {
6238
- const json = JSON.parse(new TextDecoder().decode((0, import_utils33.base64urlDecode)(parts[1])));
6342
+ const json = JSON.parse(new TextDecoder().decode((0, import_utils35.base64urlDecode)(parts[1])));
6239
6343
  const e = json?.exp;
6240
6344
  return typeof e === "number" ? toEpochSeconds(e) : void 0;
6241
6345
  } catch {
@@ -6298,7 +6402,7 @@ var progressiveAuthStateSchema = import_lazy_zod6.z.object({
6298
6402
  });
6299
6403
 
6300
6404
  // libs/auth/src/authorization/authorization.class.ts
6301
- var import_utils36 = require("@frontmcp/utils");
6405
+ var import_utils38 = require("@frontmcp/utils");
6302
6406
  var AuthorizationBase = class _AuthorizationBase {
6303
6407
  id;
6304
6408
  isAnonymous;
@@ -6346,14 +6450,14 @@ var AuthorizationBase = class _AuthorizationBase {
6346
6450
  * @param fingerprint - Optional client fingerprint for tracking
6347
6451
  */
6348
6452
  createTransportSession(protocol, fingerprint) {
6349
- const sessionId = (0, import_utils36.randomUUID)();
6453
+ const sessionId = (0, import_utils38.randomUUID)();
6350
6454
  const session = {
6351
6455
  id: sessionId,
6352
6456
  authorizationId: this.id,
6353
6457
  protocol,
6354
6458
  createdAt: Date.now(),
6355
6459
  expiresAt: this.expiresAt,
6356
- nodeId: (0, import_utils36.getMachineId)(),
6460
+ nodeId: (0, import_utils38.getMachineId)(),
6357
6461
  clientFingerprint: fingerprint
6358
6462
  };
6359
6463
  this.#sessions.set(session.id, session);
@@ -6494,7 +6598,7 @@ var AuthorizationBase = class _AuthorizationBase {
6494
6598
  */
6495
6599
  static isJwtHeader(segment) {
6496
6600
  try {
6497
- const bytes = (0, import_utils36.base64urlDecode)(segment);
6601
+ const bytes = (0, import_utils38.base64urlDecode)(segment);
6498
6602
  const text = new TextDecoder().decode(bytes);
6499
6603
  const parsed = JSON.parse(text);
6500
6604
  return typeof parsed === "object" && parsed !== null && typeof parsed.alg === "string";
@@ -6546,7 +6650,7 @@ var AuthorizationBase = class _AuthorizationBase {
6546
6650
  };
6547
6651
 
6548
6652
  // libs/auth/src/authorization/public.authorization.ts
6549
- var import_utils37 = require("@frontmcp/utils");
6653
+ var import_utils39 = require("@frontmcp/utils");
6550
6654
  var PublicAuthorization = class _PublicAuthorization extends AuthorizationBase {
6551
6655
  mode = "public";
6552
6656
  /**
@@ -6582,7 +6686,7 @@ var PublicAuthorization = class _PublicAuthorization extends AuthorizationBase {
6582
6686
  allowedTools = "all",
6583
6687
  allowedPrompts = "all"
6584
6688
  } = ctx;
6585
- const uuid = (0, import_utils37.randomUUID)();
6689
+ const uuid = (0, import_utils39.randomUUID)();
6586
6690
  const sub = `${prefix}:${uuid}`;
6587
6691
  const user = {
6588
6692
  sub,
@@ -6666,7 +6770,7 @@ var PublicAuthorization = class _PublicAuthorization extends AuthorizationBase {
6666
6770
  };
6667
6771
 
6668
6772
  // libs/auth/src/authorization/transparent.authorization.ts
6669
- var import_utils38 = require("@frontmcp/utils");
6773
+ var import_utils40 = require("@frontmcp/utils");
6670
6774
  var TransparentAuthorization = class _TransparentAuthorization extends AuthorizationBase {
6671
6775
  mode = "transparent";
6672
6776
  /**
@@ -6766,7 +6870,7 @@ var TransparentAuthorization = class _TransparentAuthorization extends Authoriza
6766
6870
  static generateAuthorizationId(token) {
6767
6871
  const parts = token.split(".");
6768
6872
  const signature = parts[2] || token;
6769
- return (0, import_utils38.sha256Hex)(signature).substring(0, 16);
6873
+ return (0, import_utils40.sha256Hex)(signature).substring(0, 16);
6770
6874
  }
6771
6875
  /**
6772
6876
  * Get the issuer from the token claims
@@ -6792,11 +6896,11 @@ var TransparentAuthorization = class _TransparentAuthorization extends Authoriza
6792
6896
  };
6793
6897
 
6794
6898
  // libs/auth/src/utils/authorization-id.utils.ts
6795
- var import_utils39 = require("@frontmcp/utils");
6899
+ var import_utils41 = require("@frontmcp/utils");
6796
6900
  function deriveAuthorizationId(token) {
6797
6901
  const parts = token.split(".");
6798
6902
  const signature = parts[2] || token;
6799
- return (0, import_utils39.sha256Hex)(signature).substring(0, 16);
6903
+ return (0, import_utils41.sha256Hex)(signature).substring(0, 16);
6800
6904
  }
6801
6905
 
6802
6906
  // libs/auth/src/authorization/orchestrated.authorization.ts
@@ -7165,7 +7269,7 @@ var OrchestratedAuthAccessorAdapter = class {
7165
7269
  };
7166
7270
 
7167
7271
  // libs/auth/src/authorization/dcr-client.registry.ts
7168
- var import_utils40 = require("@frontmcp/utils");
7272
+ var import_utils42 = require("@frontmcp/utils");
7169
7273
  var DEFAULT_MAX_DYNAMIC_CLIENTS = 1e3;
7170
7274
  function globMatch(pattern, input) {
7171
7275
  let p = 0;
@@ -7291,7 +7395,7 @@ var DcrClientRegistry = class {
7291
7395
  if (!isConfidential) return "ok";
7292
7396
  if (!client.client_secret) return "invalid";
7293
7397
  if (!presentedSecret) return "invalid";
7294
- return (0, import_utils40.timingSafeEqual)((0, import_utils40.sha256)(presentedSecret), (0, import_utils40.sha256)(client.client_secret)) ? "ok" : "invalid";
7398
+ return (0, import_utils42.timingSafeEqual)((0, import_utils42.sha256)(presentedSecret), (0, import_utils42.sha256)(client.client_secret)) ? "ok" : "invalid";
7295
7399
  }
7296
7400
  /** Whether a client id is known to the registry. */
7297
7401
  has(clientId) {
@@ -7379,7 +7483,7 @@ var DcrClientRegistry = class {
7379
7483
  if (typeof presented !== "string" || presented.length === 0) {
7380
7484
  return false;
7381
7485
  }
7382
- return (0, import_utils40.timingSafeEqual)((0, import_utils40.sha256)(presented), (0, import_utils40.sha256)(expected));
7486
+ return (0, import_utils42.timingSafeEqual)((0, import_utils42.sha256)(presented), (0, import_utils42.sha256)(expected));
7383
7487
  }
7384
7488
  };
7385
7489
 
@@ -7708,7 +7812,7 @@ var cimdConfigSchema = import_lazy_zod9.z.object({
7708
7812
  init_cimd_cache();
7709
7813
 
7710
7814
  // libs/auth/src/cimd/cimd.service.ts
7711
- var import_utils41 = require("@frontmcp/utils");
7815
+ var import_utils43 = require("@frontmcp/utils");
7712
7816
  init_cimd_cache();
7713
7817
  var CimdService = class {
7714
7818
  config;
@@ -7883,7 +7987,7 @@ var CimdService = class {
7883
7987
  httpStatus: 304
7884
7988
  });
7885
7989
  }
7886
- if ((0, import_utils41.isRedirectResponse)(response)) {
7990
+ if ((0, import_utils43.isRedirectResponse)(response)) {
7887
7991
  const location = response.headers.get("location");
7888
7992
  if (!location) {
7889
7993
  throw new CimdFetchError(clientId, "Redirect response missing Location header", {
@@ -8196,7 +8300,16 @@ var upstreamProviderSchema = import_lazy_zod10.z.object({
8196
8300
  /** User info endpoint (optional, used to enrich the session identity). */
8197
8301
  userInfoEndpoint: import_lazy_zod10.z.string().url().optional(),
8198
8302
  /** JWKS URI for upstream id_token validation (optional). */
8199
- jwksUri: import_lazy_zod10.z.string().url().optional()
8303
+ jwksUri: import_lazy_zod10.z.string().url().optional(),
8304
+ /**
8305
+ * The provider's issuer identifier (its OIDC `iss`). When set, an RFC 9207
8306
+ * `iss` on the provider's callback and the `iss` of its `id_token` must name
8307
+ * it (or one of {@link additionalIssuers}). Without it the provider's
8308
+ * `id_token` is not used for identity; `userInfoEndpoint` is asked instead.
8309
+ */
8310
+ issuer: import_lazy_zod10.z.string().url().optional(),
8311
+ /** Other issuer values this provider legitimately uses. */
8312
+ additionalIssuers: import_lazy_zod10.z.array(import_lazy_zod10.z.string().trim().min(1, "additionalIssuers entries must be non-empty")).optional()
8200
8313
  }).refine((p) => !!(p.authorizationEndpoint ?? p.authorizeUrl), {
8201
8314
  message: "authorizationEndpoint (or authorizeUrl) is required",
8202
8315
  path: ["authorizationEndpoint"]
@@ -8605,17 +8718,21 @@ var sharedAuthFields = {
8605
8718
  * Require the OAuth client to be KNOWN (registered via DCR / pre-registered,
8606
8719
  * or a CIMD client-id URL) before an authorization request is accepted.
8607
8720
  *
8608
- * @default false (backward compatible — unknown client ids are accepted).
8721
+ * @default true
8609
8722
  *
8610
- * SECURITY: with the default, an unregistered client id has no trusted
8611
- * `redirect_uris` to validate against, so its (attacker-chosen) redirect_uri
8612
- * is accepted and a real authorization code can be delivered to an attacker
8613
- * (auth-code interception → account takeover in real-IdP modes). Set this to
8614
- * `true` (recommended for production) to enforce OAuth 2.1 exact redirect-uri
8615
- * matching for every client. Clients on a configured
8616
- * `dcr.allowedRedirectUris` allowlist still pass.
8617
- */
8618
- requireRegisteredClients: import_lazy_zod14.z.boolean().default(false),
8723
+ * SECURITY: an unregistered client id has no trusted `redirect_uris` to
8724
+ * validate against, so accepting it lets anyone start a sign-in whose
8725
+ * authorization code is delivered to a redirect_uri of their choosing
8726
+ * (auth-code interception). `false` is for local development only; a
8727
+ * `dcr.allowedRedirectUris` allowlist still constrains such clients.
8728
+ */
8729
+ requireRegisteredClients: import_lazy_zod14.z.boolean().default(true),
8730
+ /**
8731
+ * Scopes the server may grant (exact values or `*` globs). Requested scopes
8732
+ * outside this list are dropped from the grant. Unset → the standard OpenID
8733
+ * scopes only (see `DEFAULT_ALLOWED_SCOPES`).
8734
+ */
8735
+ allowedScopes: import_lazy_zod14.z.array(import_lazy_zod14.z.string().trim().min(1, "allowedScopes entries must be non-empty")).optional(),
8619
8736
  cimd: cimdConfigSchema.optional(),
8620
8737
  // #469 — custom auth UI as a slot→file map + extras name→handler map (per-app
8621
8738
  // under splitByApp). Both optional; omitting them serves the built-in pages.
@@ -9226,6 +9343,43 @@ var AudienceValidator = class _AudienceValidator {
9226
9343
  }
9227
9344
  };
9228
9345
 
9346
+ // libs/auth/src/utils/scope-grant.utils.ts
9347
+ var DEFAULT_ALLOWED_SCOPES = Object.freeze([
9348
+ "openid",
9349
+ "profile",
9350
+ "email",
9351
+ "offline_access"
9352
+ ]);
9353
+ function grantScopes(requested, allowed) {
9354
+ const patterns = allowed ?? DEFAULT_ALLOWED_SCOPES;
9355
+ const granted = [];
9356
+ for (const scope of requested) {
9357
+ if (!scope || granted.includes(scope)) continue;
9358
+ if (patterns.some((pattern) => globMatch(pattern, scope))) granted.push(scope);
9359
+ }
9360
+ return granted;
9361
+ }
9362
+
9363
+ // libs/auth/src/utils/redirect-uri.utils.ts
9364
+ function isLoopbackRedirectUri(uri) {
9365
+ let url;
9366
+ try {
9367
+ url = new URL(uri);
9368
+ } catch {
9369
+ return false;
9370
+ }
9371
+ if (url.protocol !== "http:" && url.protocol !== "https:") return false;
9372
+ if (url.username || url.password) return false;
9373
+ const host = url.hostname.replace(/^\[|\]$/g, "").toLowerCase();
9374
+ if (host === "localhost" || host === "::1") return true;
9375
+ const m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
9376
+ if (m) {
9377
+ const octets = m.slice(1).map((n) => Number(n));
9378
+ return octets.every((o) => o >= 0 && o <= 255) && octets[0] === 127;
9379
+ }
9380
+ return false;
9381
+ }
9382
+
9229
9383
  // libs/auth/src/vault/auth-providers.types.ts
9230
9384
  var import_lazy_zod19 = require("@frontmcp/lazy-zod");
9231
9385
  var credentialScopeSchema = import_lazy_zod19.z.enum(["global", "user", "session"]);
@@ -9468,7 +9622,7 @@ var CredentialCache = class {
9468
9622
  var AUTH_PROVIDERS_ACCESSOR = /* @__PURE__ */ Symbol.for("frontmcp:AUTH_PROVIDERS_ACCESSOR");
9469
9623
 
9470
9624
  // libs/auth/src/vault/auth-providers.accessor.impl.ts
9471
- var import_utils44 = require("@frontmcp/utils");
9625
+ var import_utils46 = require("@frontmcp/utils");
9472
9626
  var AuthProvidersAccessorImpl = class {
9473
9627
  constructor(registry, vault, cache, loader, context, logger) {
9474
9628
  this.registry = registry;
@@ -9683,7 +9837,7 @@ var AuthProvidersAccessorImpl = class {
9683
9837
  return { [credential.headerName]: credential.key };
9684
9838
  case "basic": {
9685
9839
  const value = `${credential.username}:${credential.password}`;
9686
- const encoded = credential.encodedValue ?? (0, import_utils44.base64Encode)(new TextEncoder().encode(value));
9840
+ const encoded = credential.encodedValue ?? (0, import_utils46.base64Encode)(new TextEncoder().encode(value));
9687
9841
  return { Authorization: `Basic ${encoded}` };
9688
9842
  }
9689
9843
  case "custom":
@@ -10791,6 +10945,211 @@ async function evaluateRebac(policy, ctx) {
10791
10945
  return { granted: true, evaluatedPolicies: ["relationships"] };
10792
10946
  }
10793
10947
 
10948
+ // libs/auth/src/authorities/authorities.validation.ts
10949
+ var ABAC_OPERATORS = /* @__PURE__ */ new Set([
10950
+ "eq",
10951
+ "neq",
10952
+ "in",
10953
+ "notIn",
10954
+ "gt",
10955
+ "gte",
10956
+ "lt",
10957
+ "lte",
10958
+ "contains",
10959
+ "startsWith",
10960
+ "endsWith",
10961
+ "exists",
10962
+ "matches"
10963
+ ]);
10964
+ var CHECK_FIELDS = [
10965
+ "roles",
10966
+ "permissions",
10967
+ "attributes",
10968
+ "relationships",
10969
+ "custom",
10970
+ "guards",
10971
+ "allOf",
10972
+ "anyOf",
10973
+ "not"
10974
+ ];
10975
+ var RULE_FIELDS = /* @__PURE__ */ new Set([...CHECK_FIELDS, "operator"]);
10976
+ function isPlainObject(value) {
10977
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10978
+ }
10979
+ function isNonEmptyString(value) {
10980
+ return typeof value === "string" && value.length > 0;
10981
+ }
10982
+ function unknownFields(value, known, path) {
10983
+ const prefix = path ? `${path} has` : "has";
10984
+ return Object.keys(value).filter((key) => !known.has(key)).map((key) => `${prefix} an unknown field "${key}"`);
10985
+ }
10986
+ function checkStringList(value, path) {
10987
+ if (!Array.isArray(value)) return [`${path} must be a list of names`];
10988
+ if (value.length === 0) return [`${path} is empty`];
10989
+ return value.every(isNonEmptyString) ? [] : [`${path} must contain only non-empty names`];
10990
+ }
10991
+ var ALL_OR_ANY = /* @__PURE__ */ new Set(["all", "any"]);
10992
+ function checkAllOrAny(value, path) {
10993
+ if (!isPlainObject(value)) return [`${path} must be an object with "all" or "any"`];
10994
+ const problems = unknownFields(value, ALL_OR_ANY, path);
10995
+ if (value["all"] === void 0 && value["any"] === void 0) {
10996
+ problems.push(`${path} needs "all" or "any"`);
10997
+ }
10998
+ if (value["all"] !== void 0) problems.push(...checkStringList(value["all"], `${path}.all`));
10999
+ if (value["any"] !== void 0) problems.push(...checkStringList(value["any"], `${path}.any`));
11000
+ return problems;
11001
+ }
11002
+ var ATTRIBUTE_FIELDS = /* @__PURE__ */ new Set(["match", "conditions"]);
11003
+ var CONDITION_FIELDS = /* @__PURE__ */ new Set(["path", "op", "value"]);
11004
+ function isValueReference(value) {
11005
+ return isPlainObject(value) && ("fromInput" in value || "fromClaims" in value);
11006
+ }
11007
+ function checkValueReference(value, path) {
11008
+ const ref = value["fromInput"] ?? value["fromClaims"];
11009
+ return Object.keys(value).length === 1 && isNonEmptyString(ref) ? [] : [`${path} must be { fromInput: "<name>" } or { fromClaims: "<path>" }`];
11010
+ }
11011
+ var OPERATOR_VALUES = {
11012
+ in: { accepts: Array.isArray, needs: "a list" },
11013
+ notIn: { accepts: Array.isArray, needs: "a list" },
11014
+ gt: { accepts: (value) => typeof value === "number", needs: "a number" },
11015
+ gte: { accepts: (value) => typeof value === "number", needs: "a number" },
11016
+ lt: { accepts: (value) => typeof value === "number", needs: "a number" },
11017
+ lte: { accepts: (value) => typeof value === "number", needs: "a number" },
11018
+ startsWith: { accepts: (value) => typeof value === "string", needs: "a string" },
11019
+ endsWith: { accepts: (value) => typeof value === "string", needs: "a string" },
11020
+ matches: { accepts: (value) => typeof value === "string", needs: "a string" }
11021
+ };
11022
+ function checkConditionValue(op, condition, path) {
11023
+ const value = condition["value"];
11024
+ if (value === void 0) return [`${path} is missing`];
11025
+ if (op === "exists") {
11026
+ return typeof value === "boolean" ? [] : [`${path} must be true or false for "exists"`];
11027
+ }
11028
+ if (isValueReference(value)) return checkValueReference(value, path);
11029
+ const expected = OPERATOR_VALUES[op];
11030
+ if (expected && !expected.accepts(value)) return [`${path} must be ${expected.needs} for "${op}"`];
11031
+ if (Array.isArray(value) && value.length === 0 && (op === "in" || op === "notIn")) return [`${path} is empty`];
11032
+ return [];
11033
+ }
11034
+ function checkAttributes(value, path) {
11035
+ if (!isPlainObject(value)) return [`${path} must be an object with "match" or "conditions"`];
11036
+ const problems = unknownFields(value, ATTRIBUTE_FIELDS, path);
11037
+ const { match, conditions } = value;
11038
+ if (match === void 0 && conditions === void 0) {
11039
+ problems.push(`${path} needs "match" or "conditions"`);
11040
+ }
11041
+ if (match !== void 0) {
11042
+ if (!isPlainObject(match)) problems.push(`${path}.match must be an object`);
11043
+ else if (Object.keys(match).length === 0) problems.push(`${path}.match is empty`);
11044
+ else {
11045
+ for (const [attribute, expected] of Object.entries(match)) {
11046
+ const at = `${path}.match[${JSON.stringify(attribute)}]`;
11047
+ if (expected === void 0) problems.push(`${at} has no value`);
11048
+ else if (isValueReference(expected)) problems.push(...checkValueReference(expected, at));
11049
+ }
11050
+ }
11051
+ }
11052
+ if (conditions !== void 0) {
11053
+ if (!Array.isArray(conditions)) problems.push(`${path}.conditions must be a list`);
11054
+ else if (conditions.length === 0) problems.push(`${path}.conditions is empty`);
11055
+ else {
11056
+ conditions.forEach((condition, index) => {
11057
+ const at = `${path}.conditions[${index}]`;
11058
+ if (!isPlainObject(condition)) {
11059
+ problems.push(`${at} must be an object with "path" and "op"`);
11060
+ return;
11061
+ }
11062
+ problems.push(...unknownFields(condition, CONDITION_FIELDS, at));
11063
+ if (!isNonEmptyString(condition["path"])) problems.push(`${at}.path must be a non-empty string`);
11064
+ const op = condition["op"];
11065
+ if (!ABAC_OPERATORS.has(op)) {
11066
+ problems.push(`${at}.op "${String(condition["op"])}" is not an operator`);
11067
+ } else {
11068
+ problems.push(...checkConditionValue(op, condition, `${at}.value`));
11069
+ }
11070
+ });
11071
+ }
11072
+ }
11073
+ return problems;
11074
+ }
11075
+ var RELATIONSHIP_FIELDS = /* @__PURE__ */ new Set(["type", "resource", "resourceId"]);
11076
+ function checkResourceId(value, path) {
11077
+ if (isNonEmptyString(value)) return [];
11078
+ if (isPlainObject(value) && Object.keys(value).length === 1) {
11079
+ const ref = value["fromInput"] ?? value["fromClaims"];
11080
+ if (isNonEmptyString(ref)) return [];
11081
+ }
11082
+ return [`${path} must be a string, { fromInput } or { fromClaims }`];
11083
+ }
11084
+ function checkRelationship(value, path) {
11085
+ if (!isPlainObject(value)) return [`${path} must be an object with "type", "resource" and "resourceId"`];
11086
+ const problems = unknownFields(value, RELATIONSHIP_FIELDS, path);
11087
+ if (!isNonEmptyString(value["type"])) problems.push(`${path}.type must be a non-empty string`);
11088
+ if (!isNonEmptyString(value["resource"])) problems.push(`${path}.resource must be a non-empty string`);
11089
+ problems.push(...checkResourceId(value["resourceId"], `${path}.resourceId`));
11090
+ return problems;
11091
+ }
11092
+ function checkRelationships(value, path) {
11093
+ if (!Array.isArray(value)) return checkRelationship(value, path);
11094
+ if (value.length === 0) return [`${path} is empty`];
11095
+ return value.flatMap((relationship, index) => checkRelationship(relationship, `${path}[${index}]`));
11096
+ }
11097
+ function checkRuleList(value, path) {
11098
+ if (!Array.isArray(value)) return [`${path} must be a list of rules`];
11099
+ if (value.length === 0) return [`${path} is empty`];
11100
+ return value.flatMap((rule, index) => checkRule(rule, `${path}[${index}]`));
11101
+ }
11102
+ function checkRule(rule, path) {
11103
+ if (!isPlainObject(rule)) {
11104
+ return [`${path || "the rule"} must be a rule object${typeof rule === "string" ? ", not a profile name" : ""}`];
11105
+ }
11106
+ const problems = unknownFields(rule, RULE_FIELDS, path);
11107
+ if (!CHECK_FIELDS.some((field) => rule[field] !== void 0)) {
11108
+ problems.push(`${path ? `${path} ` : ""}checks nothing`);
11109
+ }
11110
+ if (rule["operator"] !== void 0 && rule["operator"] !== "AND" && rule["operator"] !== "OR") {
11111
+ problems.push(`${path}.operator must be "AND" or "OR"`);
11112
+ }
11113
+ if (rule["roles"] !== void 0) problems.push(...checkAllOrAny(rule["roles"], `${path}.roles`));
11114
+ if (rule["permissions"] !== void 0) problems.push(...checkAllOrAny(rule["permissions"], `${path}.permissions`));
11115
+ if (rule["attributes"] !== void 0) problems.push(...checkAttributes(rule["attributes"], `${path}.attributes`));
11116
+ if (rule["relationships"] !== void 0) {
11117
+ problems.push(...checkRelationships(rule["relationships"], `${path}.relationships`));
11118
+ }
11119
+ if (rule["custom"] !== void 0) {
11120
+ if (!isPlainObject(rule["custom"])) problems.push(`${path}.custom must be an object`);
11121
+ else if (Object.keys(rule["custom"]).length === 0) problems.push(`${path}.custom is empty`);
11122
+ }
11123
+ if (rule["guards"] !== void 0) {
11124
+ const guards = rule["guards"];
11125
+ if (!Array.isArray(guards)) problems.push(`${path}.guards must be a list of functions`);
11126
+ else if (guards.length === 0) problems.push(`${path}.guards is empty`);
11127
+ else if (!guards.every((guard) => typeof guard === "function")) {
11128
+ problems.push(`${path}.guards must contain only functions`);
11129
+ }
11130
+ }
11131
+ if (rule["allOf"] !== void 0) problems.push(...checkRuleList(rule["allOf"], `${path}.allOf`));
11132
+ if (rule["anyOf"] !== void 0) problems.push(...checkRuleList(rule["anyOf"], `${path}.anyOf`));
11133
+ if (rule["not"] !== void 0) problems.push(...checkRule(rule["not"], `${path}.not`));
11134
+ return problems;
11135
+ }
11136
+ function findAuthoritiesRuleProblems(authorities) {
11137
+ if (typeof authorities === "string") {
11138
+ return authorities.length > 0 ? [] : ["the profile name is empty"];
11139
+ }
11140
+ if (Array.isArray(authorities)) {
11141
+ if (authorities.length === 0) return ["the list of profiles is empty"];
11142
+ return authorities.every(isNonEmptyString) ? [] : ["a list of profiles must contain only non-empty profile names"];
11143
+ }
11144
+ if (!isPlainObject(authorities)) {
11145
+ return ["must be a profile name, a list of profile names, or a rule object"];
11146
+ }
11147
+ return checkRule(authorities, "");
11148
+ }
11149
+ function findAuthoritiesProfileProblems(name, policy) {
11150
+ return checkRule(policy, "").map((problem) => `profile "${name}": ${problem.replace(/^\./, "")}`);
11151
+ }
11152
+
10794
11153
  // libs/auth/src/authorities/authorities.engine.ts
10795
11154
  function mergeResult(base, ...others) {
10796
11155
  const evaluatedPolicies = [...base.evaluatedPolicies];
@@ -10799,8 +11158,12 @@ function mergeResult(base, ...others) {
10799
11158
  }
10800
11159
  return { ...base, evaluatedPolicies };
10801
11160
  }
10802
- function granted(policies) {
10803
- return { granted: true, evaluatedPolicies: policies };
11161
+ function invalidRule(problems, policies = []) {
11162
+ return {
11163
+ granted: false,
11164
+ deniedBy: `invalid authorities rule: ${problems.join("; ")}`,
11165
+ evaluatedPolicies: policies
11166
+ };
10804
11167
  }
10805
11168
  var AuthoritiesEngine = class {
10806
11169
  constructor(profiles, evaluators) {
@@ -10809,10 +11172,32 @@ var AuthoritiesEngine = class {
10809
11172
  }
10810
11173
  profiles;
10811
11174
  evaluators;
11175
+ /** Problems found per rule object, so a rule is validated once however often it is evaluated. */
11176
+ ruleProblems = /* @__PURE__ */ new WeakMap();
11177
+ /**
11178
+ * Problems with an `authorities` value declared on an entry (see {@link findAuthoritiesRuleProblems}).
11179
+ * The server reports them when it starts; {@link evaluate} denies such a value.
11180
+ */
11181
+ findRuleProblems(authorities) {
11182
+ return findAuthoritiesRuleProblems(authorities);
11183
+ }
11184
+ /**
11185
+ * Problems with every registered profile's rule (see {@link findAuthoritiesRuleProblems}).
11186
+ * The server reports them when it starts.
11187
+ */
11188
+ findProfileProblems() {
11189
+ return Object.entries(this.profiles.getAll()).flatMap(
11190
+ ([name, policy]) => findAuthoritiesProfileProblems(name, policy)
11191
+ );
11192
+ }
10812
11193
  /**
10813
11194
  * Evaluate an AuthoritiesMetadata value (string, string[], or policy object).
11195
+ *
11196
+ * A value that is malformed or checks nothing (see {@link findAuthoritiesRuleProblems}) is denied.
10814
11197
  */
10815
11198
  async evaluate(authorities, ctx) {
11199
+ const problems = this.problemsOf(authorities, findAuthoritiesRuleProblems);
11200
+ if (problems.length > 0) return invalidRule(problems);
10816
11201
  if (typeof authorities === "string") {
10817
11202
  return this.evaluateProfile(authorities, ctx);
10818
11203
  }
@@ -10833,6 +11218,8 @@ var AuthoritiesEngine = class {
10833
11218
  evaluatedPolicies: [`profile:${name}`]
10834
11219
  };
10835
11220
  }
11221
+ const problems = this.problemsOf(policy, (rule) => findAuthoritiesProfileProblems(name, rule));
11222
+ if (problems.length > 0) return invalidRule(problems, [`profile:${name}`]);
10836
11223
  const result = await this.evaluatePolicy(policy, ctx);
10837
11224
  if (!result.granted) {
10838
11225
  return {
@@ -10843,6 +11230,16 @@ var AuthoritiesEngine = class {
10843
11230
  }
10844
11231
  return mergeResult(result, { granted: true, evaluatedPolicies: [`profile:${name}`] });
10845
11232
  }
11233
+ /** Validate a rule, reusing the result for a rule object seen before. */
11234
+ problemsOf(rule, find) {
11235
+ if (typeof rule !== "object" || rule === null) return find(rule);
11236
+ let problems = this.ruleProblems.get(rule);
11237
+ if (!problems) {
11238
+ problems = find(rule);
11239
+ this.ruleProblems.set(rule, problems);
11240
+ }
11241
+ return problems;
11242
+ }
10846
11243
  /**
10847
11244
  * Evaluate an array of profile names (AND semantics).
10848
11245
  */
@@ -10891,7 +11288,7 @@ var AuthoritiesEngine = class {
10891
11288
  results.push(await this.evaluateNot(policy.not, ctx));
10892
11289
  }
10893
11290
  if (results.length === 0) {
10894
- return granted([]);
11291
+ return invalidRule(["checks nothing"]);
10895
11292
  }
10896
11293
  return this.combineResults(results, operator);
10897
11294
  }
@@ -10942,14 +11339,15 @@ var AuthoritiesEngine = class {
10942
11339
  }
10943
11340
  /**
10944
11341
  * Evaluate async guard functions in sequence.
10945
- * Each guard returns true (granted) or a string (denial message).
11342
+ * Only `true` grants. A string is the denial message; anything else (`false`, but also
11343
+ * `undefined` from a guard that forgot to return, `null`, `0` or an object) denies.
10946
11344
  */
10947
11345
  async evaluateGuards(guards, ctx) {
10948
11346
  for (let i = 0; i < guards.length; i++) {
10949
11347
  const guard = guards[i];
10950
11348
  const result = await guard(ctx);
10951
- if (result === false || typeof result === "string") {
10952
- const denialMessage = typeof result === "string" ? result : `guard[${i}] denied`;
11349
+ if (result !== true) {
11350
+ const denialMessage = typeof result === "string" ? result : result === false ? `guard[${i}] denied` : `guard[${i}] did not return true (returned ${result === null ? "null" : typeof result})`;
10953
11351
  return {
10954
11352
  granted: false,
10955
11353
  deniedBy: `guards[${i}]: ${denialMessage}`,
@@ -11007,6 +11405,13 @@ var AuthoritiesEngine = class {
11007
11405
  evaluatedPolicies: ["not", ...result.evaluatedPolicies]
11008
11406
  };
11009
11407
  }
11408
+ /**
11409
+ * The policy a profile name stands for when `evaluate()` is given that name, or `undefined`
11410
+ * when no such profile is registered. Lets callers inspect a named rule without evaluating it.
11411
+ */
11412
+ resolveProfile(name) {
11413
+ return this.profiles.resolve(name);
11414
+ }
11010
11415
  };
11011
11416
 
11012
11417
  // libs/auth/src/authorities/authorities.registry.ts
@@ -11252,6 +11657,7 @@ var FetchCredentialMiddleware = class {
11252
11657
  AuthoritiesProfileRegistry,
11253
11658
  AuthorityDeniedError,
11254
11659
  AuthorizationBase,
11660
+ BUILT_IN_AUTH_PAGE_CSP,
11255
11661
  CDN,
11256
11662
  CREDENTIALS_ACCESSOR,
11257
11663
  CimdClientIdMismatchError,
@@ -11266,6 +11672,7 @@ var FetchCredentialMiddleware = class {
11266
11672
  CredentialProviderAlreadyRegisteredError,
11267
11673
  CredentialStorageError,
11268
11674
  CredentialsAccessorImpl,
11675
+ DEFAULT_ALLOWED_SCOPES,
11269
11676
  DEFAULT_INCREMENTAL_TICKET_TTL_MS,
11270
11677
  DEFAULT_RESUME_TTL_MS,
11271
11678
  DEFAULT_THEME,
@@ -11304,6 +11711,9 @@ var FetchCredentialMiddleware = class {
11304
11711
  RedisAuthorizationStore,
11305
11712
  RedisSessionStore,
11306
11713
  SECURE_STORE_ACCESSOR,
11714
+ SIGNIN_BINDING_COOKIE_PREFIX,
11715
+ SIGNIN_BINDING_MAX_AGE_SECONDS,
11716
+ SIGNIN_BINDING_SECURE_COOKIE_PREFIX,
11307
11717
  ScopeDeniedError,
11308
11718
  SecureStoreAccessorImpl,
11309
11719
  SessionCredentialVault,
@@ -11379,6 +11789,7 @@ var FetchCredentialMiddleware = class {
11379
11789
  buildToolConsentPage,
11380
11790
  buildUnauthorizedHeader,
11381
11791
  buildWwwAuthenticate,
11792
+ builtInAuthPageHeaders,
11382
11793
  centeredCardLayout,
11383
11794
  checkSsrfProtection,
11384
11795
  cimdCacheConfigSchema,
@@ -11398,6 +11809,7 @@ var FetchCredentialMiddleware = class {
11398
11809
  createLayout,
11399
11810
  createSecureStore,
11400
11811
  createSessionCredentialVault,
11812
+ createSigninBinding,
11401
11813
  createTokenStorageAdapter,
11402
11814
  credentialProviderConfigSchema,
11403
11815
  credentialSchema,
@@ -11430,6 +11842,8 @@ var FetchCredentialMiddleware = class {
11430
11842
  federatedLoginStateSchema,
11431
11843
  federatedProviderItemSchema,
11432
11844
  federatedSelectionSchema,
11845
+ findAuthoritiesProfileProblems,
11846
+ findAuthoritiesRuleProblems,
11433
11847
  flatRemoteProviderFields,
11434
11848
  fromSessionRecord,
11435
11849
  generateAuthorizationCode,
@@ -11441,6 +11855,7 @@ var FetchCredentialMiddleware = class {
11441
11855
  getProviderApps,
11442
11856
  getProviderScopes,
11443
11857
  getTokenSignatureFingerprint,
11858
+ grantScopes,
11444
11859
  hasOnlyLocalhostRedirectUris,
11445
11860
  headerApplier,
11446
11861
  hkdfSha256,
@@ -11450,6 +11865,7 @@ var FetchCredentialMiddleware = class {
11450
11865
  isFrontMcpCredentials,
11451
11866
  isJwt,
11452
11867
  isLocalMode,
11868
+ isLoopbackRedirectUri,
11453
11869
  isOrchestratedLocal,
11454
11870
  isOrchestratedMode,
11455
11871
  isOrchestratedRemote,
@@ -11479,6 +11895,7 @@ var FetchCredentialMiddleware = class {
11479
11895
  noopLogger,
11480
11896
  normalizeIssuer,
11481
11897
  oauthCredentialSchema,
11898
+ openPendingLogin,
11482
11899
  orchestratedLocalSchema,
11483
11900
  orchestratedRemoteSchema,
11484
11901
  parseAuthOptions,
@@ -11506,6 +11923,7 @@ var FetchCredentialMiddleware = class {
11506
11923
  resolveRequiredScopes,
11507
11924
  resourceIdRefSchema,
11508
11925
  safeDecrypt,
11926
+ sealPendingLogin,
11509
11927
  secureStoreConfigSchema,
11510
11928
  secureStoreCustomBackendSchema,
11511
11929
  secureStoreEncryptionSchema,
@@ -11516,6 +11934,8 @@ var FetchCredentialMiddleware = class {
11516
11934
  signCredentialResumeToken,
11517
11935
  signIncrementalAuthTicket,
11518
11936
  signSession,
11937
+ signinBindingCookieName,
11938
+ signinBindingMatches,
11519
11939
  skippedAppBehaviorSchema,
11520
11940
  sseTransportStateSchema,
11521
11941
  sshKeyCredentialSchema,
@@ -11548,5 +11968,6 @@ var FetchCredentialMiddleware = class {
11548
11968
  verifyOrParseSession,
11549
11969
  verifyPkce,
11550
11970
  verifySession,
11551
- wideLayout
11971
+ wideLayout,
11972
+ withoutSecretFieldValues
11552
11973
  });