@frontmcp/auth 1.8.2 → 1.8.4
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 +25 -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 +530 -91
- package/esm/package.json +5 -5
- package/index.d.ts +4 -4
- package/index.d.ts.map +1 -1
- package/index.js +615 -166
- package/jwks/jwks.service.d.ts +4 -1
- 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 +21 -0
- package/utils/scope-grant.utils.d.ts.map +1 -0
package/esm/index.mjs
CHANGED
|
@@ -619,6 +619,12 @@ var TRANSPARENT_JWT_ALGS = [
|
|
|
619
619
|
];
|
|
620
620
|
var MAX_JWKS_FETCH_BYTES = 1048576;
|
|
621
621
|
var MAX_JWKS_FETCH_HOPS = 3;
|
|
622
|
+
var MISSING_EXP_ERROR = 'missing required "exp" claim';
|
|
623
|
+
function isMissingExpError(error) {
|
|
624
|
+
if (typeof error !== "object" || error === null) return false;
|
|
625
|
+
const { code, claim, reason } = error;
|
|
626
|
+
return code === "ERR_JWT_CLAIM_VALIDATION_FAILED" && claim === "exp" && reason === "missing";
|
|
627
|
+
}
|
|
622
628
|
var WEAK_KEY_WARNING = `
|
|
623
629
|
\u26A0\uFE0F SECURITY WARNING: OAuth provider is using an RSA key smaller than 2048 bits.
|
|
624
630
|
This is considered insecure and should be updated.
|
|
@@ -688,6 +694,7 @@ var JwksService = class {
|
|
|
688
694
|
kid = typeof header?.kid === "string" ? header.kid : void 0;
|
|
689
695
|
} catch {
|
|
690
696
|
}
|
|
697
|
+
let missingExp = false;
|
|
691
698
|
for (const p of candidates) {
|
|
692
699
|
let jwks;
|
|
693
700
|
try {
|
|
@@ -699,7 +706,9 @@ var JwksService = class {
|
|
|
699
706
|
// Pin to asymmetric algorithms so a symmetric key in the provider JWKS
|
|
700
707
|
// can't be used to accept an HMAC-signed token, and `none` is refused.
|
|
701
708
|
algorithms: [...TRANSPARENT_JWT_ALGS],
|
|
702
|
-
...issuerConstraint ? { issuer: issuerConstraint } : {}
|
|
709
|
+
...issuerConstraint ? { issuer: issuerConstraint } : {},
|
|
710
|
+
// A bearer token without `exp` would be valid forever (#272).
|
|
711
|
+
requiredClaims: ["exp"]
|
|
703
712
|
});
|
|
704
713
|
return {
|
|
705
714
|
ok: true,
|
|
@@ -717,11 +726,14 @@ var JwksService = class {
|
|
|
717
726
|
if (fallbackResult.ok) {
|
|
718
727
|
return fallbackResult;
|
|
719
728
|
}
|
|
729
|
+
if (fallbackResult.error === "missing_exp") missingExp = true;
|
|
720
730
|
}
|
|
721
731
|
}
|
|
732
|
+
if (isMissingExpError(e)) missingExp = true;
|
|
722
733
|
this.logger.debug("[JwksService] Failed to verify token for provider: %s", p.id, e);
|
|
723
734
|
}
|
|
724
735
|
}
|
|
736
|
+
if (missingExp) return { ok: false, error: MISSING_EXP_ERROR };
|
|
725
737
|
return { ok: false, error: `no_provider_verified${kid ? ` (kid=${kid})` : ""}` };
|
|
726
738
|
}
|
|
727
739
|
/**
|
|
@@ -802,7 +814,10 @@ var JwksService = class {
|
|
|
802
814
|
}
|
|
803
815
|
}
|
|
804
816
|
const nowSec = Math.floor(Date.now() / 1e3);
|
|
805
|
-
if (payload.exp
|
|
817
|
+
if (payload.exp === void 0) {
|
|
818
|
+
return { ok: false, error: "missing_exp" };
|
|
819
|
+
}
|
|
820
|
+
if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp) || payload.exp <= nowSec) {
|
|
806
821
|
return { ok: false, error: "token_expired" };
|
|
807
822
|
}
|
|
808
823
|
if (payload.nbf !== void 0 && (typeof payload.nbf !== "number" || !Number.isFinite(payload.nbf) || payload.nbf > nowSec)) {
|
|
@@ -851,7 +866,10 @@ var JwksService = class {
|
|
|
851
866
|
* 1) inline jwks (if provided) → cache & return
|
|
852
867
|
* 2) cached & fresh (TTL) → return
|
|
853
868
|
* 3) explicit jwksUri → fetch, cache, return
|
|
854
|
-
* 4) discover jwks_uri
|
|
869
|
+
* 4) discover jwks_uri → the issuer's OAuth authorization server
|
|
870
|
+
* metadata (RFC 8414), else its OpenID configuration (OpenID Connect
|
|
871
|
+
* Discovery 1.0 §4, where Google, Okta, Auth0 and Entra publish it),
|
|
872
|
+
* then fetch jwks_uri, cache, return
|
|
855
873
|
*/
|
|
856
874
|
async getJwksForProvider(ref) {
|
|
857
875
|
if (ref.jwks?.keys?.length) {
|
|
@@ -867,11 +885,13 @@ var JwksService = class {
|
|
|
867
885
|
if (fromUri?.keys?.length) return fromUri;
|
|
868
886
|
}
|
|
869
887
|
const issuer = trimSlash(ref.issuerUrl);
|
|
870
|
-
const
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
888
|
+
for (const document of ["oauth-authorization-server", "openid-configuration"]) {
|
|
889
|
+
const meta = await this.tryFetchAsMeta(`${issuer}/.well-known/${document}`);
|
|
890
|
+
const uri = meta && typeof meta === "object" && meta["jwks_uri"] ? String(meta["jwks_uri"]) : void 0;
|
|
891
|
+
if (uri) {
|
|
892
|
+
const fromMeta = await this.tryFetchJwks(ref.id, uri);
|
|
893
|
+
if (fromMeta?.keys?.length) return fromMeta;
|
|
894
|
+
}
|
|
875
895
|
}
|
|
876
896
|
const maxStaleMs = Math.max(this.opts.providerJwksTtlMs * 4, 6e4);
|
|
877
897
|
if (cached && Date.now() - cached.fetchedAt < maxStaleMs) {
|
|
@@ -1058,6 +1078,26 @@ var CDN = {
|
|
|
1058
1078
|
stylesheet: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
|
|
1059
1079
|
}
|
|
1060
1080
|
};
|
|
1081
|
+
var BUILT_IN_AUTH_PAGE_CSP = [
|
|
1082
|
+
"default-src 'self'",
|
|
1083
|
+
"script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net",
|
|
1084
|
+
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
|
|
1085
|
+
"font-src 'self' https://fonts.gstatic.com",
|
|
1086
|
+
"img-src 'self' data: https:",
|
|
1087
|
+
"connect-src 'self'",
|
|
1088
|
+
"object-src 'none'",
|
|
1089
|
+
"base-uri 'none'",
|
|
1090
|
+
"frame-ancestors 'none'"
|
|
1091
|
+
].join("; ");
|
|
1092
|
+
function builtInAuthPageHeaders(options = {}) {
|
|
1093
|
+
return {
|
|
1094
|
+
...options.csp === false ? {} : { "Content-Security-Policy": BUILT_IN_AUTH_PAGE_CSP },
|
|
1095
|
+
"X-Frame-Options": "DENY",
|
|
1096
|
+
"X-Content-Type-Options": "nosniff",
|
|
1097
|
+
"Referrer-Policy": "same-origin",
|
|
1098
|
+
"Cache-Control": "no-store"
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1061
1101
|
var DEFAULT_THEME = {
|
|
1062
1102
|
colors: {
|
|
1063
1103
|
primary: "#3b82f6",
|
|
@@ -1295,7 +1335,7 @@ function buildIncrementalAuthPage(params) {
|
|
|
1295
1335
|
</div>
|
|
1296
1336
|
</div>
|
|
1297
1337
|
|
|
1298
|
-
<form method="
|
|
1338
|
+
<form method="POST" action="${escapeHtml3(callbackPath)}" class="flex gap-3 pt-4 border-t border-gray-100">
|
|
1299
1339
|
<input type="hidden" name="pending_auth_id" value="${escapeHtml3(sessionHint)}">
|
|
1300
1340
|
<button type="button" onclick="window.close()"
|
|
1301
1341
|
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">
|
|
@@ -1341,7 +1381,7 @@ function buildFederatedLoginPage(params) {
|
|
|
1341
1381
|
${escapeHtml3(clientName)} uses multiple authentication providers. Select which ones you want to authorize.
|
|
1342
1382
|
</p>
|
|
1343
1383
|
|
|
1344
|
-
<form method="
|
|
1384
|
+
<form method="POST" action="${escapeHtml3(callbackPath)}" id="federated-form">
|
|
1345
1385
|
<input type="hidden" name="pending_auth_id" value="${escapeHtml3(pendingAuthId)}">
|
|
1346
1386
|
<input type="hidden" name="federated" value="true">
|
|
1347
1387
|
|
|
@@ -1486,7 +1526,7 @@ function buildToolConsentPage(params) {
|
|
|
1486
1526
|
${errorBanner}
|
|
1487
1527
|
${userInfo}
|
|
1488
1528
|
|
|
1489
|
-
<form method="
|
|
1529
|
+
<form method="POST" action="${escapeHtml3(callbackPath)}" id="consent-form">
|
|
1490
1530
|
<input type="hidden" name="csrf" value="${escapeHtml3(csrfToken)}">
|
|
1491
1531
|
<input type="hidden" name="pending_auth_id" value="${escapeHtml3(pendingAuthId)}">
|
|
1492
1532
|
<input type="hidden" name="consent_submitted" value="1">
|
|
@@ -1584,7 +1624,7 @@ function buildLoginPage(params) {
|
|
|
1584
1624
|
|
|
1585
1625
|
${scopesHtml}
|
|
1586
1626
|
|
|
1587
|
-
<form method="
|
|
1627
|
+
<form method="POST" action="${escapeHtml3(callbackPath)}">
|
|
1588
1628
|
<input type="hidden" name="pending_auth_id" value="${escapeHtml3(pendingAuthId)}">
|
|
1589
1629
|
${fieldsHtml}
|
|
1590
1630
|
|
|
@@ -1681,7 +1721,17 @@ function toLoginExtraFields(fields, values) {
|
|
|
1681
1721
|
value: values?.[name]
|
|
1682
1722
|
}));
|
|
1683
1723
|
}
|
|
1684
|
-
function
|
|
1724
|
+
function withoutSecretFieldValues(values, fields) {
|
|
1725
|
+
if (!values) return void 0;
|
|
1726
|
+
const kept = {};
|
|
1727
|
+
for (const [name, value] of Object.entries(values)) {
|
|
1728
|
+
if (fields?.[name]?.type === "password") continue;
|
|
1729
|
+
kept[name] = value;
|
|
1730
|
+
}
|
|
1731
|
+
return kept;
|
|
1732
|
+
}
|
|
1733
|
+
function renderLocalLoginPage(login, ctx, submitted) {
|
|
1734
|
+
const values = withoutSecretFieldValues(submitted, login?.fields);
|
|
1685
1735
|
if (login?.render) {
|
|
1686
1736
|
return login.render({ ...ctx, values });
|
|
1687
1737
|
}
|
|
@@ -1788,7 +1838,8 @@ function buildPendingRecord(params) {
|
|
|
1788
1838
|
priorAuthorizedAppIds: params.priorAuthorizedAppIds,
|
|
1789
1839
|
federatedLogin: params.federatedLogin,
|
|
1790
1840
|
consent: params.consent,
|
|
1791
|
-
authUiCsrf: params.authUiCsrf
|
|
1841
|
+
authUiCsrf: params.authUiCsrf,
|
|
1842
|
+
signinBinding: params.signinBinding
|
|
1792
1843
|
};
|
|
1793
1844
|
}
|
|
1794
1845
|
function buildRefreshTokenRecord(params) {
|
|
@@ -2018,6 +2069,63 @@ var RedisAuthorizationStore = class {
|
|
|
2018
2069
|
}
|
|
2019
2070
|
};
|
|
2020
2071
|
|
|
2072
|
+
// libs/auth/src/session/pending-login.ts
|
|
2073
|
+
import {
|
|
2074
|
+
base64urlDecode,
|
|
2075
|
+
base64urlEncode,
|
|
2076
|
+
decryptAesGcm,
|
|
2077
|
+
encryptAesGcm,
|
|
2078
|
+
hkdfSha256,
|
|
2079
|
+
randomBytes as randomBytes2
|
|
2080
|
+
} from "@frontmcp/utils";
|
|
2081
|
+
var SALT = new TextEncoder().encode("frontmcp-pending-login");
|
|
2082
|
+
function keyFor(secret, pendingAuthId) {
|
|
2083
|
+
return hkdfSha256(secret, SALT, new TextEncoder().encode(`pending-login:${pendingAuthId}`), 32);
|
|
2084
|
+
}
|
|
2085
|
+
function sealPendingLogin(secret, pendingAuthId, state) {
|
|
2086
|
+
const iv = randomBytes2(12);
|
|
2087
|
+
const { ciphertext, tag } = encryptAesGcm(
|
|
2088
|
+
keyFor(secret, pendingAuthId),
|
|
2089
|
+
new TextEncoder().encode(JSON.stringify(state)),
|
|
2090
|
+
iv
|
|
2091
|
+
);
|
|
2092
|
+
return [iv, tag, ciphertext].map((part) => base64urlEncode(part)).join(".");
|
|
2093
|
+
}
|
|
2094
|
+
function openPendingLogin(secret, pendingAuthId, sealed) {
|
|
2095
|
+
try {
|
|
2096
|
+
const [iv, tag, ciphertext] = sealed.split(".").map((part) => base64urlDecode(part));
|
|
2097
|
+
if (!iv || !tag || !ciphertext) return void 0;
|
|
2098
|
+
const plaintext = decryptAesGcm(keyFor(secret, pendingAuthId), ciphertext, iv, tag);
|
|
2099
|
+
const state = JSON.parse(new TextDecoder().decode(plaintext));
|
|
2100
|
+
return typeof state?.sub === "string" && state.sub.length > 0 ? state : void 0;
|
|
2101
|
+
} catch {
|
|
2102
|
+
return void 0;
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
// libs/auth/src/session/signin-binding.ts
|
|
2107
|
+
import { base64urlEncode as base64urlEncode2, getCookie, randomBytes as randomBytes3, sha256Hex, timingSafeEqual } from "@frontmcp/utils";
|
|
2108
|
+
var SIGNIN_BINDING_COOKIE_PREFIX = "frontmcp_signin_";
|
|
2109
|
+
var SIGNIN_BINDING_SECURE_COOKIE_PREFIX = `__Host-${SIGNIN_BINDING_COOKIE_PREFIX}`;
|
|
2110
|
+
var SIGNIN_BINDING_MAX_AGE_SECONDS = 30 * 60;
|
|
2111
|
+
function signinBindingCookieName(pendingAuthId, options = {}) {
|
|
2112
|
+
const prefix = options.secure ? SIGNIN_BINDING_SECURE_COOKIE_PREFIX : SIGNIN_BINDING_COOKIE_PREFIX;
|
|
2113
|
+
return `${prefix}${sha256Hex(`signin:${pendingAuthId}`).slice(0, 16)}`;
|
|
2114
|
+
}
|
|
2115
|
+
function createSigninBinding(pendingAuthId, options = {}) {
|
|
2116
|
+
const value = base64urlEncode2(randomBytes3(32));
|
|
2117
|
+
return { cookieName: signinBindingCookieName(pendingAuthId, options), value, hash: sha256Hex(value) };
|
|
2118
|
+
}
|
|
2119
|
+
function signinBindingMatches(cookieHeader, pendingAuthId, expectedHash, options = {}) {
|
|
2120
|
+
if (!expectedHash) return false;
|
|
2121
|
+
const value = getCookie(cookieHeader, signinBindingCookieName(pendingAuthId, options));
|
|
2122
|
+
if (!value) return false;
|
|
2123
|
+
const encoder = new TextEncoder();
|
|
2124
|
+
const actual = encoder.encode(sha256Hex(value));
|
|
2125
|
+
const expected = encoder.encode(expectedHash);
|
|
2126
|
+
return actual.length === expected.length && timingSafeEqual(actual, expected);
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2021
2129
|
// libs/auth/src/session/storage-authorization.store.ts
|
|
2022
2130
|
import { TypedStorage } from "@frontmcp/utils";
|
|
2023
2131
|
var StorageAuthorizationStore = class {
|
|
@@ -2410,12 +2518,12 @@ import { getEnv, randomUUID as randomUUID2 } from "@frontmcp/utils";
|
|
|
2410
2518
|
// libs/auth/src/session/vault-encryption.ts
|
|
2411
2519
|
import { z as z3 } from "@frontmcp/lazy-zod";
|
|
2412
2520
|
import {
|
|
2413
|
-
base64urlDecode,
|
|
2414
|
-
base64urlEncode,
|
|
2415
|
-
decryptAesGcm,
|
|
2416
|
-
encryptAesGcm,
|
|
2417
|
-
hkdfSha256,
|
|
2418
|
-
randomBytes as
|
|
2521
|
+
base64urlDecode as base64urlDecode2,
|
|
2522
|
+
base64urlEncode as base64urlEncode3,
|
|
2523
|
+
decryptAesGcm as decryptAesGcm2,
|
|
2524
|
+
encryptAesGcm as encryptAesGcm2,
|
|
2525
|
+
hkdfSha256 as hkdfSha2562,
|
|
2526
|
+
randomBytes as randomBytes4
|
|
2419
2527
|
} from "@frontmcp/utils";
|
|
2420
2528
|
var encryptedDataSchema = z3.object({
|
|
2421
2529
|
/** Version for future algorithm changes */
|
|
@@ -2456,7 +2564,7 @@ var VaultEncryption = class {
|
|
|
2456
2564
|
];
|
|
2457
2565
|
const ikm = new TextEncoder().encode(ikmParts.join(""));
|
|
2458
2566
|
const infoBytes = new TextEncoder().encode(this.hkdfInfo);
|
|
2459
|
-
const key = await
|
|
2567
|
+
const key = await hkdfSha2562(ikm, new Uint8Array(0), infoBytes, 32);
|
|
2460
2568
|
return key;
|
|
2461
2569
|
}
|
|
2462
2570
|
/**
|
|
@@ -2483,7 +2591,7 @@ var VaultEncryption = class {
|
|
|
2483
2591
|
];
|
|
2484
2592
|
const ikm = new TextEncoder().encode(ikmParts.join(""));
|
|
2485
2593
|
const infoBytes = new TextEncoder().encode(this.hkdfInfo);
|
|
2486
|
-
const key = await
|
|
2594
|
+
const key = await hkdfSha2562(ikm, new Uint8Array(0), infoBytes, 32);
|
|
2487
2595
|
return key;
|
|
2488
2596
|
}
|
|
2489
2597
|
/**
|
|
@@ -2497,14 +2605,14 @@ var VaultEncryption = class {
|
|
|
2497
2605
|
if (key.length !== 32) {
|
|
2498
2606
|
throw new Error("Encryption key must be 32 bytes");
|
|
2499
2607
|
}
|
|
2500
|
-
const iv =
|
|
2501
|
-
const { ciphertext, tag } = await
|
|
2608
|
+
const iv = randomBytes4(12);
|
|
2609
|
+
const { ciphertext, tag } = await encryptAesGcm2(key, new TextEncoder().encode(plaintext), iv);
|
|
2502
2610
|
return {
|
|
2503
2611
|
v: 1,
|
|
2504
2612
|
alg: "aes-256-gcm",
|
|
2505
|
-
iv:
|
|
2506
|
-
ct:
|
|
2507
|
-
tag:
|
|
2613
|
+
iv: base64urlEncode3(iv),
|
|
2614
|
+
ct: base64urlEncode3(ciphertext),
|
|
2615
|
+
tag: base64urlEncode3(tag)
|
|
2508
2616
|
};
|
|
2509
2617
|
}
|
|
2510
2618
|
/**
|
|
@@ -2524,11 +2632,11 @@ var VaultEncryption = class {
|
|
|
2524
2632
|
throw new Error("Invalid encrypted data format");
|
|
2525
2633
|
}
|
|
2526
2634
|
const { iv, ct, tag } = parsed.data;
|
|
2527
|
-
const ivBuffer =
|
|
2528
|
-
const ciphertext =
|
|
2529
|
-
const tagBuffer =
|
|
2635
|
+
const ivBuffer = base64urlDecode2(iv);
|
|
2636
|
+
const ciphertext = base64urlDecode2(ct);
|
|
2637
|
+
const tagBuffer = base64urlDecode2(tag);
|
|
2530
2638
|
try {
|
|
2531
|
-
const plaintext = await
|
|
2639
|
+
const plaintext = await decryptAesGcm2(key, ciphertext, ivBuffer, tagBuffer);
|
|
2532
2640
|
return new TextDecoder().decode(plaintext);
|
|
2533
2641
|
} catch (_error) {
|
|
2534
2642
|
throw new Error("Decryption failed: invalid key or corrupted data");
|
|
@@ -3124,7 +3232,7 @@ function errMsg2(err) {
|
|
|
3124
3232
|
var SECURE_STORE_ACCESSOR = /* @__PURE__ */ Symbol.for("frontmcp:SECURE_STORE_ACCESSOR");
|
|
3125
3233
|
|
|
3126
3234
|
// libs/auth/src/session/secure-store-accessor.impl.ts
|
|
3127
|
-
import { sha256Hex } from "@frontmcp/utils";
|
|
3235
|
+
import { sha256Hex as sha256Hex2 } from "@frontmcp/utils";
|
|
3128
3236
|
var GLOBAL_NAMESPACE = "global";
|
|
3129
3237
|
var SecureStoreAccessorImpl = class {
|
|
3130
3238
|
backend;
|
|
@@ -3153,12 +3261,12 @@ var SecureStoreAccessorImpl = class {
|
|
|
3153
3261
|
return GLOBAL_NAMESPACE;
|
|
3154
3262
|
case "session": {
|
|
3155
3263
|
const sid = this.resolveSessionId();
|
|
3156
|
-
return sid ? `s:${
|
|
3264
|
+
return sid ? `s:${sha256Hex2(sid).slice(0, 32)}` : void 0;
|
|
3157
3265
|
}
|
|
3158
3266
|
case "user":
|
|
3159
3267
|
default: {
|
|
3160
3268
|
const sub = this.resolveSub();
|
|
3161
|
-
return sub ? `u:${
|
|
3269
|
+
return sub ? `u:${sha256Hex2(sub).slice(0, 32)}` : void 0;
|
|
3162
3270
|
}
|
|
3163
3271
|
}
|
|
3164
3272
|
}
|
|
@@ -3262,7 +3370,7 @@ async function createSecureStore(options) {
|
|
|
3262
3370
|
}
|
|
3263
3371
|
|
|
3264
3372
|
// libs/auth/src/session/token.vault.ts
|
|
3265
|
-
import { encryptAesGcm as
|
|
3373
|
+
import { encryptAesGcm as encryptAesGcm3, decryptAesGcm as decryptAesGcm3, randomBytes as randomBytes5, base64urlEncode as base64urlEncode4, base64urlDecode as base64urlDecode3 } from "@frontmcp/utils";
|
|
3266
3374
|
var TokenVault = class {
|
|
3267
3375
|
/** Active key used for new encryptions */
|
|
3268
3376
|
active;
|
|
@@ -3291,14 +3399,14 @@ var TokenVault = class {
|
|
|
3291
3399
|
this.keys.set(k.kid, k.key);
|
|
3292
3400
|
}
|
|
3293
3401
|
async encrypt(plaintext, opts) {
|
|
3294
|
-
const iv =
|
|
3295
|
-
const { ciphertext, tag } = await
|
|
3402
|
+
const iv = randomBytes5(12);
|
|
3403
|
+
const { ciphertext, tag } = await encryptAesGcm3(this.active.key, new TextEncoder().encode(plaintext), iv);
|
|
3296
3404
|
return {
|
|
3297
3405
|
alg: "A256GCM",
|
|
3298
3406
|
kid: this.active.kid,
|
|
3299
|
-
iv:
|
|
3300
|
-
tag:
|
|
3301
|
-
data:
|
|
3407
|
+
iv: base64urlEncode4(iv),
|
|
3408
|
+
tag: base64urlEncode4(tag),
|
|
3409
|
+
data: base64urlEncode4(ciphertext),
|
|
3302
3410
|
exp: opts?.exp,
|
|
3303
3411
|
meta: opts?.meta
|
|
3304
3412
|
};
|
|
@@ -3312,21 +3420,21 @@ var TokenVault = class {
|
|
|
3312
3420
|
}
|
|
3313
3421
|
const key = this.keys.get(blob.kid);
|
|
3314
3422
|
if (!key) throw new Error(`vault_unknown_kid:${blob.kid}`);
|
|
3315
|
-
const iv =
|
|
3316
|
-
const tag =
|
|
3317
|
-
const data =
|
|
3318
|
-
const plaintext = await
|
|
3423
|
+
const iv = base64urlDecode3(blob.iv);
|
|
3424
|
+
const tag = base64urlDecode3(blob.tag);
|
|
3425
|
+
const data = base64urlDecode3(blob.data);
|
|
3426
|
+
const plaintext = await decryptAesGcm3(key, data, iv, tag);
|
|
3319
3427
|
return new TextDecoder().decode(plaintext);
|
|
3320
3428
|
}
|
|
3321
3429
|
};
|
|
3322
3430
|
|
|
3323
3431
|
// libs/auth/src/session/index.ts
|
|
3324
3432
|
import {
|
|
3325
|
-
hkdfSha256 as
|
|
3433
|
+
hkdfSha256 as hkdfSha2565,
|
|
3326
3434
|
encryptValue as encryptValue2,
|
|
3327
3435
|
decryptValue as decryptValue2,
|
|
3328
|
-
encryptAesGcm as
|
|
3329
|
-
decryptAesGcm as
|
|
3436
|
+
encryptAesGcm as encryptAesGcm6,
|
|
3437
|
+
decryptAesGcm as decryptAesGcm6
|
|
3330
3438
|
} from "@frontmcp/utils";
|
|
3331
3439
|
|
|
3332
3440
|
// libs/auth/src/session/transport-session.types.ts
|
|
@@ -3433,7 +3541,7 @@ import {
|
|
|
3433
3541
|
} from "@frontmcp/utils";
|
|
3434
3542
|
|
|
3435
3543
|
// libs/auth/src/errors/auth-internal.error.ts
|
|
3436
|
-
import { randomBytes as
|
|
3544
|
+
import { randomBytes as randomBytes6, bytesToHex as bytesToHex2 } from "@frontmcp/utils";
|
|
3437
3545
|
var AuthInternalError = class extends Error {
|
|
3438
3546
|
/**
|
|
3439
3547
|
* Unique error ID for tracking in logs.
|
|
@@ -3455,7 +3563,7 @@ var AuthInternalError = class extends Error {
|
|
|
3455
3563
|
super(message);
|
|
3456
3564
|
this.name = this.constructor.name;
|
|
3457
3565
|
this.code = code;
|
|
3458
|
-
this.errorId = `err_${bytesToHex2(
|
|
3566
|
+
this.errorId = `err_${bytesToHex2(randomBytes6(8))}`;
|
|
3459
3567
|
Error.captureStackTrace(this, this.constructor);
|
|
3460
3568
|
}
|
|
3461
3569
|
/**
|
|
@@ -4761,7 +4869,7 @@ var VercelKvSessionStore = class {
|
|
|
4761
4869
|
};
|
|
4762
4870
|
|
|
4763
4871
|
// libs/auth/src/session/orchestrated-token.store.ts
|
|
4764
|
-
import { encryptAesGcm as
|
|
4872
|
+
import { encryptAesGcm as encryptAesGcm4, decryptAesGcm as decryptAesGcm4, randomBytes as randomBytes7, hkdfSha256 as hkdfSha2563 } from "@frontmcp/utils";
|
|
4765
4873
|
var InMemoryOrchestratedTokenStore = class {
|
|
4766
4874
|
/** Token storage: Map<compositeKey, ProviderTokenRecord> */
|
|
4767
4875
|
tokens = /* @__PURE__ */ new Map();
|
|
@@ -4803,7 +4911,7 @@ var InMemoryOrchestratedTokenStore = class {
|
|
|
4803
4911
|
}
|
|
4804
4912
|
const info = new TextEncoder().encode(`orchestrated-token:${compositeKey}`);
|
|
4805
4913
|
const salt = new TextEncoder().encode("frontmcp-token-store");
|
|
4806
|
-
const derivedKey =
|
|
4914
|
+
const derivedKey = hkdfSha2563(this.encryptionKey, salt, info, 32);
|
|
4807
4915
|
this.derivedKeys.set(compositeKey, derivedKey);
|
|
4808
4916
|
return derivedKey;
|
|
4809
4917
|
}
|
|
@@ -4813,8 +4921,8 @@ var InMemoryOrchestratedTokenStore = class {
|
|
|
4813
4921
|
async encryptRecord(compositeKey, record) {
|
|
4814
4922
|
const key = await this.deriveKeyForRecord(compositeKey);
|
|
4815
4923
|
const plaintext = JSON.stringify(record);
|
|
4816
|
-
const iv =
|
|
4817
|
-
const { ciphertext, tag } =
|
|
4924
|
+
const iv = randomBytes7(12);
|
|
4925
|
+
const { ciphertext, tag } = encryptAesGcm4(key, new TextEncoder().encode(plaintext), iv);
|
|
4818
4926
|
return JSON.stringify({
|
|
4819
4927
|
iv: Buffer.from(iv).toString("base64url"),
|
|
4820
4928
|
tag: Buffer.from(tag).toString("base64url"),
|
|
@@ -4830,7 +4938,7 @@ var InMemoryOrchestratedTokenStore = class {
|
|
|
4830
4938
|
const ivBytes = Buffer.from(iv, "base64url");
|
|
4831
4939
|
const tagBytes = Buffer.from(tag, "base64url");
|
|
4832
4940
|
const ciphertextBytes = Buffer.from(data, "base64url");
|
|
4833
|
-
const plaintext =
|
|
4941
|
+
const plaintext = decryptAesGcm4(key, ciphertextBytes, ivBytes, tagBytes);
|
|
4834
4942
|
return JSON.parse(new TextDecoder().decode(plaintext));
|
|
4835
4943
|
}
|
|
4836
4944
|
/**
|
|
@@ -5032,7 +5140,7 @@ var InMemoryOrchestratedTokenStore = class {
|
|
|
5032
5140
|
};
|
|
5033
5141
|
|
|
5034
5142
|
// libs/auth/src/session/orchestrated-token.crypto.ts
|
|
5035
|
-
import { decryptAesGcm as
|
|
5143
|
+
import { decryptAesGcm as decryptAesGcm5, encryptAesGcm as encryptAesGcm5, hkdfSha256 as hkdfSha2564, randomBytes as randomBytes8 } from "@frontmcp/utils";
|
|
5036
5144
|
var HKDF_SALT = new TextEncoder().encode("frontmcp-token-store");
|
|
5037
5145
|
function deriveKeyForRecord(masterKey, compositeKey, cache) {
|
|
5038
5146
|
const cached = cache.get(compositeKey);
|
|
@@ -5040,14 +5148,14 @@ function deriveKeyForRecord(masterKey, compositeKey, cache) {
|
|
|
5040
5148
|
return cached;
|
|
5041
5149
|
}
|
|
5042
5150
|
const info = new TextEncoder().encode(`orchestrated-token:${compositeKey}`);
|
|
5043
|
-
const derived =
|
|
5151
|
+
const derived = hkdfSha2564(masterKey, HKDF_SALT, info, 32);
|
|
5044
5152
|
cache.set(compositeKey, derived);
|
|
5045
5153
|
return derived;
|
|
5046
5154
|
}
|
|
5047
5155
|
function encryptRecord(key, record) {
|
|
5048
5156
|
const plaintext = JSON.stringify(record);
|
|
5049
|
-
const iv =
|
|
5050
|
-
const { ciphertext, tag } =
|
|
5157
|
+
const iv = randomBytes8(12);
|
|
5158
|
+
const { ciphertext, tag } = encryptAesGcm5(key, new TextEncoder().encode(plaintext), iv);
|
|
5051
5159
|
return JSON.stringify({
|
|
5052
5160
|
iv: Buffer.from(iv).toString("base64url"),
|
|
5053
5161
|
tag: Buffer.from(tag).toString("base64url"),
|
|
@@ -5059,7 +5167,7 @@ function decryptRecord(key, encrypted) {
|
|
|
5059
5167
|
const ivBytes = Buffer.from(iv, "base64url");
|
|
5060
5168
|
const tagBytes = Buffer.from(tag, "base64url");
|
|
5061
5169
|
const ciphertextBytes = Buffer.from(data, "base64url");
|
|
5062
|
-
const plaintext =
|
|
5170
|
+
const plaintext = decryptAesGcm5(key, ciphertextBytes, ivBytes, tagBytes);
|
|
5063
5171
|
return JSON.parse(new TextDecoder().decode(plaintext));
|
|
5064
5172
|
}
|
|
5065
5173
|
|
|
@@ -5325,6 +5433,7 @@ var InMemoryFederatedAuthSessionStore = class {
|
|
|
5325
5433
|
providerQueue: [...params.providerIds],
|
|
5326
5434
|
completedProviders: /* @__PURE__ */ new Map(),
|
|
5327
5435
|
skippedProviders: [],
|
|
5436
|
+
signinBinding: params.signinBinding,
|
|
5328
5437
|
createdAt: now,
|
|
5329
5438
|
expiresAt: now + this.sessionTtlMs
|
|
5330
5439
|
};
|
|
@@ -5357,6 +5466,7 @@ function createFederatedAuthSession(params, ttlMs = 15 * 60 * 1e3) {
|
|
|
5357
5466
|
providerQueue: [...params.providerIds],
|
|
5358
5467
|
completedProviders: /* @__PURE__ */ new Map(),
|
|
5359
5468
|
skippedProviders: [],
|
|
5469
|
+
signinBinding: params.signinBinding,
|
|
5360
5470
|
createdAt: now,
|
|
5361
5471
|
expiresAt: now + ttlMs
|
|
5362
5472
|
};
|
|
@@ -5911,7 +6021,7 @@ function createEncryptedVault(redis, config = {}) {
|
|
|
5911
6021
|
}
|
|
5912
6022
|
|
|
5913
6023
|
// libs/auth/src/session/token.refresh.ts
|
|
5914
|
-
import { base64urlDecode as
|
|
6024
|
+
import { base64urlDecode as base64urlDecode4 } from "@frontmcp/utils";
|
|
5915
6025
|
function toEpochSeconds(exp) {
|
|
5916
6026
|
if (exp == null) return void 0;
|
|
5917
6027
|
if (exp instanceof Date) return Math.floor(exp.getTime() / 1e3);
|
|
@@ -5933,7 +6043,7 @@ function tryJwtExp(token) {
|
|
|
5933
6043
|
const parts = token.split(".");
|
|
5934
6044
|
if (parts.length < 2) return void 0;
|
|
5935
6045
|
try {
|
|
5936
|
-
const json = JSON.parse(new TextDecoder().decode(
|
|
6046
|
+
const json = JSON.parse(new TextDecoder().decode(base64urlDecode4(parts[1])));
|
|
5937
6047
|
const e = json?.exp;
|
|
5938
6048
|
return typeof e === "number" ? toEpochSeconds(e) : void 0;
|
|
5939
6049
|
} catch {
|
|
@@ -5996,7 +6106,7 @@ var progressiveAuthStateSchema = z6.object({
|
|
|
5996
6106
|
});
|
|
5997
6107
|
|
|
5998
6108
|
// libs/auth/src/authorization/authorization.class.ts
|
|
5999
|
-
import { base64urlDecode as
|
|
6109
|
+
import { base64urlDecode as base64urlDecode5, getMachineId as getMachineId2, randomUUID as randomUUID11 } from "@frontmcp/utils";
|
|
6000
6110
|
var AuthorizationBase = class _AuthorizationBase {
|
|
6001
6111
|
id;
|
|
6002
6112
|
isAnonymous;
|
|
@@ -6192,7 +6302,7 @@ var AuthorizationBase = class _AuthorizationBase {
|
|
|
6192
6302
|
*/
|
|
6193
6303
|
static isJwtHeader(segment) {
|
|
6194
6304
|
try {
|
|
6195
|
-
const bytes =
|
|
6305
|
+
const bytes = base64urlDecode5(segment);
|
|
6196
6306
|
const text = new TextDecoder().decode(bytes);
|
|
6197
6307
|
const parsed = JSON.parse(text);
|
|
6198
6308
|
return typeof parsed === "object" && parsed !== null && typeof parsed.alg === "string";
|
|
@@ -6364,7 +6474,7 @@ var PublicAuthorization = class _PublicAuthorization extends AuthorizationBase {
|
|
|
6364
6474
|
};
|
|
6365
6475
|
|
|
6366
6476
|
// libs/auth/src/authorization/transparent.authorization.ts
|
|
6367
|
-
import { sha256Hex as
|
|
6477
|
+
import { sha256Hex as sha256Hex3 } from "@frontmcp/utils";
|
|
6368
6478
|
var TransparentAuthorization = class _TransparentAuthorization extends AuthorizationBase {
|
|
6369
6479
|
mode = "transparent";
|
|
6370
6480
|
/**
|
|
@@ -6464,7 +6574,7 @@ var TransparentAuthorization = class _TransparentAuthorization extends Authoriza
|
|
|
6464
6574
|
static generateAuthorizationId(token) {
|
|
6465
6575
|
const parts = token.split(".");
|
|
6466
6576
|
const signature = parts[2] || token;
|
|
6467
|
-
return
|
|
6577
|
+
return sha256Hex3(signature).substring(0, 16);
|
|
6468
6578
|
}
|
|
6469
6579
|
/**
|
|
6470
6580
|
* Get the issuer from the token claims
|
|
@@ -6490,11 +6600,11 @@ var TransparentAuthorization = class _TransparentAuthorization extends Authoriza
|
|
|
6490
6600
|
};
|
|
6491
6601
|
|
|
6492
6602
|
// libs/auth/src/utils/authorization-id.utils.ts
|
|
6493
|
-
import { sha256Hex as
|
|
6603
|
+
import { sha256Hex as sha256Hex4 } from "@frontmcp/utils";
|
|
6494
6604
|
function deriveAuthorizationId(token) {
|
|
6495
6605
|
const parts = token.split(".");
|
|
6496
6606
|
const signature = parts[2] || token;
|
|
6497
|
-
return
|
|
6607
|
+
return sha256Hex4(signature).substring(0, 16);
|
|
6498
6608
|
}
|
|
6499
6609
|
|
|
6500
6610
|
// libs/auth/src/authorization/orchestrated.authorization.ts
|
|
@@ -6863,7 +6973,7 @@ var OrchestratedAuthAccessorAdapter = class {
|
|
|
6863
6973
|
};
|
|
6864
6974
|
|
|
6865
6975
|
// libs/auth/src/authorization/dcr-client.registry.ts
|
|
6866
|
-
import { sha256 as sha2562, timingSafeEqual } from "@frontmcp/utils";
|
|
6976
|
+
import { sha256 as sha2562, timingSafeEqual as timingSafeEqual2 } from "@frontmcp/utils";
|
|
6867
6977
|
var DEFAULT_MAX_DYNAMIC_CLIENTS = 1e3;
|
|
6868
6978
|
function globMatch(pattern, input) {
|
|
6869
6979
|
let p = 0;
|
|
@@ -6989,7 +7099,7 @@ var DcrClientRegistry = class {
|
|
|
6989
7099
|
if (!isConfidential) return "ok";
|
|
6990
7100
|
if (!client.client_secret) return "invalid";
|
|
6991
7101
|
if (!presentedSecret) return "invalid";
|
|
6992
|
-
return
|
|
7102
|
+
return timingSafeEqual2(sha2562(presentedSecret), sha2562(client.client_secret)) ? "ok" : "invalid";
|
|
6993
7103
|
}
|
|
6994
7104
|
/** Whether a client id is known to the registry. */
|
|
6995
7105
|
has(clientId) {
|
|
@@ -7077,7 +7187,7 @@ var DcrClientRegistry = class {
|
|
|
7077
7187
|
if (typeof presented !== "string" || presented.length === 0) {
|
|
7078
7188
|
return false;
|
|
7079
7189
|
}
|
|
7080
|
-
return
|
|
7190
|
+
return timingSafeEqual2(sha2562(presented), sha2562(expected));
|
|
7081
7191
|
}
|
|
7082
7192
|
};
|
|
7083
7193
|
|
|
@@ -7894,7 +8004,16 @@ var upstreamProviderSchema = z10.object({
|
|
|
7894
8004
|
/** User info endpoint (optional, used to enrich the session identity). */
|
|
7895
8005
|
userInfoEndpoint: z10.string().url().optional(),
|
|
7896
8006
|
/** JWKS URI for upstream id_token validation (optional). */
|
|
7897
|
-
jwksUri: z10.string().url().optional()
|
|
8007
|
+
jwksUri: z10.string().url().optional(),
|
|
8008
|
+
/**
|
|
8009
|
+
* The provider's issuer identifier (its OIDC `iss`). When set, an RFC 9207
|
|
8010
|
+
* `iss` on the provider's callback and the `iss` of its `id_token` must name
|
|
8011
|
+
* it (or one of {@link additionalIssuers}). Without it the provider's
|
|
8012
|
+
* `id_token` is not used for identity; `userInfoEndpoint` is asked instead.
|
|
8013
|
+
*/
|
|
8014
|
+
issuer: z10.string().url().optional(),
|
|
8015
|
+
/** Other issuer values this provider legitimately uses. */
|
|
8016
|
+
additionalIssuers: z10.array(z10.string().trim().min(1, "additionalIssuers entries must be non-empty")).optional()
|
|
7898
8017
|
}).refine((p) => !!(p.authorizationEndpoint ?? p.authorizeUrl), {
|
|
7899
8018
|
message: "authorizationEndpoint (or authorizeUrl) is required",
|
|
7900
8019
|
path: ["authorizationEndpoint"]
|
|
@@ -8303,17 +8422,21 @@ var sharedAuthFields = {
|
|
|
8303
8422
|
* Require the OAuth client to be KNOWN (registered via DCR / pre-registered,
|
|
8304
8423
|
* or a CIMD client-id URL) before an authorization request is accepted.
|
|
8305
8424
|
*
|
|
8306
|
-
* @default
|
|
8425
|
+
* @default true
|
|
8307
8426
|
*
|
|
8308
|
-
* SECURITY:
|
|
8309
|
-
*
|
|
8310
|
-
*
|
|
8311
|
-
* (auth-code interception
|
|
8312
|
-
* `
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8427
|
+
* SECURITY: an unregistered client id has no trusted `redirect_uris` to
|
|
8428
|
+
* validate against, so accepting it lets anyone start a sign-in whose
|
|
8429
|
+
* authorization code is delivered to a redirect_uri of their choosing
|
|
8430
|
+
* (auth-code interception). `false` is for local development only; a
|
|
8431
|
+
* `dcr.allowedRedirectUris` allowlist still constrains such clients.
|
|
8432
|
+
*/
|
|
8433
|
+
requireRegisteredClients: z14.boolean().default(true),
|
|
8434
|
+
/**
|
|
8435
|
+
* Scopes the server may grant (exact values or `*` globs). Requested scopes
|
|
8436
|
+
* outside this list are dropped from the grant. Unset → the standard OpenID
|
|
8437
|
+
* scopes only (see `DEFAULT_ALLOWED_SCOPES`).
|
|
8438
|
+
*/
|
|
8439
|
+
allowedScopes: z14.array(z14.string().trim().min(1, "allowedScopes entries must be non-empty")).optional(),
|
|
8317
8440
|
cimd: cimdConfigSchema.optional(),
|
|
8318
8441
|
// #469 — custom auth UI as a slot→file map + extras name→handler map (per-app
|
|
8319
8442
|
// under splitByApp). Both optional; omitting them serves the built-in pages.
|
|
@@ -8924,6 +9047,50 @@ var AudienceValidator = class _AudienceValidator {
|
|
|
8924
9047
|
}
|
|
8925
9048
|
};
|
|
8926
9049
|
|
|
9050
|
+
// libs/auth/src/utils/scope-grant.utils.ts
|
|
9051
|
+
var DEFAULT_ALLOWED_SCOPES = Object.freeze([
|
|
9052
|
+
"openid",
|
|
9053
|
+
"profile",
|
|
9054
|
+
"email",
|
|
9055
|
+
"offline_access"
|
|
9056
|
+
]);
|
|
9057
|
+
function grantScopes(requested, allowed) {
|
|
9058
|
+
const patterns = allowed ?? DEFAULT_ALLOWED_SCOPES;
|
|
9059
|
+
const granted = [];
|
|
9060
|
+
for (const scope of requested) {
|
|
9061
|
+
if (!scope || granted.includes(scope)) continue;
|
|
9062
|
+
if (patterns.some((pattern) => globMatch(pattern, scope))) granted.push(scope);
|
|
9063
|
+
}
|
|
9064
|
+
return granted;
|
|
9065
|
+
}
|
|
9066
|
+
function advertisedScopes(allowed) {
|
|
9067
|
+
const scopes = [];
|
|
9068
|
+
for (const scope of allowed ?? DEFAULT_ALLOWED_SCOPES) {
|
|
9069
|
+
if (scope && !scope.includes("*") && !scopes.includes(scope)) scopes.push(scope);
|
|
9070
|
+
}
|
|
9071
|
+
return scopes;
|
|
9072
|
+
}
|
|
9073
|
+
|
|
9074
|
+
// libs/auth/src/utils/redirect-uri.utils.ts
|
|
9075
|
+
function isLoopbackRedirectUri(uri) {
|
|
9076
|
+
let url;
|
|
9077
|
+
try {
|
|
9078
|
+
url = new URL(uri);
|
|
9079
|
+
} catch {
|
|
9080
|
+
return false;
|
|
9081
|
+
}
|
|
9082
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
9083
|
+
if (url.username || url.password) return false;
|
|
9084
|
+
const host = url.hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
9085
|
+
if (host === "localhost" || host === "::1") return true;
|
|
9086
|
+
const m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
|
|
9087
|
+
if (m) {
|
|
9088
|
+
const octets = m.slice(1).map((n) => Number(n));
|
|
9089
|
+
return octets.every((o) => o >= 0 && o <= 255) && octets[0] === 127;
|
|
9090
|
+
}
|
|
9091
|
+
return false;
|
|
9092
|
+
}
|
|
9093
|
+
|
|
8927
9094
|
// libs/auth/src/vault/auth-providers.types.ts
|
|
8928
9095
|
import { z as z19 } from "@frontmcp/lazy-zod";
|
|
8929
9096
|
var credentialScopeSchema = z19.enum(["global", "user", "session"]);
|
|
@@ -10489,6 +10656,211 @@ async function evaluateRebac(policy, ctx) {
|
|
|
10489
10656
|
return { granted: true, evaluatedPolicies: ["relationships"] };
|
|
10490
10657
|
}
|
|
10491
10658
|
|
|
10659
|
+
// libs/auth/src/authorities/authorities.validation.ts
|
|
10660
|
+
var ABAC_OPERATORS = /* @__PURE__ */ new Set([
|
|
10661
|
+
"eq",
|
|
10662
|
+
"neq",
|
|
10663
|
+
"in",
|
|
10664
|
+
"notIn",
|
|
10665
|
+
"gt",
|
|
10666
|
+
"gte",
|
|
10667
|
+
"lt",
|
|
10668
|
+
"lte",
|
|
10669
|
+
"contains",
|
|
10670
|
+
"startsWith",
|
|
10671
|
+
"endsWith",
|
|
10672
|
+
"exists",
|
|
10673
|
+
"matches"
|
|
10674
|
+
]);
|
|
10675
|
+
var CHECK_FIELDS = [
|
|
10676
|
+
"roles",
|
|
10677
|
+
"permissions",
|
|
10678
|
+
"attributes",
|
|
10679
|
+
"relationships",
|
|
10680
|
+
"custom",
|
|
10681
|
+
"guards",
|
|
10682
|
+
"allOf",
|
|
10683
|
+
"anyOf",
|
|
10684
|
+
"not"
|
|
10685
|
+
];
|
|
10686
|
+
var RULE_FIELDS = /* @__PURE__ */ new Set([...CHECK_FIELDS, "operator"]);
|
|
10687
|
+
function isPlainObject(value) {
|
|
10688
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10689
|
+
}
|
|
10690
|
+
function isNonEmptyString(value) {
|
|
10691
|
+
return typeof value === "string" && value.length > 0;
|
|
10692
|
+
}
|
|
10693
|
+
function unknownFields(value, known, path) {
|
|
10694
|
+
const prefix = path ? `${path} has` : "has";
|
|
10695
|
+
return Object.keys(value).filter((key) => !known.has(key)).map((key) => `${prefix} an unknown field "${key}"`);
|
|
10696
|
+
}
|
|
10697
|
+
function checkStringList(value, path) {
|
|
10698
|
+
if (!Array.isArray(value)) return [`${path} must be a list of names`];
|
|
10699
|
+
if (value.length === 0) return [`${path} is empty`];
|
|
10700
|
+
return value.every(isNonEmptyString) ? [] : [`${path} must contain only non-empty names`];
|
|
10701
|
+
}
|
|
10702
|
+
var ALL_OR_ANY = /* @__PURE__ */ new Set(["all", "any"]);
|
|
10703
|
+
function checkAllOrAny(value, path) {
|
|
10704
|
+
if (!isPlainObject(value)) return [`${path} must be an object with "all" or "any"`];
|
|
10705
|
+
const problems = unknownFields(value, ALL_OR_ANY, path);
|
|
10706
|
+
if (value["all"] === void 0 && value["any"] === void 0) {
|
|
10707
|
+
problems.push(`${path} needs "all" or "any"`);
|
|
10708
|
+
}
|
|
10709
|
+
if (value["all"] !== void 0) problems.push(...checkStringList(value["all"], `${path}.all`));
|
|
10710
|
+
if (value["any"] !== void 0) problems.push(...checkStringList(value["any"], `${path}.any`));
|
|
10711
|
+
return problems;
|
|
10712
|
+
}
|
|
10713
|
+
var ATTRIBUTE_FIELDS = /* @__PURE__ */ new Set(["match", "conditions"]);
|
|
10714
|
+
var CONDITION_FIELDS = /* @__PURE__ */ new Set(["path", "op", "value"]);
|
|
10715
|
+
function isValueReference(value) {
|
|
10716
|
+
return isPlainObject(value) && ("fromInput" in value || "fromClaims" in value);
|
|
10717
|
+
}
|
|
10718
|
+
function checkValueReference(value, path) {
|
|
10719
|
+
const ref = value["fromInput"] ?? value["fromClaims"];
|
|
10720
|
+
return Object.keys(value).length === 1 && isNonEmptyString(ref) ? [] : [`${path} must be { fromInput: "<name>" } or { fromClaims: "<path>" }`];
|
|
10721
|
+
}
|
|
10722
|
+
var OPERATOR_VALUES = {
|
|
10723
|
+
in: { accepts: Array.isArray, needs: "a list" },
|
|
10724
|
+
notIn: { accepts: Array.isArray, needs: "a list" },
|
|
10725
|
+
gt: { accepts: (value) => typeof value === "number", needs: "a number" },
|
|
10726
|
+
gte: { accepts: (value) => typeof value === "number", needs: "a number" },
|
|
10727
|
+
lt: { accepts: (value) => typeof value === "number", needs: "a number" },
|
|
10728
|
+
lte: { accepts: (value) => typeof value === "number", needs: "a number" },
|
|
10729
|
+
startsWith: { accepts: (value) => typeof value === "string", needs: "a string" },
|
|
10730
|
+
endsWith: { accepts: (value) => typeof value === "string", needs: "a string" },
|
|
10731
|
+
matches: { accepts: (value) => typeof value === "string", needs: "a string" }
|
|
10732
|
+
};
|
|
10733
|
+
function checkConditionValue(op, condition, path) {
|
|
10734
|
+
const value = condition["value"];
|
|
10735
|
+
if (value === void 0) return [`${path} is missing`];
|
|
10736
|
+
if (op === "exists") {
|
|
10737
|
+
return typeof value === "boolean" ? [] : [`${path} must be true or false for "exists"`];
|
|
10738
|
+
}
|
|
10739
|
+
if (isValueReference(value)) return checkValueReference(value, path);
|
|
10740
|
+
const expected = OPERATOR_VALUES[op];
|
|
10741
|
+
if (expected && !expected.accepts(value)) return [`${path} must be ${expected.needs} for "${op}"`];
|
|
10742
|
+
if (Array.isArray(value) && value.length === 0 && (op === "in" || op === "notIn")) return [`${path} is empty`];
|
|
10743
|
+
return [];
|
|
10744
|
+
}
|
|
10745
|
+
function checkAttributes(value, path) {
|
|
10746
|
+
if (!isPlainObject(value)) return [`${path} must be an object with "match" or "conditions"`];
|
|
10747
|
+
const problems = unknownFields(value, ATTRIBUTE_FIELDS, path);
|
|
10748
|
+
const { match, conditions } = value;
|
|
10749
|
+
if (match === void 0 && conditions === void 0) {
|
|
10750
|
+
problems.push(`${path} needs "match" or "conditions"`);
|
|
10751
|
+
}
|
|
10752
|
+
if (match !== void 0) {
|
|
10753
|
+
if (!isPlainObject(match)) problems.push(`${path}.match must be an object`);
|
|
10754
|
+
else if (Object.keys(match).length === 0) problems.push(`${path}.match is empty`);
|
|
10755
|
+
else {
|
|
10756
|
+
for (const [attribute, expected] of Object.entries(match)) {
|
|
10757
|
+
const at = `${path}.match[${JSON.stringify(attribute)}]`;
|
|
10758
|
+
if (expected === void 0) problems.push(`${at} has no value`);
|
|
10759
|
+
else if (isValueReference(expected)) problems.push(...checkValueReference(expected, at));
|
|
10760
|
+
}
|
|
10761
|
+
}
|
|
10762
|
+
}
|
|
10763
|
+
if (conditions !== void 0) {
|
|
10764
|
+
if (!Array.isArray(conditions)) problems.push(`${path}.conditions must be a list`);
|
|
10765
|
+
else if (conditions.length === 0) problems.push(`${path}.conditions is empty`);
|
|
10766
|
+
else {
|
|
10767
|
+
conditions.forEach((condition, index) => {
|
|
10768
|
+
const at = `${path}.conditions[${index}]`;
|
|
10769
|
+
if (!isPlainObject(condition)) {
|
|
10770
|
+
problems.push(`${at} must be an object with "path" and "op"`);
|
|
10771
|
+
return;
|
|
10772
|
+
}
|
|
10773
|
+
problems.push(...unknownFields(condition, CONDITION_FIELDS, at));
|
|
10774
|
+
if (!isNonEmptyString(condition["path"])) problems.push(`${at}.path must be a non-empty string`);
|
|
10775
|
+
const op = condition["op"];
|
|
10776
|
+
if (!ABAC_OPERATORS.has(op)) {
|
|
10777
|
+
problems.push(`${at}.op "${String(condition["op"])}" is not an operator`);
|
|
10778
|
+
} else {
|
|
10779
|
+
problems.push(...checkConditionValue(op, condition, `${at}.value`));
|
|
10780
|
+
}
|
|
10781
|
+
});
|
|
10782
|
+
}
|
|
10783
|
+
}
|
|
10784
|
+
return problems;
|
|
10785
|
+
}
|
|
10786
|
+
var RELATIONSHIP_FIELDS = /* @__PURE__ */ new Set(["type", "resource", "resourceId"]);
|
|
10787
|
+
function checkResourceId(value, path) {
|
|
10788
|
+
if (isNonEmptyString(value)) return [];
|
|
10789
|
+
if (isPlainObject(value) && Object.keys(value).length === 1) {
|
|
10790
|
+
const ref = value["fromInput"] ?? value["fromClaims"];
|
|
10791
|
+
if (isNonEmptyString(ref)) return [];
|
|
10792
|
+
}
|
|
10793
|
+
return [`${path} must be a string, { fromInput } or { fromClaims }`];
|
|
10794
|
+
}
|
|
10795
|
+
function checkRelationship(value, path) {
|
|
10796
|
+
if (!isPlainObject(value)) return [`${path} must be an object with "type", "resource" and "resourceId"`];
|
|
10797
|
+
const problems = unknownFields(value, RELATIONSHIP_FIELDS, path);
|
|
10798
|
+
if (!isNonEmptyString(value["type"])) problems.push(`${path}.type must be a non-empty string`);
|
|
10799
|
+
if (!isNonEmptyString(value["resource"])) problems.push(`${path}.resource must be a non-empty string`);
|
|
10800
|
+
problems.push(...checkResourceId(value["resourceId"], `${path}.resourceId`));
|
|
10801
|
+
return problems;
|
|
10802
|
+
}
|
|
10803
|
+
function checkRelationships(value, path) {
|
|
10804
|
+
if (!Array.isArray(value)) return checkRelationship(value, path);
|
|
10805
|
+
if (value.length === 0) return [`${path} is empty`];
|
|
10806
|
+
return value.flatMap((relationship, index) => checkRelationship(relationship, `${path}[${index}]`));
|
|
10807
|
+
}
|
|
10808
|
+
function checkRuleList(value, path) {
|
|
10809
|
+
if (!Array.isArray(value)) return [`${path} must be a list of rules`];
|
|
10810
|
+
if (value.length === 0) return [`${path} is empty`];
|
|
10811
|
+
return value.flatMap((rule, index) => checkRule(rule, `${path}[${index}]`));
|
|
10812
|
+
}
|
|
10813
|
+
function checkRule(rule, path) {
|
|
10814
|
+
if (!isPlainObject(rule)) {
|
|
10815
|
+
return [`${path || "the rule"} must be a rule object${typeof rule === "string" ? ", not a profile name" : ""}`];
|
|
10816
|
+
}
|
|
10817
|
+
const problems = unknownFields(rule, RULE_FIELDS, path);
|
|
10818
|
+
if (!CHECK_FIELDS.some((field) => rule[field] !== void 0)) {
|
|
10819
|
+
problems.push(`${path ? `${path} ` : ""}checks nothing`);
|
|
10820
|
+
}
|
|
10821
|
+
if (rule["operator"] !== void 0 && rule["operator"] !== "AND" && rule["operator"] !== "OR") {
|
|
10822
|
+
problems.push(`${path}.operator must be "AND" or "OR"`);
|
|
10823
|
+
}
|
|
10824
|
+
if (rule["roles"] !== void 0) problems.push(...checkAllOrAny(rule["roles"], `${path}.roles`));
|
|
10825
|
+
if (rule["permissions"] !== void 0) problems.push(...checkAllOrAny(rule["permissions"], `${path}.permissions`));
|
|
10826
|
+
if (rule["attributes"] !== void 0) problems.push(...checkAttributes(rule["attributes"], `${path}.attributes`));
|
|
10827
|
+
if (rule["relationships"] !== void 0) {
|
|
10828
|
+
problems.push(...checkRelationships(rule["relationships"], `${path}.relationships`));
|
|
10829
|
+
}
|
|
10830
|
+
if (rule["custom"] !== void 0) {
|
|
10831
|
+
if (!isPlainObject(rule["custom"])) problems.push(`${path}.custom must be an object`);
|
|
10832
|
+
else if (Object.keys(rule["custom"]).length === 0) problems.push(`${path}.custom is empty`);
|
|
10833
|
+
}
|
|
10834
|
+
if (rule["guards"] !== void 0) {
|
|
10835
|
+
const guards = rule["guards"];
|
|
10836
|
+
if (!Array.isArray(guards)) problems.push(`${path}.guards must be a list of functions`);
|
|
10837
|
+
else if (guards.length === 0) problems.push(`${path}.guards is empty`);
|
|
10838
|
+
else if (!guards.every((guard) => typeof guard === "function")) {
|
|
10839
|
+
problems.push(`${path}.guards must contain only functions`);
|
|
10840
|
+
}
|
|
10841
|
+
}
|
|
10842
|
+
if (rule["allOf"] !== void 0) problems.push(...checkRuleList(rule["allOf"], `${path}.allOf`));
|
|
10843
|
+
if (rule["anyOf"] !== void 0) problems.push(...checkRuleList(rule["anyOf"], `${path}.anyOf`));
|
|
10844
|
+
if (rule["not"] !== void 0) problems.push(...checkRule(rule["not"], `${path}.not`));
|
|
10845
|
+
return problems;
|
|
10846
|
+
}
|
|
10847
|
+
function findAuthoritiesRuleProblems(authorities) {
|
|
10848
|
+
if (typeof authorities === "string") {
|
|
10849
|
+
return authorities.length > 0 ? [] : ["the profile name is empty"];
|
|
10850
|
+
}
|
|
10851
|
+
if (Array.isArray(authorities)) {
|
|
10852
|
+
if (authorities.length === 0) return ["the list of profiles is empty"];
|
|
10853
|
+
return authorities.every(isNonEmptyString) ? [] : ["a list of profiles must contain only non-empty profile names"];
|
|
10854
|
+
}
|
|
10855
|
+
if (!isPlainObject(authorities)) {
|
|
10856
|
+
return ["must be a profile name, a list of profile names, or a rule object"];
|
|
10857
|
+
}
|
|
10858
|
+
return checkRule(authorities, "");
|
|
10859
|
+
}
|
|
10860
|
+
function findAuthoritiesProfileProblems(name, policy) {
|
|
10861
|
+
return checkRule(policy, "").map((problem) => `profile "${name}": ${problem.replace(/^\./, "")}`);
|
|
10862
|
+
}
|
|
10863
|
+
|
|
10492
10864
|
// libs/auth/src/authorities/authorities.engine.ts
|
|
10493
10865
|
function mergeResult(base, ...others) {
|
|
10494
10866
|
const evaluatedPolicies = [...base.evaluatedPolicies];
|
|
@@ -10497,8 +10869,12 @@ function mergeResult(base, ...others) {
|
|
|
10497
10869
|
}
|
|
10498
10870
|
return { ...base, evaluatedPolicies };
|
|
10499
10871
|
}
|
|
10500
|
-
function
|
|
10501
|
-
return {
|
|
10872
|
+
function invalidRule(problems, policies = []) {
|
|
10873
|
+
return {
|
|
10874
|
+
granted: false,
|
|
10875
|
+
deniedBy: `invalid authorities rule: ${problems.join("; ")}`,
|
|
10876
|
+
evaluatedPolicies: policies
|
|
10877
|
+
};
|
|
10502
10878
|
}
|
|
10503
10879
|
var AuthoritiesEngine = class {
|
|
10504
10880
|
constructor(profiles, evaluators) {
|
|
@@ -10507,10 +10883,36 @@ var AuthoritiesEngine = class {
|
|
|
10507
10883
|
}
|
|
10508
10884
|
profiles;
|
|
10509
10885
|
evaluators;
|
|
10886
|
+
/** Problems found per rule object, so a rule is validated once however often it is evaluated. */
|
|
10887
|
+
ruleProblems = /* @__PURE__ */ new WeakMap();
|
|
10888
|
+
/**
|
|
10889
|
+
* Problems with an `authorities` value declared on an entry (see {@link findAuthoritiesRuleProblems}),
|
|
10890
|
+
* and each profile name it uses that no registered profile has. The server reports them when it
|
|
10891
|
+
* starts; {@link evaluate} denies such a value (an unknown profile as `profile '…' is not registered`).
|
|
10892
|
+
*/
|
|
10893
|
+
findRuleProblems(authorities) {
|
|
10894
|
+
const problems = findAuthoritiesRuleProblems(authorities);
|
|
10895
|
+
if (problems.length > 0) return problems;
|
|
10896
|
+
const names = typeof authorities === "string" ? [authorities] : Array.isArray(authorities) ? authorities : [];
|
|
10897
|
+
return names.filter((name) => typeof name === "string" && !this.profiles.has(name)).map((name) => `names an unknown profile "${name}"`);
|
|
10898
|
+
}
|
|
10899
|
+
/**
|
|
10900
|
+
* Problems with every registered profile's rule (see {@link findAuthoritiesRuleProblems}).
|
|
10901
|
+
* The server reports them when it starts.
|
|
10902
|
+
*/
|
|
10903
|
+
findProfileProblems() {
|
|
10904
|
+
return Object.entries(this.profiles.getAll()).flatMap(
|
|
10905
|
+
([name, policy]) => findAuthoritiesProfileProblems(name, policy)
|
|
10906
|
+
);
|
|
10907
|
+
}
|
|
10510
10908
|
/**
|
|
10511
10909
|
* Evaluate an AuthoritiesMetadata value (string, string[], or policy object).
|
|
10910
|
+
*
|
|
10911
|
+
* A value that is malformed or checks nothing (see {@link findAuthoritiesRuleProblems}) is denied.
|
|
10512
10912
|
*/
|
|
10513
10913
|
async evaluate(authorities, ctx) {
|
|
10914
|
+
const problems = this.problemsOf(authorities, findAuthoritiesRuleProblems);
|
|
10915
|
+
if (problems.length > 0) return invalidRule(problems);
|
|
10514
10916
|
if (typeof authorities === "string") {
|
|
10515
10917
|
return this.evaluateProfile(authorities, ctx);
|
|
10516
10918
|
}
|
|
@@ -10531,6 +10933,8 @@ var AuthoritiesEngine = class {
|
|
|
10531
10933
|
evaluatedPolicies: [`profile:${name}`]
|
|
10532
10934
|
};
|
|
10533
10935
|
}
|
|
10936
|
+
const problems = this.problemsOf(policy, (rule) => findAuthoritiesProfileProblems(name, rule));
|
|
10937
|
+
if (problems.length > 0) return invalidRule(problems, [`profile:${name}`]);
|
|
10534
10938
|
const result = await this.evaluatePolicy(policy, ctx);
|
|
10535
10939
|
if (!result.granted) {
|
|
10536
10940
|
return {
|
|
@@ -10541,6 +10945,16 @@ var AuthoritiesEngine = class {
|
|
|
10541
10945
|
}
|
|
10542
10946
|
return mergeResult(result, { granted: true, evaluatedPolicies: [`profile:${name}`] });
|
|
10543
10947
|
}
|
|
10948
|
+
/** Validate a rule, reusing the result for a rule object seen before. */
|
|
10949
|
+
problemsOf(rule, find) {
|
|
10950
|
+
if (typeof rule !== "object" || rule === null) return find(rule);
|
|
10951
|
+
let problems = this.ruleProblems.get(rule);
|
|
10952
|
+
if (!problems) {
|
|
10953
|
+
problems = find(rule);
|
|
10954
|
+
this.ruleProblems.set(rule, problems);
|
|
10955
|
+
}
|
|
10956
|
+
return problems;
|
|
10957
|
+
}
|
|
10544
10958
|
/**
|
|
10545
10959
|
* Evaluate an array of profile names (AND semantics).
|
|
10546
10960
|
*/
|
|
@@ -10589,7 +11003,7 @@ var AuthoritiesEngine = class {
|
|
|
10589
11003
|
results.push(await this.evaluateNot(policy.not, ctx));
|
|
10590
11004
|
}
|
|
10591
11005
|
if (results.length === 0) {
|
|
10592
|
-
return
|
|
11006
|
+
return invalidRule(["checks nothing"]);
|
|
10593
11007
|
}
|
|
10594
11008
|
return this.combineResults(results, operator);
|
|
10595
11009
|
}
|
|
@@ -10640,14 +11054,15 @@ var AuthoritiesEngine = class {
|
|
|
10640
11054
|
}
|
|
10641
11055
|
/**
|
|
10642
11056
|
* Evaluate async guard functions in sequence.
|
|
10643
|
-
*
|
|
11057
|
+
* Only `true` grants. A string is the denial message; anything else (`false`, but also
|
|
11058
|
+
* `undefined` from a guard that forgot to return, `null`, `0` or an object) denies.
|
|
10644
11059
|
*/
|
|
10645
11060
|
async evaluateGuards(guards, ctx) {
|
|
10646
11061
|
for (let i = 0; i < guards.length; i++) {
|
|
10647
11062
|
const guard = guards[i];
|
|
10648
11063
|
const result = await guard(ctx);
|
|
10649
|
-
if (result
|
|
10650
|
-
const denialMessage = typeof result === "string" ? result : `guard[${i}] denied`;
|
|
11064
|
+
if (result !== true) {
|
|
11065
|
+
const denialMessage = typeof result === "string" ? result : result === false ? `guard[${i}] denied` : `guard[${i}] did not return true (returned ${result === null ? "null" : typeof result})`;
|
|
10651
11066
|
return {
|
|
10652
11067
|
granted: false,
|
|
10653
11068
|
deniedBy: `guards[${i}]: ${denialMessage}`,
|
|
@@ -10705,6 +11120,13 @@ var AuthoritiesEngine = class {
|
|
|
10705
11120
|
evaluatedPolicies: ["not", ...result.evaluatedPolicies]
|
|
10706
11121
|
};
|
|
10707
11122
|
}
|
|
11123
|
+
/**
|
|
11124
|
+
* The policy a profile name stands for when `evaluate()` is given that name, or `undefined`
|
|
11125
|
+
* when no such profile is registered. Lets callers inspect a named rule without evaluating it.
|
|
11126
|
+
*/
|
|
11127
|
+
resolveProfile(name) {
|
|
11128
|
+
return this.profiles.resolve(name);
|
|
11129
|
+
}
|
|
10708
11130
|
};
|
|
10709
11131
|
|
|
10710
11132
|
// libs/auth/src/authorities/authorities.registry.ts
|
|
@@ -10949,6 +11371,7 @@ export {
|
|
|
10949
11371
|
AuthoritiesProfileRegistry,
|
|
10950
11372
|
AuthorityDeniedError,
|
|
10951
11373
|
AuthorizationBase,
|
|
11374
|
+
BUILT_IN_AUTH_PAGE_CSP,
|
|
10952
11375
|
CDN,
|
|
10953
11376
|
CREDENTIALS_ACCESSOR,
|
|
10954
11377
|
CimdClientIdMismatchError,
|
|
@@ -10963,6 +11386,7 @@ export {
|
|
|
10963
11386
|
CredentialProviderAlreadyRegisteredError,
|
|
10964
11387
|
CredentialStorageError,
|
|
10965
11388
|
CredentialsAccessorImpl,
|
|
11389
|
+
DEFAULT_ALLOWED_SCOPES,
|
|
10966
11390
|
DEFAULT_INCREMENTAL_TICKET_TTL_MS,
|
|
10967
11391
|
DEFAULT_RESUME_TTL_MS,
|
|
10968
11392
|
DEFAULT_THEME,
|
|
@@ -11001,6 +11425,9 @@ export {
|
|
|
11001
11425
|
RedisAuthorizationStore,
|
|
11002
11426
|
RedisSessionStore,
|
|
11003
11427
|
SECURE_STORE_ACCESSOR,
|
|
11428
|
+
SIGNIN_BINDING_COOKIE_PREFIX,
|
|
11429
|
+
SIGNIN_BINDING_MAX_AGE_SECONDS,
|
|
11430
|
+
SIGNIN_BINDING_SECURE_COOKIE_PREFIX,
|
|
11004
11431
|
ScopeDeniedError,
|
|
11005
11432
|
SecureStoreAccessorImpl,
|
|
11006
11433
|
SessionCredentialVault,
|
|
@@ -11028,6 +11455,7 @@ export {
|
|
|
11028
11455
|
abacConditionSchema,
|
|
11029
11456
|
abacOperatorSchema,
|
|
11030
11457
|
abacPolicySchema,
|
|
11458
|
+
advertisedScopes,
|
|
11031
11459
|
aiPlatformTypeSchema,
|
|
11032
11460
|
allowsPublicAccess,
|
|
11033
11461
|
apiKeyCredentialSchema,
|
|
@@ -11076,6 +11504,7 @@ export {
|
|
|
11076
11504
|
buildToolConsentPage,
|
|
11077
11505
|
buildUnauthorizedHeader,
|
|
11078
11506
|
buildWwwAuthenticate,
|
|
11507
|
+
builtInAuthPageHeaders,
|
|
11079
11508
|
centeredCardLayout,
|
|
11080
11509
|
checkSsrfProtection,
|
|
11081
11510
|
cimdCacheConfigSchema,
|
|
@@ -11095,6 +11524,7 @@ export {
|
|
|
11095
11524
|
createLayout,
|
|
11096
11525
|
createSecureStore,
|
|
11097
11526
|
createSessionCredentialVault,
|
|
11527
|
+
createSigninBinding,
|
|
11098
11528
|
createTokenStorageAdapter,
|
|
11099
11529
|
credentialProviderConfigSchema,
|
|
11100
11530
|
credentialSchema,
|
|
@@ -11102,7 +11532,7 @@ export {
|
|
|
11102
11532
|
credentialTypeSchema,
|
|
11103
11533
|
customCredentialSchema,
|
|
11104
11534
|
decodeJwtPayloadSafe,
|
|
11105
|
-
|
|
11535
|
+
decryptAesGcm6 as decryptAesGcm,
|
|
11106
11536
|
decryptSessionJson,
|
|
11107
11537
|
decryptValue2 as decryptValue,
|
|
11108
11538
|
defaultSessionRateLimiter,
|
|
@@ -11112,7 +11542,7 @@ export {
|
|
|
11112
11542
|
deriveTypedUser,
|
|
11113
11543
|
detectAuthProviders,
|
|
11114
11544
|
detectedAuthProviderSchema,
|
|
11115
|
-
|
|
11545
|
+
encryptAesGcm6 as encryptAesGcm,
|
|
11116
11546
|
encryptJson,
|
|
11117
11547
|
encryptValue2 as encryptValue,
|
|
11118
11548
|
encryptedBlobSchema,
|
|
@@ -11127,6 +11557,8 @@ export {
|
|
|
11127
11557
|
federatedLoginStateSchema,
|
|
11128
11558
|
federatedProviderItemSchema,
|
|
11129
11559
|
federatedSelectionSchema,
|
|
11560
|
+
findAuthoritiesProfileProblems,
|
|
11561
|
+
findAuthoritiesRuleProblems,
|
|
11130
11562
|
flatRemoteProviderFields,
|
|
11131
11563
|
fromSessionRecord,
|
|
11132
11564
|
generateAuthorizationCode,
|
|
@@ -11138,15 +11570,17 @@ export {
|
|
|
11138
11570
|
getProviderApps,
|
|
11139
11571
|
getProviderScopes,
|
|
11140
11572
|
getTokenSignatureFingerprint,
|
|
11573
|
+
grantScopes,
|
|
11141
11574
|
hasOnlyLocalhostRedirectUris,
|
|
11142
11575
|
headerApplier,
|
|
11143
|
-
|
|
11576
|
+
hkdfSha2565 as hkdfSha256,
|
|
11144
11577
|
incrementalAuthConfigSchema,
|
|
11145
11578
|
isAnonymousSubject,
|
|
11146
11579
|
isCimdClientId,
|
|
11147
11580
|
isFrontMcpCredentials,
|
|
11148
11581
|
isJwt,
|
|
11149
11582
|
isLocalMode,
|
|
11583
|
+
isLoopbackRedirectUri,
|
|
11150
11584
|
isOrchestratedLocal,
|
|
11151
11585
|
isOrchestratedMode,
|
|
11152
11586
|
isOrchestratedRemote,
|
|
@@ -11176,6 +11610,7 @@ export {
|
|
|
11176
11610
|
noopLogger,
|
|
11177
11611
|
normalizeIssuer,
|
|
11178
11612
|
oauthCredentialSchema,
|
|
11613
|
+
openPendingLogin,
|
|
11179
11614
|
orchestratedLocalSchema,
|
|
11180
11615
|
orchestratedRemoteSchema,
|
|
11181
11616
|
parseAuthOptions,
|
|
@@ -11203,6 +11638,7 @@ export {
|
|
|
11203
11638
|
resolveRequiredScopes,
|
|
11204
11639
|
resourceIdRefSchema,
|
|
11205
11640
|
safeDecrypt,
|
|
11641
|
+
sealPendingLogin,
|
|
11206
11642
|
secureStoreConfigSchema,
|
|
11207
11643
|
secureStoreCustomBackendSchema,
|
|
11208
11644
|
secureStoreEncryptionSchema,
|
|
@@ -11213,6 +11649,8 @@ export {
|
|
|
11213
11649
|
signCredentialResumeToken,
|
|
11214
11650
|
signIncrementalAuthTicket,
|
|
11215
11651
|
signSession,
|
|
11652
|
+
signinBindingCookieName,
|
|
11653
|
+
signinBindingMatches,
|
|
11216
11654
|
skippedAppBehaviorSchema,
|
|
11217
11655
|
sseTransportStateSchema,
|
|
11218
11656
|
sshKeyCredentialSchema,
|
|
@@ -11245,5 +11683,6 @@ export {
|
|
|
11245
11683
|
verifyOrParseSession,
|
|
11246
11684
|
verifyPkce,
|
|
11247
11685
|
verifySession,
|
|
11248
|
-
wideLayout
|
|
11686
|
+
wideLayout,
|
|
11687
|
+
withoutSecretFieldValues
|
|
11249
11688
|
};
|