@frontmcp/auth 1.8.1 → 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.
- package/authorities/authorities.engine.d.ts +24 -2
- package/authorities/authorities.engine.d.ts.map +1 -1
- package/authorities/authorities.validation.d.ts +27 -0
- package/authorities/authorities.validation.d.ts.map +1 -0
- package/authorities/index.d.ts +1 -0
- package/authorities/index.d.ts.map +1 -1
- package/authorization/dcr-client.registry.d.ts +13 -0
- package/authorization/dcr-client.registry.d.ts.map +1 -1
- package/esm/index.mjs +497 -85
- package/esm/package.json +5 -5
- package/index.d.ts +4 -4
- package/index.d.ts.map +1 -1
- package/index.js +581 -160
- package/jwks/jwks.service.d.ts.map +1 -1
- package/options/app-auth.schema.d.ts +4 -0
- package/options/app-auth.schema.d.ts.map +1 -1
- package/options/interfaces.d.ts +48 -7
- package/options/interfaces.d.ts.map +1 -1
- package/options/orchestrated.schema.d.ts +8 -0
- package/options/orchestrated.schema.d.ts.map +1 -1
- package/options/schema.d.ts +4 -0
- package/options/schema.d.ts.map +1 -1
- package/options/shared.schemas.d.ts +2 -0
- package/options/shared.schemas.d.ts.map +1 -1
- package/package.json +5 -5
- package/session/authorization.store.d.ts +19 -3
- package/session/authorization.store.d.ts.map +1 -1
- package/session/federated-auth.session.d.ts +16 -0
- package/session/federated-auth.session.d.ts.map +1 -1
- package/session/index.d.ts +4 -0
- package/session/index.d.ts.map +1 -1
- package/session/pending-login.d.ts +23 -0
- package/session/pending-login.d.ts.map +1 -0
- package/session/signin-binding.d.ts +39 -0
- package/session/signin-binding.d.ts.map +1 -0
- package/ui/base-layout.d.ts +26 -0
- package/ui/base-layout.d.ts.map +1 -1
- package/ui/index.d.ts +2 -2
- package/ui/index.d.ts.map +1 -1
- package/ui/local-login.helper.d.ts +9 -2
- package/ui/local-login.helper.d.ts.map +1 -1
- package/ui/templates.d.ts +5 -4
- package/ui/templates.d.ts.map +1 -1
- package/utils/index.d.ts +2 -0
- package/utils/index.d.ts.map +1 -1
- package/utils/redirect-uri.utils.d.ts +16 -0
- package/utils/redirect-uri.utils.d.ts.map +1 -0
- package/utils/scope-grant.utils.d.ts +11 -0
- 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: () =>
|
|
274
|
+
EncryptedStorageError: () => import_utils26.EncryptedStorageError,
|
|
273
275
|
EncryptedStorageSecureStoreBackend: () => EncryptedStorageSecureStoreBackend,
|
|
274
|
-
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: () =>
|
|
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: () =>
|
|
411
|
+
decryptAesGcm: () => import_utils36.decryptAesGcm,
|
|
405
412
|
decryptSessionJson: () => decryptSessionJson,
|
|
406
|
-
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: () =>
|
|
421
|
+
encryptAesGcm: () => import_utils36.encryptAesGcm,
|
|
415
422
|
encryptJson: () => encryptJson,
|
|
416
|
-
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: () =>
|
|
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: () =>
|
|
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
|
|
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="
|
|
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="
|
|
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="
|
|
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="
|
|
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
|
|
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/
|
|
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
|
|
2356
|
-
this.pendingStore = new
|
|
2357
|
-
this.refreshStore = new
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
2816
|
-
const { ciphertext, tag } = await (0,
|
|
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,
|
|
2821
|
-
ct: (0,
|
|
2822
|
-
tag: (0,
|
|
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,
|
|
2843
|
-
const ciphertext = (0,
|
|
2844
|
-
const tagBuffer = (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
3351
|
+
jti: (0, import_utils13.randomUUID)(),
|
|
3250
3352
|
exp: Date.now() + (input.ttlMs ?? DEFAULT_INCREMENTAL_TICKET_TTL_MS)
|
|
3251
3353
|
};
|
|
3252
|
-
const signedJson = (0,
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
3610
|
-
const { ciphertext, tag } = await (0,
|
|
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,
|
|
3615
|
-
tag: (0,
|
|
3616
|
-
data: (0,
|
|
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,
|
|
3631
|
-
const tag = (0,
|
|
3632
|
-
const data = (0,
|
|
3633
|
-
const plaintext = await (0,
|
|
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
|
|
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
|
|
3837
|
+
var import_utils18 = require("@frontmcp/utils");
|
|
3736
3838
|
|
|
3737
3839
|
// libs/auth/src/errors/auth-internal.error.ts
|
|
3738
|
-
var
|
|
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,
|
|
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,
|
|
4001
|
+
const secret = config?.secret || (0, import_utils18.getEnv)("MCP_SESSION_SECRET");
|
|
3900
4002
|
if (!secret) {
|
|
3901
|
-
if ((0,
|
|
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,
|
|
4015
|
+
return (0, import_utils18.signData)(session, resolveConfig(config));
|
|
3914
4016
|
}
|
|
3915
4017
|
function verifySession(signedData, config) {
|
|
3916
|
-
return (0,
|
|
4018
|
+
return (0, import_utils18.verifyData)(signedData, resolveConfig(config));
|
|
3917
4019
|
}
|
|
3918
4020
|
function verifyOrParseSession(data, config) {
|
|
3919
|
-
return (0,
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
4252
|
+
const secret = (0, import_utils21.getEnv)("MCP_SESSION_SECRET");
|
|
4151
4253
|
if (!secret) {
|
|
4152
|
-
if ((0,
|
|
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,
|
|
4160
|
-
cachedKey = (0,
|
|
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,
|
|
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,
|
|
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
|
|
4189
|
-
var
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
5119
|
-
const { ciphertext, tag } = (0,
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
5352
|
-
const { ciphertext, tag } = (0,
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
8721
|
+
* @default true
|
|
8609
8722
|
*
|
|
8610
|
-
* SECURITY:
|
|
8611
|
-
*
|
|
8612
|
-
*
|
|
8613
|
-
* (auth-code interception
|
|
8614
|
-
* `
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
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
|
|
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,
|
|
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
|
|
10803
|
-
return {
|
|
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
|
|
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
|
-
*
|
|
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
|
|
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
|
});
|