@frontmcp/auth 1.8.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/authorities/authorities.context.d.ts +16 -1
- package/authorities/authorities.context.d.ts.map +1 -1
- package/authorities/authorities.evaluator.d.ts +1 -1
- package/authorities/authorities.evaluator.d.ts.map +1 -1
- package/authorities/authorities.types.d.ts +2 -2
- package/authorities/authorities.types.d.ts.map +1 -1
- package/authorities/index.d.ts +1 -1
- package/authorities/index.d.ts.map +1 -1
- package/cimd/cimd.service.d.ts.map +1 -1
- package/cimd/cimd.validator.d.ts.map +1 -1
- package/context/frontmcp-auth-context.impl.d.ts.map +1 -1
- package/esm/index.mjs +74 -129
- package/esm/package.json +5 -5
- package/index.js +223 -278
- package/jwks/jwks.service.d.ts.map +1 -1
- package/package.json +5 -5
- package/session/authorization-vault.d.ts +1 -1
package/index.js
CHANGED
|
@@ -269,9 +269,9 @@ __export(index_exports, {
|
|
|
269
269
|
EagerCredentialLoader: () => EagerCredentialLoader,
|
|
270
270
|
ElicitationSecretRequiredError: () => ElicitationSecretRequiredError,
|
|
271
271
|
EncryptedRedisVault: () => EncryptedRedisVault,
|
|
272
|
-
EncryptedStorageError: () =>
|
|
272
|
+
EncryptedStorageError: () => import_utils24.EncryptedStorageError,
|
|
273
273
|
EncryptedStorageSecureStoreBackend: () => EncryptedStorageSecureStoreBackend,
|
|
274
|
-
EncryptedTypedStorage: () =>
|
|
274
|
+
EncryptedTypedStorage: () => import_utils24.EncryptedTypedStorage,
|
|
275
275
|
EncryptionContextNotSetError: () => EncryptionContextNotSetError,
|
|
276
276
|
EncryptionKeyNotConfiguredError: () => EncryptionKeyNotConfiguredError,
|
|
277
277
|
FetchCredentialMiddleware: () => FetchCredentialMiddleware,
|
|
@@ -319,7 +319,7 @@ __export(index_exports, {
|
|
|
319
319
|
TokenVault: () => TokenVault,
|
|
320
320
|
TransparentAuthorization: () => TransparentAuthorization,
|
|
321
321
|
TransportIdGenerator: () => TransportIdGenerator,
|
|
322
|
-
TypedStorage: () =>
|
|
322
|
+
TypedStorage: () => import_utils23.TypedStorage,
|
|
323
323
|
VaultEncryption: () => VaultEncryption,
|
|
324
324
|
VaultLoadError: () => VaultLoadError,
|
|
325
325
|
VaultNotFoundError: () => VaultNotFoundError,
|
|
@@ -401,9 +401,9 @@ __export(index_exports, {
|
|
|
401
401
|
credentialTypeSchema: () => credentialTypeSchema,
|
|
402
402
|
customCredentialSchema: () => customCredentialSchema,
|
|
403
403
|
decodeJwtPayloadSafe: () => decodeJwtPayloadSafe,
|
|
404
|
-
decryptAesGcm: () =>
|
|
404
|
+
decryptAesGcm: () => import_utils34.decryptAesGcm,
|
|
405
405
|
decryptSessionJson: () => decryptSessionJson,
|
|
406
|
-
decryptValue: () =>
|
|
406
|
+
decryptValue: () => import_utils34.decryptValue,
|
|
407
407
|
defaultSessionRateLimiter: () => defaultSessionRateLimiter,
|
|
408
408
|
deriveAuthorizationId: () => deriveAuthorizationId,
|
|
409
409
|
deriveExpectedAudience: () => deriveExpectedAudience,
|
|
@@ -411,13 +411,13 @@ __export(index_exports, {
|
|
|
411
411
|
deriveTypedUser: () => deriveTypedUser,
|
|
412
412
|
detectAuthProviders: () => detectAuthProviders,
|
|
413
413
|
detectedAuthProviderSchema: () => detectedAuthProviderSchema,
|
|
414
|
-
encryptAesGcm: () =>
|
|
414
|
+
encryptAesGcm: () => import_utils34.encryptAesGcm,
|
|
415
415
|
encryptJson: () => encryptJson,
|
|
416
|
-
encryptValue: () =>
|
|
416
|
+
encryptValue: () => import_utils34.encryptValue,
|
|
417
417
|
encryptedBlobSchema: () => encryptedBlobSchema,
|
|
418
418
|
encryptedDataSchema: () => encryptedDataSchema,
|
|
419
419
|
encryptedVaultEntrySchema: () => encryptedVaultEntrySchema,
|
|
420
|
-
escapeHtml: () =>
|
|
420
|
+
escapeHtml: () => import_utils4.escapeHtml,
|
|
421
421
|
extraWideLayout: () => extraWideLayout,
|
|
422
422
|
extractBearerToken: () => extractBearerToken,
|
|
423
423
|
extractCacheHeaders: () => extractCacheHeaders,
|
|
@@ -439,8 +439,9 @@ __export(index_exports, {
|
|
|
439
439
|
getTokenSignatureFingerprint: () => getTokenSignatureFingerprint,
|
|
440
440
|
hasOnlyLocalhostRedirectUris: () => hasOnlyLocalhostRedirectUris,
|
|
441
441
|
headerApplier: () => headerApplier,
|
|
442
|
-
hkdfSha256: () =>
|
|
442
|
+
hkdfSha256: () => import_utils34.hkdfSha256,
|
|
443
443
|
incrementalAuthConfigSchema: () => incrementalAuthConfigSchema,
|
|
444
|
+
isAnonymousSubject: () => isAnonymousSubject,
|
|
444
445
|
isCimdClientId: () => isCimdClientId,
|
|
445
446
|
isFrontMcpCredentials: () => isFrontMcpCredentials,
|
|
446
447
|
isJwt: () => isJwt,
|
|
@@ -453,7 +454,7 @@ __export(index_exports, {
|
|
|
453
454
|
isRedisTokenStorage: () => isRedisTokenStorage,
|
|
454
455
|
isRemoteMode: () => isRemoteMode,
|
|
455
456
|
isSessionComplete: () => isSessionComplete,
|
|
456
|
-
isSignedSession: () =>
|
|
457
|
+
isSignedSession: () => import_utils16.isSignedData,
|
|
457
458
|
isSoonExpiring: () => isSoonExpiring,
|
|
458
459
|
isSoonExpiringProvider: () => isSoonExpiringProvider,
|
|
459
460
|
isSqliteTokenStorage: () => isSqliteTokenStorage,
|
|
@@ -549,6 +550,9 @@ module.exports = __toCommonJS(index_exports);
|
|
|
549
550
|
|
|
550
551
|
// libs/auth/src/jwks/jwks.service.ts
|
|
551
552
|
var import_jose = require("jose");
|
|
553
|
+
var import_utils2 = require("@frontmcp/utils");
|
|
554
|
+
|
|
555
|
+
// libs/auth/src/cimd/cimd.validator.ts
|
|
552
556
|
var import_utils = require("@frontmcp/utils");
|
|
553
557
|
|
|
554
558
|
// libs/auth/src/cimd/cimd.errors.ts
|
|
@@ -814,122 +818,38 @@ function isIpAddress(hostname) {
|
|
|
814
818
|
}
|
|
815
819
|
return false;
|
|
816
820
|
}
|
|
821
|
+
var BLOCKED_RANGE_REASONS = /* @__PURE__ */ new Map([
|
|
822
|
+
["0.0.0.0/8", "Current network addresses (0.x.x.x) are not allowed"],
|
|
823
|
+
["10.0.0.0/8", "Private IP addresses (10.x.x.x) are not allowed"],
|
|
824
|
+
["100.64.0.0/10", "Carrier-grade NAT addresses (100.64-127.x.x) are not allowed"],
|
|
825
|
+
["127.0.0.0/8", "Loopback addresses (127.x.x.x) are not allowed"],
|
|
826
|
+
["169.254.0.0/16", "Link-local addresses (169.254.x.x) are not allowed"],
|
|
827
|
+
["172.16.0.0/12", "Private IP addresses (172.16-31.x.x) are not allowed"],
|
|
828
|
+
["192.0.0.0/24", "IETF protocol-assignment addresses (192.0.0.0/24) are not allowed"],
|
|
829
|
+
["192.168.0.0/16", "Private IP addresses (192.168.x.x) are not allowed"],
|
|
830
|
+
["224.0.0.0/4", "Multicast addresses are not allowed"],
|
|
831
|
+
["255.255.255.255/32", "Broadcast address is not allowed"],
|
|
832
|
+
["::/128", "IPv6 unspecified address (::) is not allowed"],
|
|
833
|
+
["::1/128", "IPv6 loopback address (::1) is not allowed"],
|
|
834
|
+
["fc00::/7", "IPv6 unique local addresses (fc00::/7) are not allowed"],
|
|
835
|
+
["fe80::/10", "IPv6 link-local addresses (fe80::/10) are not allowed"]
|
|
836
|
+
]);
|
|
817
837
|
function checkIpAddress(ip) {
|
|
818
|
-
const
|
|
819
|
-
if (
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
const parts = ip.split(".").map(Number);
|
|
826
|
-
if (parts.length !== 4 || parts.some((p) => isNaN(p) || p < 0 || p > 255)) {
|
|
827
|
-
return { allowed: false, reason: "Invalid IPv4 address" };
|
|
828
|
-
}
|
|
829
|
-
const [a, b, c, d] = parts;
|
|
830
|
-
if (a === 127) {
|
|
831
|
-
return { allowed: false, reason: "Loopback addresses (127.x.x.x) are not allowed" };
|
|
832
|
-
}
|
|
833
|
-
if (a === 10) {
|
|
834
|
-
return { allowed: false, reason: "Private IP addresses (10.x.x.x) are not allowed" };
|
|
835
|
-
}
|
|
836
|
-
if (a === 172 && b >= 16 && b <= 31) {
|
|
837
|
-
return { allowed: false, reason: "Private IP addresses (172.16-31.x.x) are not allowed" };
|
|
838
|
-
}
|
|
839
|
-
if (a === 192 && b === 168) {
|
|
840
|
-
return { allowed: false, reason: "Private IP addresses (192.168.x.x) are not allowed" };
|
|
841
|
-
}
|
|
842
|
-
if (a === 169 && b === 254) {
|
|
843
|
-
return { allowed: false, reason: "Link-local addresses (169.254.x.x) are not allowed" };
|
|
844
|
-
}
|
|
845
|
-
if (a === 100 && b >= 64 && b <= 127) {
|
|
846
|
-
return { allowed: false, reason: "Carrier-grade NAT addresses (100.64-127.x.x) are not allowed" };
|
|
847
|
-
}
|
|
848
|
-
if (a === 192 && b === 0 && c === 0) {
|
|
849
|
-
return { allowed: false, reason: "IETF protocol-assignment addresses (192.0.0.0/24) are not allowed" };
|
|
850
|
-
}
|
|
851
|
-
if (a === 0) {
|
|
852
|
-
return { allowed: false, reason: "Current network addresses (0.x.x.x) are not allowed" };
|
|
853
|
-
}
|
|
854
|
-
if (a === 255 && b === 255 && c === 255 && d === 255) {
|
|
855
|
-
return { allowed: false, reason: "Broadcast address is not allowed" };
|
|
856
|
-
}
|
|
857
|
-
if (a >= 224 && a <= 239) {
|
|
858
|
-
return { allowed: false, reason: "Multicast addresses are not allowed" };
|
|
859
|
-
}
|
|
860
|
-
return { allowed: true, reason: "" };
|
|
861
|
-
}
|
|
862
|
-
function expandIpv6(ip) {
|
|
863
|
-
let text = ip.toLowerCase();
|
|
864
|
-
if (text.includes("%")) {
|
|
865
|
-
text = text.slice(0, text.indexOf("%"));
|
|
866
|
-
}
|
|
867
|
-
const dottedQuad = text.match(/^(.*:)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/);
|
|
868
|
-
if (dottedQuad) {
|
|
869
|
-
const octets = dottedQuad[2].split(".").map(Number);
|
|
870
|
-
if (octets.some((octet) => !Number.isInteger(octet) || octet < 0 || octet > 255)) return null;
|
|
871
|
-
const high = (octets[0] << 8 | octets[1]).toString(16);
|
|
872
|
-
const low = (octets[2] << 8 | octets[3]).toString(16);
|
|
873
|
-
text = `${dottedQuad[1]}${high}:${low}`;
|
|
874
|
-
}
|
|
875
|
-
const halves = text.split("::");
|
|
876
|
-
if (halves.length > 2) return null;
|
|
877
|
-
const parseGroups = (part) => {
|
|
878
|
-
if (part === "") return [];
|
|
879
|
-
const groups = [];
|
|
880
|
-
for (const group of part.split(":")) {
|
|
881
|
-
if (!/^[0-9a-f]{1,4}$/.test(group)) return null;
|
|
882
|
-
groups.push(parseInt(group, 16));
|
|
883
|
-
}
|
|
884
|
-
return groups;
|
|
885
|
-
};
|
|
886
|
-
const head = parseGroups(halves[0]);
|
|
887
|
-
if (head === null) return null;
|
|
888
|
-
if (halves.length === 1) {
|
|
889
|
-
return head.length === 8 ? head : null;
|
|
890
|
-
}
|
|
891
|
-
const tail = parseGroups(halves[1]);
|
|
892
|
-
if (tail === null) return null;
|
|
893
|
-
if (head.length + tail.length > 7) return null;
|
|
894
|
-
return [...head, ...new Array(8 - head.length - tail.length).fill(0), ...tail];
|
|
895
|
-
}
|
|
896
|
-
function checkIpv6(ip) {
|
|
897
|
-
const groups = expandIpv6(ip);
|
|
898
|
-
if (!groups) {
|
|
899
|
-
return { allowed: false, reason: `Malformed IPv6 address: ${ip}` };
|
|
900
|
-
}
|
|
901
|
-
const isZeroPrefix = groups.slice(0, 7).every((group) => group === 0);
|
|
902
|
-
if (isZeroPrefix && groups[7] === 1) {
|
|
903
|
-
return { allowed: false, reason: "IPv6 loopback address (::1) is not allowed" };
|
|
904
|
-
}
|
|
905
|
-
if (isZeroPrefix && groups[7] === 0) {
|
|
906
|
-
return { allowed: false, reason: "IPv6 unspecified address (::) is not allowed" };
|
|
907
|
-
}
|
|
908
|
-
if ((groups[0] & 65472) === 65152) {
|
|
909
|
-
return { allowed: false, reason: "IPv6 link-local addresses (fe80::/10) are not allowed" };
|
|
910
|
-
}
|
|
911
|
-
if ((groups[0] & 65024) === 64512) {
|
|
912
|
-
return { allowed: false, reason: "IPv6 unique local addresses (fc00::/7) are not allowed" };
|
|
838
|
+
const classification = (0, import_utils.classifyIpAddress)(ip);
|
|
839
|
+
if (!classification) {
|
|
840
|
+
const bareIp = ip.replace(/^\[|\]$/g, "");
|
|
841
|
+
return {
|
|
842
|
+
allowed: false,
|
|
843
|
+
reason: bareIp.includes(":") ? `Malformed IPv6 address: ${bareIp}` : "Invalid IPv4 address"
|
|
844
|
+
};
|
|
913
845
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
return checkIpv4(embedded);
|
|
846
|
+
if (classification.range === "public") {
|
|
847
|
+
return { allowed: true, reason: "" };
|
|
917
848
|
}
|
|
918
|
-
return {
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
const toDotted = () => {
|
|
922
|
-
const high = groups[6];
|
|
923
|
-
const low = groups[7];
|
|
924
|
-
return `${high >> 8}.${high & 255}.${low >> 8}.${low & 255}`;
|
|
849
|
+
return {
|
|
850
|
+
allowed: false,
|
|
851
|
+
reason: BLOCKED_RANGE_REASONS.get(classification.cidr) ?? `Addresses in the ${classification.range} range (${classification.cidr}) are not allowed`
|
|
925
852
|
};
|
|
926
|
-
const firstFive = groups.slice(0, 5).every((group) => group === 0);
|
|
927
|
-
if (firstFive && groups[5] === 65535) return toDotted();
|
|
928
|
-
if (firstFive && groups[5] === 0) return toDotted();
|
|
929
|
-
if (groups[0] === 100 && groups[1] === 65435 && groups.slice(2, 6).every((group) => group === 0)) {
|
|
930
|
-
return toDotted();
|
|
931
|
-
}
|
|
932
|
-
return null;
|
|
933
853
|
}
|
|
934
854
|
function isDomainInList(hostname, domainList) {
|
|
935
855
|
const lowerHostname = hostname.toLowerCase();
|
|
@@ -1060,9 +980,9 @@ var JwksService = class {
|
|
|
1060
980
|
* Returns null if persistence is disabled (production).
|
|
1061
981
|
*/
|
|
1062
982
|
async getKeyPersistence() {
|
|
1063
|
-
if ((0,
|
|
983
|
+
if ((0, import_utils2.isProduction)()) return null;
|
|
1064
984
|
if (!this.keyPersistence) {
|
|
1065
|
-
this.keyPersistence = await (0,
|
|
985
|
+
this.keyPersistence = await (0, import_utils2.createKeyPersistence)({
|
|
1066
986
|
baseDir: ".frontmcp/keys"
|
|
1067
987
|
});
|
|
1068
988
|
}
|
|
@@ -1181,7 +1101,7 @@ var JwksService = class {
|
|
|
1181
1101
|
if (!jwtAlg) {
|
|
1182
1102
|
return { ok: false, error: "missing_alg" };
|
|
1183
1103
|
}
|
|
1184
|
-
const isValid = await (0,
|
|
1104
|
+
const isValid = await (0, import_utils2.rsaVerify)(
|
|
1185
1105
|
jwtAlg,
|
|
1186
1106
|
Buffer.from(signatureInput),
|
|
1187
1107
|
matchingKey,
|
|
@@ -1324,10 +1244,10 @@ var JwksService = class {
|
|
|
1324
1244
|
const parsed = new URL(currentUrl);
|
|
1325
1245
|
const host = parsed.hostname.toLowerCase();
|
|
1326
1246
|
const isLocalhost = host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]" || host.endsWith(".localhost");
|
|
1327
|
-
if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && (isLocalhost || !(0,
|
|
1247
|
+
if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && (isLocalhost || !(0, import_utils2.isProduction)()))) {
|
|
1328
1248
|
throw new Error(`insecure scheme "${parsed.protocol}"`);
|
|
1329
1249
|
}
|
|
1330
|
-
if (!(isLocalhost && !(0,
|
|
1250
|
+
if (!(isLocalhost && !(0, import_utils2.isProduction)())) {
|
|
1331
1251
|
const ssrf = await resolveAndCheckHostname(parsed.hostname);
|
|
1332
1252
|
if (!ssrf.allowed) throw new Error(`blocked host: ${ssrf.reason}`);
|
|
1333
1253
|
}
|
|
@@ -1338,11 +1258,10 @@ var JwksService = class {
|
|
|
1338
1258
|
// Follow redirects manually so each hop is re-validated (scheme + SSRF).
|
|
1339
1259
|
redirect: "manual"
|
|
1340
1260
|
});
|
|
1341
|
-
if (
|
|
1261
|
+
if ((0, import_utils2.isRedirectResponse)(res)) {
|
|
1342
1262
|
const location = res.headers.get("location");
|
|
1343
|
-
if (!location
|
|
1344
|
-
|
|
1345
|
-
}
|
|
1263
|
+
if (!location) throw new Error("redirect without a readable Location header");
|
|
1264
|
+
if (hop >= MAX_JWKS_FETCH_HOPS) throw new Error("too many redirects");
|
|
1346
1265
|
currentUrl = new URL(location, currentUrl).toString();
|
|
1347
1266
|
continue;
|
|
1348
1267
|
}
|
|
@@ -1435,13 +1354,13 @@ var JwksService = class {
|
|
|
1435
1354
|
const { generateKeyPairSync } = require("node:crypto");
|
|
1436
1355
|
if (alg === "RS256") {
|
|
1437
1356
|
const { privateKey, publicKey } = generateKeyPairSync("rsa", { modulusLength: 2048 });
|
|
1438
|
-
const kid = (0,
|
|
1357
|
+
const kid = (0, import_utils2.bytesToHex)((0, import_utils2.randomBytes)(8));
|
|
1439
1358
|
const publicJwk = publicKey.export({ format: "jwk" });
|
|
1440
1359
|
Object.assign(publicJwk, { kid, alg: "RS256", use: "sig", kty: "RSA" });
|
|
1441
1360
|
return { kid, privateKey, publicJwk: { keys: [publicJwk] }, createdAt: Date.now() };
|
|
1442
1361
|
} else {
|
|
1443
1362
|
const { privateKey, publicKey } = generateKeyPairSync("ec", { namedCurve: "P-256" });
|
|
1444
|
-
const kid = (0,
|
|
1363
|
+
const kid = (0, import_utils2.bytesToHex)((0, import_utils2.randomBytes)(8));
|
|
1445
1364
|
const publicJwk = publicKey.export({ format: "jwk" });
|
|
1446
1365
|
Object.assign(publicJwk, { kid, alg: "ES256", use: "sig", kty: "EC" });
|
|
1447
1366
|
return { kid, privateKey, publicJwk: { keys: [publicJwk] }, createdAt: Date.now() };
|
|
@@ -1450,8 +1369,8 @@ var JwksService = class {
|
|
|
1450
1369
|
};
|
|
1451
1370
|
|
|
1452
1371
|
// libs/auth/src/ui/base-layout.ts
|
|
1453
|
-
var import_utils2 = require("@frontmcp/utils");
|
|
1454
1372
|
var import_utils3 = require("@frontmcp/utils");
|
|
1373
|
+
var import_utils4 = require("@frontmcp/utils");
|
|
1455
1374
|
var CDN = {
|
|
1456
1375
|
/** Tailwind CSS v4 Browser CDN - generates styles on-the-fly with @theme support */
|
|
1457
1376
|
tailwind: "https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4",
|
|
@@ -1530,13 +1449,13 @@ function baseLayout(content, options) {
|
|
|
1530
1449
|
}
|
|
1531
1450
|
${customCss}
|
|
1532
1451
|
</style>` : "";
|
|
1533
|
-
const metaDescription = description ? `<meta name="description" content="${(0,
|
|
1452
|
+
const metaDescription = description ? `<meta name="description" content="${(0, import_utils3.escapeHtml)(description)}">` : "";
|
|
1534
1453
|
return `<!DOCTYPE html>
|
|
1535
1454
|
<html lang="en">
|
|
1536
1455
|
<head>
|
|
1537
1456
|
<meta charset="UTF-8">
|
|
1538
1457
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1539
|
-
<title>${(0,
|
|
1458
|
+
<title>${(0, import_utils3.escapeHtml)(title)} - FrontMCP</title>
|
|
1540
1459
|
${metaDescription}
|
|
1541
1460
|
|
|
1542
1461
|
<!-- Google Fonts CDN - Inter (modern UI font) -->
|
|
@@ -1547,7 +1466,7 @@ function baseLayout(content, options) {
|
|
|
1547
1466
|
${tailwindBlock}
|
|
1548
1467
|
${headExtra}
|
|
1549
1468
|
</head>
|
|
1550
|
-
<body class="${(0,
|
|
1469
|
+
<body class="${(0, import_utils3.escapeHtml)(bodyClass)}">
|
|
1551
1470
|
${content}
|
|
1552
1471
|
</body>
|
|
1553
1472
|
</html>`;
|
|
@@ -1602,7 +1521,7 @@ function extraWideLayout(content, options) {
|
|
|
1602
1521
|
}
|
|
1603
1522
|
|
|
1604
1523
|
// libs/auth/src/ui/templates.ts
|
|
1605
|
-
var escapeHtml3 =
|
|
1524
|
+
var escapeHtml3 = import_utils4.escapeHtml;
|
|
1606
1525
|
function buildConsentPage(params) {
|
|
1607
1526
|
const { apps, clientName, pendingAuthId, csrfToken, callbackPath } = params;
|
|
1608
1527
|
const appCards = apps.map((app) => buildAppCardHtml(app, pendingAuthId, csrfToken, callbackPath)).join("\n");
|
|
@@ -2104,7 +2023,7 @@ function renderLocalLoginPage(login, ctx, values) {
|
|
|
2104
2023
|
|
|
2105
2024
|
// libs/auth/src/session/authorization.store.ts
|
|
2106
2025
|
var import_lazy_zod = require("@frontmcp/lazy-zod");
|
|
2107
|
-
var
|
|
2026
|
+
var import_utils5 = require("@frontmcp/utils");
|
|
2108
2027
|
var pkceChallengeSchema = import_lazy_zod.z.object({
|
|
2109
2028
|
challenge: import_lazy_zod.z.string().min(43).max(128),
|
|
2110
2029
|
method: import_lazy_zod.z.literal("S256")
|
|
@@ -2139,10 +2058,10 @@ var AUTH_CODE_TTL_MS = 60 * 1e3;
|
|
|
2139
2058
|
var PENDING_AUTH_TTL_MS = 10 * 60 * 1e3;
|
|
2140
2059
|
var REFRESH_TOKEN_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
2141
2060
|
function generateAuthorizationCode() {
|
|
2142
|
-
return (0,
|
|
2061
|
+
return (0, import_utils5.randomUUID)().replace(/-/g, "") + (0, import_utils5.randomUUID)().replace(/-/g, "");
|
|
2143
2062
|
}
|
|
2144
2063
|
function generateRefreshTokenValue() {
|
|
2145
|
-
return (0,
|
|
2064
|
+
return (0, import_utils5.randomUUID)() + "-" + (0, import_utils5.randomUUID)();
|
|
2146
2065
|
}
|
|
2147
2066
|
function buildCodeRecord(params) {
|
|
2148
2067
|
const now = Date.now();
|
|
@@ -2173,7 +2092,7 @@ function buildCodeRecord(params) {
|
|
|
2173
2092
|
function buildPendingRecord(params) {
|
|
2174
2093
|
const now = Date.now();
|
|
2175
2094
|
return {
|
|
2176
|
-
id: (0,
|
|
2095
|
+
id: (0, import_utils5.randomUUID)(),
|
|
2177
2096
|
clientId: params.clientId,
|
|
2178
2097
|
redirectUri: params.redirectUri,
|
|
2179
2098
|
scopes: params.scopes,
|
|
@@ -2222,11 +2141,11 @@ function verifyPkce(codeVerifier, challenge) {
|
|
|
2222
2141
|
if (challenge.method !== "S256") {
|
|
2223
2142
|
return false;
|
|
2224
2143
|
}
|
|
2225
|
-
const hash = (0,
|
|
2144
|
+
const hash = (0, import_utils5.sha256Base64url)(codeVerifier);
|
|
2226
2145
|
return hash === challenge.challenge;
|
|
2227
2146
|
}
|
|
2228
2147
|
function generatePkceChallenge(codeVerifier) {
|
|
2229
|
-
const challenge = (0,
|
|
2148
|
+
const challenge = (0, import_utils5.sha256Base64url)(codeVerifier);
|
|
2230
2149
|
return { challenge, method: "S256" };
|
|
2231
2150
|
}
|
|
2232
2151
|
var InMemoryAuthorizationStore = class {
|
|
@@ -2422,7 +2341,7 @@ var RedisAuthorizationStore = class {
|
|
|
2422
2341
|
};
|
|
2423
2342
|
|
|
2424
2343
|
// libs/auth/src/session/storage-authorization.store.ts
|
|
2425
|
-
var
|
|
2344
|
+
var import_utils6 = require("@frontmcp/utils");
|
|
2426
2345
|
var StorageAuthorizationStore = class {
|
|
2427
2346
|
codeStore;
|
|
2428
2347
|
pendingStore;
|
|
@@ -2433,9 +2352,9 @@ var StorageAuthorizationStore = class {
|
|
|
2433
2352
|
this.namespace = options.namespace ?? "oauth";
|
|
2434
2353
|
this.storageIsNamespaced = this.isNamespacedStorage(storage);
|
|
2435
2354
|
const base = this.storageIsNamespaced ? storage.namespace(this.namespace) : storage;
|
|
2436
|
-
this.codeStore = new
|
|
2437
|
-
this.pendingStore = new
|
|
2438
|
-
this.refreshStore = new
|
|
2355
|
+
this.codeStore = new import_utils6.TypedStorage(base);
|
|
2356
|
+
this.pendingStore = new import_utils6.TypedStorage(base);
|
|
2357
|
+
this.refreshStore = new import_utils6.TypedStorage(base);
|
|
2439
2358
|
}
|
|
2440
2359
|
// ============================================
|
|
2441
2360
|
// Record builders (pure; shared with in-memory store)
|
|
@@ -2808,11 +2727,11 @@ var authorizationVaultEntrySchema = import_lazy_zod2.z.object({
|
|
|
2808
2727
|
});
|
|
2809
2728
|
|
|
2810
2729
|
// libs/auth/src/session/session-credential-vault.ts
|
|
2811
|
-
var
|
|
2730
|
+
var import_utils8 = require("@frontmcp/utils");
|
|
2812
2731
|
|
|
2813
2732
|
// libs/auth/src/session/vault-encryption.ts
|
|
2814
2733
|
var import_lazy_zod3 = require("@frontmcp/lazy-zod");
|
|
2815
|
-
var
|
|
2734
|
+
var import_utils7 = require("@frontmcp/utils");
|
|
2816
2735
|
var encryptedDataSchema = import_lazy_zod3.z.object({
|
|
2817
2736
|
/** Version for future algorithm changes */
|
|
2818
2737
|
v: import_lazy_zod3.z.literal(1),
|
|
@@ -2852,7 +2771,7 @@ var VaultEncryption = class {
|
|
|
2852
2771
|
];
|
|
2853
2772
|
const ikm = new TextEncoder().encode(ikmParts.join(""));
|
|
2854
2773
|
const infoBytes = new TextEncoder().encode(this.hkdfInfo);
|
|
2855
|
-
const key = await (0,
|
|
2774
|
+
const key = await (0, import_utils7.hkdfSha256)(ikm, new Uint8Array(0), infoBytes, 32);
|
|
2856
2775
|
return key;
|
|
2857
2776
|
}
|
|
2858
2777
|
/**
|
|
@@ -2879,7 +2798,7 @@ var VaultEncryption = class {
|
|
|
2879
2798
|
];
|
|
2880
2799
|
const ikm = new TextEncoder().encode(ikmParts.join(""));
|
|
2881
2800
|
const infoBytes = new TextEncoder().encode(this.hkdfInfo);
|
|
2882
|
-
const key = await (0,
|
|
2801
|
+
const key = await (0, import_utils7.hkdfSha256)(ikm, new Uint8Array(0), infoBytes, 32);
|
|
2883
2802
|
return key;
|
|
2884
2803
|
}
|
|
2885
2804
|
/**
|
|
@@ -2893,14 +2812,14 @@ var VaultEncryption = class {
|
|
|
2893
2812
|
if (key.length !== 32) {
|
|
2894
2813
|
throw new Error("Encryption key must be 32 bytes");
|
|
2895
2814
|
}
|
|
2896
|
-
const iv = (0,
|
|
2897
|
-
const { ciphertext, tag } = await (0,
|
|
2815
|
+
const iv = (0, import_utils7.randomBytes)(12);
|
|
2816
|
+
const { ciphertext, tag } = await (0, import_utils7.encryptAesGcm)(key, new TextEncoder().encode(plaintext), iv);
|
|
2898
2817
|
return {
|
|
2899
2818
|
v: 1,
|
|
2900
2819
|
alg: "aes-256-gcm",
|
|
2901
|
-
iv: (0,
|
|
2902
|
-
ct: (0,
|
|
2903
|
-
tag: (0,
|
|
2820
|
+
iv: (0, import_utils7.base64urlEncode)(iv),
|
|
2821
|
+
ct: (0, import_utils7.base64urlEncode)(ciphertext),
|
|
2822
|
+
tag: (0, import_utils7.base64urlEncode)(tag)
|
|
2904
2823
|
};
|
|
2905
2824
|
}
|
|
2906
2825
|
/**
|
|
@@ -2920,11 +2839,11 @@ var VaultEncryption = class {
|
|
|
2920
2839
|
throw new Error("Invalid encrypted data format");
|
|
2921
2840
|
}
|
|
2922
2841
|
const { iv, ct, tag } = parsed.data;
|
|
2923
|
-
const ivBuffer = (0,
|
|
2924
|
-
const ciphertext = (0,
|
|
2925
|
-
const tagBuffer = (0,
|
|
2842
|
+
const ivBuffer = (0, import_utils7.base64urlDecode)(iv);
|
|
2843
|
+
const ciphertext = (0, import_utils7.base64urlDecode)(ct);
|
|
2844
|
+
const tagBuffer = (0, import_utils7.base64urlDecode)(tag);
|
|
2926
2845
|
try {
|
|
2927
|
-
const plaintext = await (0,
|
|
2846
|
+
const plaintext = await (0, import_utils7.decryptAesGcm)(key, ciphertext, ivBuffer, tagBuffer);
|
|
2928
2847
|
return new TextDecoder().decode(plaintext);
|
|
2929
2848
|
} catch (_error) {
|
|
2930
2849
|
throw new Error("Decryption failed: invalid key or corrupted data");
|
|
@@ -3064,7 +2983,7 @@ var SessionCredentialVault = class {
|
|
|
3064
2983
|
* it expires via TTL when configured.
|
|
3065
2984
|
*/
|
|
3066
2985
|
async rotateVault(sub) {
|
|
3067
|
-
const vaultId = (0,
|
|
2986
|
+
const vaultId = (0, import_utils8.randomUUID)();
|
|
3068
2987
|
await this.storage.set(this.indexKey(sub), vaultId, this.credentialSetOpts);
|
|
3069
2988
|
return vaultId;
|
|
3070
2989
|
}
|
|
@@ -3213,19 +3132,19 @@ var SessionCredentialVault = class {
|
|
|
3213
3132
|
}
|
|
3214
3133
|
};
|
|
3215
3134
|
function resolveVaultPepper(logger) {
|
|
3216
|
-
const fromEnv = (0,
|
|
3135
|
+
const fromEnv = (0, import_utils8.getEnv)("VAULT_SECRET") ?? (0, import_utils8.getEnv)("JWT_SECRET");
|
|
3217
3136
|
if (fromEnv && fromEnv.trim().length > 0) return fromEnv;
|
|
3218
3137
|
logger.warn(
|
|
3219
3138
|
"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."
|
|
3220
3139
|
);
|
|
3221
|
-
return (0,
|
|
3140
|
+
return (0, import_utils8.randomUUID)() + (0, import_utils8.randomUUID)();
|
|
3222
3141
|
}
|
|
3223
3142
|
function errMsg(err) {
|
|
3224
3143
|
return err instanceof Error ? err.message : String(err);
|
|
3225
3144
|
}
|
|
3226
3145
|
|
|
3227
3146
|
// libs/auth/src/session/token-storage.factory.ts
|
|
3228
|
-
var
|
|
3147
|
+
var import_utils9 = require("@frontmcp/utils");
|
|
3229
3148
|
function isRedisTokenStorage(config) {
|
|
3230
3149
|
return typeof config === "object" && config !== null && "redis" in config && !!config.redis;
|
|
3231
3150
|
}
|
|
@@ -3237,7 +3156,7 @@ function isPersistentTokenStorage(config) {
|
|
|
3237
3156
|
}
|
|
3238
3157
|
async function createTokenStorageAdapter(config) {
|
|
3239
3158
|
if (config === void 0 || config === "memory") {
|
|
3240
|
-
const adapter = new
|
|
3159
|
+
const adapter = new import_utils9.MemoryStorageAdapter();
|
|
3241
3160
|
await adapter.connect();
|
|
3242
3161
|
return adapter;
|
|
3243
3162
|
}
|
|
@@ -3254,14 +3173,14 @@ async function createTokenStorageAdapter(config) {
|
|
|
3254
3173
|
}
|
|
3255
3174
|
if (isRedisTokenStorage(config)) {
|
|
3256
3175
|
const { host, port, password, db, tls, keyPrefix } = config.redis;
|
|
3257
|
-
const adapter = new
|
|
3176
|
+
const adapter = new import_utils9.RedisStorageAdapter({
|
|
3258
3177
|
config: { host, port, password, db, tls },
|
|
3259
3178
|
keyPrefix: keyPrefix ?? ""
|
|
3260
3179
|
});
|
|
3261
3180
|
await adapter.connect();
|
|
3262
3181
|
return adapter;
|
|
3263
3182
|
}
|
|
3264
|
-
const fallback = new
|
|
3183
|
+
const fallback = new import_utils9.MemoryStorageAdapter();
|
|
3265
3184
|
await fallback.connect();
|
|
3266
3185
|
return fallback;
|
|
3267
3186
|
}
|
|
@@ -3281,7 +3200,7 @@ async function createSessionCredentialVault(options) {
|
|
|
3281
3200
|
}
|
|
3282
3201
|
|
|
3283
3202
|
// libs/auth/src/session/credential-resume-link.ts
|
|
3284
|
-
var
|
|
3203
|
+
var import_utils10 = require("@frontmcp/utils");
|
|
3285
3204
|
var DEFAULT_RESUME_TTL_MS = 6e5;
|
|
3286
3205
|
function signCredentialResumeToken(input, secret) {
|
|
3287
3206
|
const payload = {
|
|
@@ -3290,7 +3209,7 @@ function signCredentialResumeToken(input, secret) {
|
|
|
3290
3209
|
...input.context !== void 0 ? { context: input.context } : {},
|
|
3291
3210
|
exp: Date.now() + (input.ttlMs ?? DEFAULT_RESUME_TTL_MS)
|
|
3292
3211
|
};
|
|
3293
|
-
const signedJson = (0,
|
|
3212
|
+
const signedJson = (0, import_utils10.signData)(payload, { secret });
|
|
3294
3213
|
return Buffer.from(signedJson, "utf8").toString("base64url");
|
|
3295
3214
|
}
|
|
3296
3215
|
function verifyCredentialResumeToken(token, secret, now = Date.now()) {
|
|
@@ -3300,7 +3219,7 @@ function verifyCredentialResumeToken(token, secret, now = Date.now()) {
|
|
|
3300
3219
|
} catch {
|
|
3301
3220
|
return null;
|
|
3302
3221
|
}
|
|
3303
|
-
const payload = (0,
|
|
3222
|
+
const payload = (0, import_utils10.verifyData)(signedJson, { secret });
|
|
3304
3223
|
if (!payload) return null;
|
|
3305
3224
|
if (typeof payload.sub !== "string" || typeof payload.key !== "string" || !Number.isFinite(payload.exp)) {
|
|
3306
3225
|
return null;
|
|
@@ -3319,7 +3238,7 @@ function buildCredentialResumeUrl(basePath, token) {
|
|
|
3319
3238
|
}
|
|
3320
3239
|
|
|
3321
3240
|
// libs/auth/src/session/incremental-auth-ticket.ts
|
|
3322
|
-
var
|
|
3241
|
+
var import_utils11 = require("@frontmcp/utils");
|
|
3323
3242
|
var DEFAULT_INCREMENTAL_TICKET_TTL_MS = 3e5;
|
|
3324
3243
|
function signIncrementalAuthTicket(input, secret) {
|
|
3325
3244
|
const payload = {
|
|
@@ -3327,10 +3246,10 @@ function signIncrementalAuthTicket(input, secret) {
|
|
|
3327
3246
|
appId: input.appId,
|
|
3328
3247
|
...input.toolId !== void 0 ? { toolId: input.toolId } : {},
|
|
3329
3248
|
...input.priorAppIds !== void 0 ? { priorAppIds: input.priorAppIds } : {},
|
|
3330
|
-
jti: (0,
|
|
3249
|
+
jti: (0, import_utils11.randomUUID)(),
|
|
3331
3250
|
exp: Date.now() + (input.ttlMs ?? DEFAULT_INCREMENTAL_TICKET_TTL_MS)
|
|
3332
3251
|
};
|
|
3333
|
-
const signedJson = (0,
|
|
3252
|
+
const signedJson = (0, import_utils11.signData)(payload, { secret });
|
|
3334
3253
|
return Buffer.from(signedJson, "utf8").toString("base64url");
|
|
3335
3254
|
}
|
|
3336
3255
|
function verifyIncrementalAuthTicket(token, secret, now = Date.now()) {
|
|
@@ -3340,7 +3259,7 @@ function verifyIncrementalAuthTicket(token, secret, now = Date.now()) {
|
|
|
3340
3259
|
} catch {
|
|
3341
3260
|
return null;
|
|
3342
3261
|
}
|
|
3343
|
-
const payload = (0,
|
|
3262
|
+
const payload = (0, import_utils11.verifyData)(signedJson, { secret });
|
|
3344
3263
|
if (!payload) return null;
|
|
3345
3264
|
if (typeof payload.sub !== "string" || payload.sub.length === 0) return null;
|
|
3346
3265
|
if (typeof payload.appId !== "string" || payload.appId.length === 0) return null;
|
|
@@ -3520,7 +3439,7 @@ function errMsg2(err) {
|
|
|
3520
3439
|
var SECURE_STORE_ACCESSOR = /* @__PURE__ */ Symbol.for("frontmcp:SECURE_STORE_ACCESSOR");
|
|
3521
3440
|
|
|
3522
3441
|
// libs/auth/src/session/secure-store-accessor.impl.ts
|
|
3523
|
-
var
|
|
3442
|
+
var import_utils12 = require("@frontmcp/utils");
|
|
3524
3443
|
var GLOBAL_NAMESPACE = "global";
|
|
3525
3444
|
var SecureStoreAccessorImpl = class {
|
|
3526
3445
|
backend;
|
|
@@ -3549,12 +3468,12 @@ var SecureStoreAccessorImpl = class {
|
|
|
3549
3468
|
return GLOBAL_NAMESPACE;
|
|
3550
3469
|
case "session": {
|
|
3551
3470
|
const sid = this.resolveSessionId();
|
|
3552
|
-
return sid ? `s:${(0,
|
|
3471
|
+
return sid ? `s:${(0, import_utils12.sha256Hex)(sid).slice(0, 32)}` : void 0;
|
|
3553
3472
|
}
|
|
3554
3473
|
case "user":
|
|
3555
3474
|
default: {
|
|
3556
3475
|
const sub = this.resolveSub();
|
|
3557
|
-
return sub ? `u:${(0,
|
|
3476
|
+
return sub ? `u:${(0, import_utils12.sha256Hex)(sub).slice(0, 32)}` : void 0;
|
|
3558
3477
|
}
|
|
3559
3478
|
}
|
|
3560
3479
|
}
|
|
@@ -3595,7 +3514,7 @@ function errMsg3(err) {
|
|
|
3595
3514
|
}
|
|
3596
3515
|
|
|
3597
3516
|
// libs/auth/src/session/secure-store.factory.ts
|
|
3598
|
-
var
|
|
3517
|
+
var import_utils13 = require("@frontmcp/utils");
|
|
3599
3518
|
function isCustomBackendConfig(config) {
|
|
3600
3519
|
return typeof config === "object" && config !== null && "backend" in config && !!config.backend;
|
|
3601
3520
|
}
|
|
@@ -3644,7 +3563,7 @@ async function createSecureStore(options) {
|
|
|
3644
3563
|
if (storage) {
|
|
3645
3564
|
adapter = storage;
|
|
3646
3565
|
} else if (kind === "memory") {
|
|
3647
|
-
adapter = new
|
|
3566
|
+
adapter = new import_utils13.MemoryStorageAdapter();
|
|
3648
3567
|
await adapter.connect();
|
|
3649
3568
|
} else {
|
|
3650
3569
|
adapter = await createTokenStorageAdapter(toTokenStorageConfig(config));
|
|
@@ -3658,7 +3577,7 @@ async function createSecureStore(options) {
|
|
|
3658
3577
|
}
|
|
3659
3578
|
|
|
3660
3579
|
// libs/auth/src/session/token.vault.ts
|
|
3661
|
-
var
|
|
3580
|
+
var import_utils14 = require("@frontmcp/utils");
|
|
3662
3581
|
var TokenVault = class {
|
|
3663
3582
|
/** Active key used for new encryptions */
|
|
3664
3583
|
active;
|
|
@@ -3687,14 +3606,14 @@ var TokenVault = class {
|
|
|
3687
3606
|
this.keys.set(k.kid, k.key);
|
|
3688
3607
|
}
|
|
3689
3608
|
async encrypt(plaintext, opts) {
|
|
3690
|
-
const iv = (0,
|
|
3691
|
-
const { ciphertext, tag } = await (0,
|
|
3609
|
+
const iv = (0, import_utils14.randomBytes)(12);
|
|
3610
|
+
const { ciphertext, tag } = await (0, import_utils14.encryptAesGcm)(this.active.key, new TextEncoder().encode(plaintext), iv);
|
|
3692
3611
|
return {
|
|
3693
3612
|
alg: "A256GCM",
|
|
3694
3613
|
kid: this.active.kid,
|
|
3695
|
-
iv: (0,
|
|
3696
|
-
tag: (0,
|
|
3697
|
-
data: (0,
|
|
3614
|
+
iv: (0, import_utils14.base64urlEncode)(iv),
|
|
3615
|
+
tag: (0, import_utils14.base64urlEncode)(tag),
|
|
3616
|
+
data: (0, import_utils14.base64urlEncode)(ciphertext),
|
|
3698
3617
|
exp: opts?.exp,
|
|
3699
3618
|
meta: opts?.meta
|
|
3700
3619
|
};
|
|
@@ -3708,16 +3627,16 @@ var TokenVault = class {
|
|
|
3708
3627
|
}
|
|
3709
3628
|
const key = this.keys.get(blob.kid);
|
|
3710
3629
|
if (!key) throw new Error(`vault_unknown_kid:${blob.kid}`);
|
|
3711
|
-
const iv = (0,
|
|
3712
|
-
const tag = (0,
|
|
3713
|
-
const data = (0,
|
|
3714
|
-
const plaintext = await (0,
|
|
3630
|
+
const iv = (0, import_utils14.base64urlDecode)(blob.iv);
|
|
3631
|
+
const tag = (0, import_utils14.base64urlDecode)(blob.tag);
|
|
3632
|
+
const data = (0, import_utils14.base64urlDecode)(blob.data);
|
|
3633
|
+
const plaintext = await (0, import_utils14.decryptAesGcm)(key, data, iv, tag);
|
|
3715
3634
|
return new TextDecoder().decode(plaintext);
|
|
3716
3635
|
}
|
|
3717
3636
|
};
|
|
3718
3637
|
|
|
3719
3638
|
// libs/auth/src/session/index.ts
|
|
3720
|
-
var
|
|
3639
|
+
var import_utils34 = require("@frontmcp/utils");
|
|
3721
3640
|
|
|
3722
3641
|
// libs/auth/src/session/transport-session.types.ts
|
|
3723
3642
|
var import_lazy_zod4 = require("@frontmcp/lazy-zod");
|
|
@@ -3813,10 +3732,10 @@ var redisConfigSchema = import_lazy_zod4.z.object({
|
|
|
3813
3732
|
});
|
|
3814
3733
|
|
|
3815
3734
|
// libs/auth/src/session/session-crypto.ts
|
|
3816
|
-
var
|
|
3735
|
+
var import_utils16 = require("@frontmcp/utils");
|
|
3817
3736
|
|
|
3818
3737
|
// libs/auth/src/errors/auth-internal.error.ts
|
|
3819
|
-
var
|
|
3738
|
+
var import_utils15 = require("@frontmcp/utils");
|
|
3820
3739
|
var AuthInternalError = class extends Error {
|
|
3821
3740
|
/**
|
|
3822
3741
|
* Unique error ID for tracking in logs.
|
|
@@ -3838,7 +3757,7 @@ var AuthInternalError = class extends Error {
|
|
|
3838
3757
|
super(message);
|
|
3839
3758
|
this.name = this.constructor.name;
|
|
3840
3759
|
this.code = code;
|
|
3841
|
-
this.errorId = `err_${(0,
|
|
3760
|
+
this.errorId = `err_${(0, import_utils15.bytesToHex)((0, import_utils15.randomBytes)(8))}`;
|
|
3842
3761
|
Error.captureStackTrace(this, this.constructor);
|
|
3843
3762
|
}
|
|
3844
3763
|
/**
|
|
@@ -3977,9 +3896,9 @@ var AuthFlowError = class extends AuthInternalError {
|
|
|
3977
3896
|
|
|
3978
3897
|
// libs/auth/src/session/session-crypto.ts
|
|
3979
3898
|
function getSigningSecret(config) {
|
|
3980
|
-
const secret = config?.secret || (0,
|
|
3899
|
+
const secret = config?.secret || (0, import_utils16.getEnv)("MCP_SESSION_SECRET");
|
|
3981
3900
|
if (!secret) {
|
|
3982
|
-
if ((0,
|
|
3901
|
+
if ((0, import_utils16.isProduction)()) {
|
|
3983
3902
|
throw new SessionSecretRequiredError("session signing");
|
|
3984
3903
|
}
|
|
3985
3904
|
console.warn("[SessionCrypto] MCP_SESSION_SECRET not set. Using insecure default for development only.");
|
|
@@ -3991,13 +3910,13 @@ function resolveConfig(config) {
|
|
|
3991
3910
|
return { secret: getSigningSecret(config) };
|
|
3992
3911
|
}
|
|
3993
3912
|
function signSession(session, config) {
|
|
3994
|
-
return (0,
|
|
3913
|
+
return (0, import_utils16.signData)(session, resolveConfig(config));
|
|
3995
3914
|
}
|
|
3996
3915
|
function verifySession(signedData, config) {
|
|
3997
|
-
return (0,
|
|
3916
|
+
return (0, import_utils16.verifyData)(signedData, resolveConfig(config));
|
|
3998
3917
|
}
|
|
3999
3918
|
function verifyOrParseSession(data, config) {
|
|
4000
|
-
return (0,
|
|
3919
|
+
return (0, import_utils16.verifyOrParseData)(data, resolveConfig(config));
|
|
4001
3920
|
}
|
|
4002
3921
|
|
|
4003
3922
|
// libs/auth/src/session/session-rate-limiter.ts
|
|
@@ -4124,7 +4043,7 @@ var SessionRateLimiter = class {
|
|
|
4124
4043
|
var defaultSessionRateLimiter = new SessionRateLimiter();
|
|
4125
4044
|
|
|
4126
4045
|
// libs/auth/src/session/session.transport.ts
|
|
4127
|
-
var
|
|
4046
|
+
var import_utils17 = require("@frontmcp/utils");
|
|
4128
4047
|
var TransportIdGenerator = class {
|
|
4129
4048
|
/**
|
|
4130
4049
|
* Create a transport session ID.
|
|
@@ -4133,7 +4052,7 @@ var TransportIdGenerator = class {
|
|
|
4133
4052
|
* @returns A transport session ID (UUID without dashes)
|
|
4134
4053
|
*/
|
|
4135
4054
|
static createId() {
|
|
4136
|
-
return (0,
|
|
4055
|
+
return (0, import_utils17.randomUUID)().replace(/-/g, "");
|
|
4137
4056
|
}
|
|
4138
4057
|
};
|
|
4139
4058
|
|
|
@@ -4182,7 +4101,7 @@ var TinyTtlCache = class {
|
|
|
4182
4101
|
};
|
|
4183
4102
|
|
|
4184
4103
|
// libs/auth/src/session/utils/auth-token.utils.ts
|
|
4185
|
-
var
|
|
4104
|
+
var import_utils18 = require("@frontmcp/utils");
|
|
4186
4105
|
function isJwt(token) {
|
|
4187
4106
|
if (!token) return false;
|
|
4188
4107
|
return token.split(".").length === 3;
|
|
@@ -4192,7 +4111,7 @@ function getTokenSignatureFingerprint(token) {
|
|
|
4192
4111
|
const sig = token.split(".")[2];
|
|
4193
4112
|
if (sig) return sig;
|
|
4194
4113
|
}
|
|
4195
|
-
return (0,
|
|
4114
|
+
return (0, import_utils18.sha256Base64url)(token);
|
|
4196
4115
|
}
|
|
4197
4116
|
function extractClaimValue(claims, key, validator) {
|
|
4198
4117
|
const value = claims[key];
|
|
@@ -4224,26 +4143,26 @@ function extractBearerToken(header) {
|
|
|
4224
4143
|
}
|
|
4225
4144
|
|
|
4226
4145
|
// libs/auth/src/session/utils/session-crypto.utils.ts
|
|
4227
|
-
var
|
|
4146
|
+
var import_utils19 = require("@frontmcp/utils");
|
|
4228
4147
|
var cachedKey = null;
|
|
4229
4148
|
function getKey() {
|
|
4230
4149
|
if (cachedKey) return cachedKey;
|
|
4231
|
-
const secret = (0,
|
|
4150
|
+
const secret = (0, import_utils19.getEnv)("MCP_SESSION_SECRET");
|
|
4232
4151
|
if (!secret) {
|
|
4233
|
-
if ((0,
|
|
4152
|
+
if ((0, import_utils19.isProduction)()) {
|
|
4234
4153
|
throw new SessionSecretRequiredError("session ID encryption");
|
|
4235
4154
|
}
|
|
4236
4155
|
console.warn(
|
|
4237
4156
|
"[SessionIdUtils] Using machine ID as session encryption secret - NOT SECURE FOR PRODUCTION. Set MCP_SESSION_SECRET environment variable for secure session encryption."
|
|
4238
4157
|
);
|
|
4239
4158
|
}
|
|
4240
|
-
const base = secret || (0,
|
|
4241
|
-
cachedKey = (0,
|
|
4159
|
+
const base = secret || (0, import_utils19.getMachineId)();
|
|
4160
|
+
cachedKey = (0, import_utils19.sha256)(new TextEncoder().encode(base));
|
|
4242
4161
|
return cachedKey;
|
|
4243
4162
|
}
|
|
4244
4163
|
function encryptJson(obj) {
|
|
4245
4164
|
const key = getKey();
|
|
4246
|
-
const encrypted = (0,
|
|
4165
|
+
const encrypted = (0, import_utils19.encryptValue)(obj, key);
|
|
4247
4166
|
return `${encrypted.iv}.${encrypted.tag}.${encrypted.data}`;
|
|
4248
4167
|
}
|
|
4249
4168
|
function decryptSessionJson(sessionId) {
|
|
@@ -4252,7 +4171,7 @@ function decryptSessionJson(sessionId) {
|
|
|
4252
4171
|
const [ivB64, tagB64, ctB64] = parts;
|
|
4253
4172
|
if (!ivB64 || !tagB64 || !ctB64) return null;
|
|
4254
4173
|
const key = getKey();
|
|
4255
|
-
return (0,
|
|
4174
|
+
return (0, import_utils19.decryptValue)({ alg: "A256GCM", iv: ivB64, tag: tagB64, data: ctB64 }, key);
|
|
4256
4175
|
}
|
|
4257
4176
|
function safeDecrypt(sessionId) {
|
|
4258
4177
|
try {
|
|
@@ -4266,11 +4185,11 @@ function resetCachedKey() {
|
|
|
4266
4185
|
}
|
|
4267
4186
|
|
|
4268
4187
|
// libs/auth/src/session/storage/index.ts
|
|
4269
|
-
var import_utils22 = require("@frontmcp/utils");
|
|
4270
4188
|
var import_utils23 = require("@frontmcp/utils");
|
|
4189
|
+
var import_utils24 = require("@frontmcp/utils");
|
|
4271
4190
|
|
|
4272
4191
|
// libs/auth/src/session/storage/storage-token-store.ts
|
|
4273
|
-
var
|
|
4192
|
+
var import_utils20 = require("@frontmcp/utils");
|
|
4274
4193
|
var StorageTokenStore = class {
|
|
4275
4194
|
storage;
|
|
4276
4195
|
namespace;
|
|
@@ -4282,13 +4201,13 @@ var StorageTokenStore = class {
|
|
|
4282
4201
|
this.defaultTtlSeconds = options.defaultTtlSeconds;
|
|
4283
4202
|
this.storageIsNamespaced = "namespace" in storage && typeof storage.namespace === "function";
|
|
4284
4203
|
const namespacedStorage = this.storageIsNamespaced ? storage.namespace(this.namespace) : storage;
|
|
4285
|
-
this.storage = new
|
|
4204
|
+
this.storage = new import_utils20.TypedStorage(namespacedStorage);
|
|
4286
4205
|
}
|
|
4287
4206
|
/**
|
|
4288
4207
|
* Allocate a new unique ID for a token record.
|
|
4289
4208
|
*/
|
|
4290
4209
|
allocId() {
|
|
4291
|
-
return (0,
|
|
4210
|
+
return (0, import_utils20.randomUUID)();
|
|
4292
4211
|
}
|
|
4293
4212
|
/**
|
|
4294
4213
|
* Store an encrypted token blob.
|
|
@@ -4351,7 +4270,7 @@ var StorageTokenStore = class {
|
|
|
4351
4270
|
};
|
|
4352
4271
|
|
|
4353
4272
|
// libs/auth/src/session/storage/storage-authorization-vault.ts
|
|
4354
|
-
var
|
|
4273
|
+
var import_utils21 = require("@frontmcp/utils");
|
|
4355
4274
|
var StorageAuthorizationVault = class {
|
|
4356
4275
|
storage;
|
|
4357
4276
|
namespace;
|
|
@@ -4360,7 +4279,7 @@ var StorageAuthorizationVault = class {
|
|
|
4360
4279
|
this.namespace = options.namespace ?? "vault";
|
|
4361
4280
|
this.pendingAuthTtlMs = options.pendingAuthTtlMs ?? 10 * 60 * 1e3;
|
|
4362
4281
|
const namespacedStorage = this.isNamespacedStorage(storage) ? storage.namespace(this.namespace) : storage;
|
|
4363
|
-
this.storage = new
|
|
4282
|
+
this.storage = new import_utils21.TypedStorage(namespacedStorage, {
|
|
4364
4283
|
schema: options.validateOnRead ? authorizationVaultEntrySchema : void 0,
|
|
4365
4284
|
throwOnInvalid: false
|
|
4366
4285
|
});
|
|
@@ -4371,7 +4290,7 @@ var StorageAuthorizationVault = class {
|
|
|
4371
4290
|
async create(params) {
|
|
4372
4291
|
const now = Date.now();
|
|
4373
4292
|
const entry = {
|
|
4374
|
-
id: (0,
|
|
4293
|
+
id: (0, import_utils21.randomUUID)(),
|
|
4375
4294
|
userSub: params.userSub,
|
|
4376
4295
|
userEmail: params.userEmail,
|
|
4377
4296
|
userName: params.userName,
|
|
@@ -4445,7 +4364,7 @@ var StorageAuthorizationVault = class {
|
|
|
4445
4364
|
}
|
|
4446
4365
|
const now = Date.now();
|
|
4447
4366
|
const pendingAuth = {
|
|
4448
|
-
id: (0,
|
|
4367
|
+
id: (0, import_utils21.randomUUID)(),
|
|
4449
4368
|
appId: params.appId,
|
|
4450
4369
|
toolId: params.toolId,
|
|
4451
4370
|
authUrl: params.authUrl,
|
|
@@ -4649,11 +4568,11 @@ var StorageAuthorizationVault = class {
|
|
|
4649
4568
|
};
|
|
4650
4569
|
|
|
4651
4570
|
// libs/auth/src/session/storage/in-memory-authorization-vault.ts
|
|
4652
|
-
var
|
|
4571
|
+
var import_utils22 = require("@frontmcp/utils");
|
|
4653
4572
|
var InMemoryAuthorizationVault = class extends StorageAuthorizationVault {
|
|
4654
4573
|
memoryAdapter;
|
|
4655
4574
|
constructor(options = {}) {
|
|
4656
|
-
const memoryAdapter = new
|
|
4575
|
+
const memoryAdapter = new import_utils22.MemoryStorageAdapter();
|
|
4657
4576
|
super(memoryAdapter, {
|
|
4658
4577
|
namespace: options.namespace ?? "vault",
|
|
4659
4578
|
pendingAuthTtlMs: options.pendingAuthTtlMs
|
|
@@ -4674,7 +4593,7 @@ var InMemoryAuthorizationVault = class extends StorageAuthorizationVault {
|
|
|
4674
4593
|
};
|
|
4675
4594
|
|
|
4676
4595
|
// libs/auth/src/session/redis-session.store.ts
|
|
4677
|
-
var
|
|
4596
|
+
var import_utils25 = require("@frontmcp/utils");
|
|
4678
4597
|
var RedisSessionStore = class {
|
|
4679
4598
|
storage;
|
|
4680
4599
|
keyPrefix;
|
|
@@ -4696,14 +4615,14 @@ var RedisSessionStore = class {
|
|
|
4696
4615
|
});
|
|
4697
4616
|
}
|
|
4698
4617
|
if ("redis" in config && config.redis) {
|
|
4699
|
-
this.storage = new
|
|
4618
|
+
this.storage = new import_utils25.RedisStorageAdapter({
|
|
4700
4619
|
client: config.redis,
|
|
4701
4620
|
keyPrefix: this.keyPrefix
|
|
4702
4621
|
});
|
|
4703
4622
|
this.externalInstance = true;
|
|
4704
4623
|
} else {
|
|
4705
4624
|
const redisConfig = config;
|
|
4706
|
-
this.storage = new
|
|
4625
|
+
this.storage = new import_utils25.RedisStorageAdapter({
|
|
4707
4626
|
config: {
|
|
4708
4627
|
host: redisConfig.host,
|
|
4709
4628
|
port: redisConfig.port ?? 6379,
|
|
@@ -4885,7 +4804,7 @@ var RedisSessionStore = class {
|
|
|
4885
4804
|
* Allocate a new session ID
|
|
4886
4805
|
*/
|
|
4887
4806
|
allocId() {
|
|
4888
|
-
return (0,
|
|
4807
|
+
return (0, import_utils25.randomUUID)();
|
|
4889
4808
|
}
|
|
4890
4809
|
/**
|
|
4891
4810
|
* Disconnect from Redis (only if we created the connection)
|
|
@@ -4921,7 +4840,7 @@ var RedisSessionStore = class {
|
|
|
4921
4840
|
};
|
|
4922
4841
|
|
|
4923
4842
|
// libs/auth/src/session/vercel-kv-session.store.ts
|
|
4924
|
-
var
|
|
4843
|
+
var import_utils26 = require("@frontmcp/utils");
|
|
4925
4844
|
var VercelKvSessionStore = class {
|
|
4926
4845
|
storage;
|
|
4927
4846
|
keyPrefix;
|
|
@@ -4941,7 +4860,7 @@ var VercelKvSessionStore = class {
|
|
|
4941
4860
|
maxRequests: this.security.rateLimiting?.maxRequests
|
|
4942
4861
|
});
|
|
4943
4862
|
}
|
|
4944
|
-
this.storage = new
|
|
4863
|
+
this.storage = new import_utils26.VercelKvStorageAdapter({
|
|
4945
4864
|
url: config.url,
|
|
4946
4865
|
token: config.token,
|
|
4947
4866
|
keyPrefix: this.keyPrefix
|
|
@@ -5115,7 +5034,7 @@ var VercelKvSessionStore = class {
|
|
|
5115
5034
|
* Allocate a new session ID
|
|
5116
5035
|
*/
|
|
5117
5036
|
allocId() {
|
|
5118
|
-
return (0,
|
|
5037
|
+
return (0, import_utils26.randomUUID)();
|
|
5119
5038
|
}
|
|
5120
5039
|
/**
|
|
5121
5040
|
* Disconnect from Vercel KV
|
|
@@ -5144,7 +5063,7 @@ var VercelKvSessionStore = class {
|
|
|
5144
5063
|
};
|
|
5145
5064
|
|
|
5146
5065
|
// libs/auth/src/session/orchestrated-token.store.ts
|
|
5147
|
-
var
|
|
5066
|
+
var import_utils27 = require("@frontmcp/utils");
|
|
5148
5067
|
var InMemoryOrchestratedTokenStore = class {
|
|
5149
5068
|
/** Token storage: Map<compositeKey, ProviderTokenRecord> */
|
|
5150
5069
|
tokens = /* @__PURE__ */ new Map();
|
|
@@ -5186,7 +5105,7 @@ var InMemoryOrchestratedTokenStore = class {
|
|
|
5186
5105
|
}
|
|
5187
5106
|
const info = new TextEncoder().encode(`orchestrated-token:${compositeKey}`);
|
|
5188
5107
|
const salt = new TextEncoder().encode("frontmcp-token-store");
|
|
5189
|
-
const derivedKey = (0,
|
|
5108
|
+
const derivedKey = (0, import_utils27.hkdfSha256)(this.encryptionKey, salt, info, 32);
|
|
5190
5109
|
this.derivedKeys.set(compositeKey, derivedKey);
|
|
5191
5110
|
return derivedKey;
|
|
5192
5111
|
}
|
|
@@ -5196,8 +5115,8 @@ var InMemoryOrchestratedTokenStore = class {
|
|
|
5196
5115
|
async encryptRecord(compositeKey, record) {
|
|
5197
5116
|
const key = await this.deriveKeyForRecord(compositeKey);
|
|
5198
5117
|
const plaintext = JSON.stringify(record);
|
|
5199
|
-
const iv = (0,
|
|
5200
|
-
const { ciphertext, tag } = (0,
|
|
5118
|
+
const iv = (0, import_utils27.randomBytes)(12);
|
|
5119
|
+
const { ciphertext, tag } = (0, import_utils27.encryptAesGcm)(key, new TextEncoder().encode(plaintext), iv);
|
|
5201
5120
|
return JSON.stringify({
|
|
5202
5121
|
iv: Buffer.from(iv).toString("base64url"),
|
|
5203
5122
|
tag: Buffer.from(tag).toString("base64url"),
|
|
@@ -5213,7 +5132,7 @@ var InMemoryOrchestratedTokenStore = class {
|
|
|
5213
5132
|
const ivBytes = Buffer.from(iv, "base64url");
|
|
5214
5133
|
const tagBytes = Buffer.from(tag, "base64url");
|
|
5215
5134
|
const ciphertextBytes = Buffer.from(data, "base64url");
|
|
5216
|
-
const plaintext = (0,
|
|
5135
|
+
const plaintext = (0, import_utils27.decryptAesGcm)(key, ciphertextBytes, ivBytes, tagBytes);
|
|
5217
5136
|
return JSON.parse(new TextDecoder().decode(plaintext));
|
|
5218
5137
|
}
|
|
5219
5138
|
/**
|
|
@@ -5415,7 +5334,7 @@ var InMemoryOrchestratedTokenStore = class {
|
|
|
5415
5334
|
};
|
|
5416
5335
|
|
|
5417
5336
|
// libs/auth/src/session/orchestrated-token.crypto.ts
|
|
5418
|
-
var
|
|
5337
|
+
var import_utils28 = require("@frontmcp/utils");
|
|
5419
5338
|
var HKDF_SALT = new TextEncoder().encode("frontmcp-token-store");
|
|
5420
5339
|
function deriveKeyForRecord(masterKey, compositeKey, cache) {
|
|
5421
5340
|
const cached = cache.get(compositeKey);
|
|
@@ -5423,14 +5342,14 @@ function deriveKeyForRecord(masterKey, compositeKey, cache) {
|
|
|
5423
5342
|
return cached;
|
|
5424
5343
|
}
|
|
5425
5344
|
const info = new TextEncoder().encode(`orchestrated-token:${compositeKey}`);
|
|
5426
|
-
const derived = (0,
|
|
5345
|
+
const derived = (0, import_utils28.hkdfSha256)(masterKey, HKDF_SALT, info, 32);
|
|
5427
5346
|
cache.set(compositeKey, derived);
|
|
5428
5347
|
return derived;
|
|
5429
5348
|
}
|
|
5430
5349
|
function encryptRecord(key, record) {
|
|
5431
5350
|
const plaintext = JSON.stringify(record);
|
|
5432
|
-
const iv = (0,
|
|
5433
|
-
const { ciphertext, tag } = (0,
|
|
5351
|
+
const iv = (0, import_utils28.randomBytes)(12);
|
|
5352
|
+
const { ciphertext, tag } = (0, import_utils28.encryptAesGcm)(key, new TextEncoder().encode(plaintext), iv);
|
|
5434
5353
|
return JSON.stringify({
|
|
5435
5354
|
iv: Buffer.from(iv).toString("base64url"),
|
|
5436
5355
|
tag: Buffer.from(tag).toString("base64url"),
|
|
@@ -5442,7 +5361,7 @@ function decryptRecord(key, encrypted) {
|
|
|
5442
5361
|
const ivBytes = Buffer.from(iv, "base64url");
|
|
5443
5362
|
const tagBytes = Buffer.from(tag, "base64url");
|
|
5444
5363
|
const ciphertextBytes = Buffer.from(data, "base64url");
|
|
5445
|
-
const plaintext = (0,
|
|
5364
|
+
const plaintext = (0, import_utils28.decryptAesGcm)(key, ciphertextBytes, ivBytes, tagBytes);
|
|
5446
5365
|
return JSON.parse(new TextDecoder().decode(plaintext));
|
|
5447
5366
|
}
|
|
5448
5367
|
|
|
@@ -5621,7 +5540,7 @@ var StorageOrchestratedTokenStore = class {
|
|
|
5621
5540
|
};
|
|
5622
5541
|
|
|
5623
5542
|
// libs/auth/src/session/federated-auth.session.ts
|
|
5624
|
-
var
|
|
5543
|
+
var import_utils29 = require("@frontmcp/utils");
|
|
5625
5544
|
function toSessionRecord(session) {
|
|
5626
5545
|
return {
|
|
5627
5546
|
...session,
|
|
@@ -5696,7 +5615,7 @@ var InMemoryFederatedAuthSessionStore = class {
|
|
|
5696
5615
|
createSession(params) {
|
|
5697
5616
|
const now = Date.now();
|
|
5698
5617
|
return {
|
|
5699
|
-
id: (0,
|
|
5618
|
+
id: (0, import_utils29.randomUUID)(),
|
|
5700
5619
|
pendingAuthId: params.pendingAuthId,
|
|
5701
5620
|
clientId: params.clientId,
|
|
5702
5621
|
redirectUri: params.redirectUri,
|
|
@@ -5728,7 +5647,7 @@ var InMemoryFederatedAuthSessionStore = class {
|
|
|
5728
5647
|
function createFederatedAuthSession(params, ttlMs = 15 * 60 * 1e3) {
|
|
5729
5648
|
const now = Date.now();
|
|
5730
5649
|
return {
|
|
5731
|
-
id: (0,
|
|
5650
|
+
id: (0, import_utils29.randomUUID)(),
|
|
5732
5651
|
pendingAuthId: params.pendingAuthId,
|
|
5733
5652
|
clientId: params.clientId,
|
|
5734
5653
|
redirectUri: params.redirectUri,
|
|
@@ -5785,7 +5704,7 @@ function startNextProvider(session, pkce, state) {
|
|
|
5785
5704
|
}
|
|
5786
5705
|
|
|
5787
5706
|
// libs/auth/src/session/storage-federated-auth.session.ts
|
|
5788
|
-
var
|
|
5707
|
+
var import_utils30 = require("@frontmcp/utils");
|
|
5789
5708
|
var StorageFederatedAuthSessionStore = class {
|
|
5790
5709
|
storage;
|
|
5791
5710
|
namespace;
|
|
@@ -5794,7 +5713,7 @@ var StorageFederatedAuthSessionStore = class {
|
|
|
5794
5713
|
this.namespace = options.namespace ?? "fed";
|
|
5795
5714
|
this.storageIsNamespaced = this.isNamespacedStorage(storage);
|
|
5796
5715
|
const namespacedStorage = this.storageIsNamespaced ? storage.namespace(this.namespace) : storage;
|
|
5797
|
-
this.storage = new
|
|
5716
|
+
this.storage = new import_utils30.TypedStorage(namespacedStorage);
|
|
5798
5717
|
}
|
|
5799
5718
|
async store(session) {
|
|
5800
5719
|
await this.put(session);
|
|
@@ -5865,7 +5784,7 @@ var InMemoryConsentStore = class {
|
|
|
5865
5784
|
};
|
|
5866
5785
|
|
|
5867
5786
|
// libs/auth/src/session/storage-consent.store.ts
|
|
5868
|
-
var
|
|
5787
|
+
var import_utils31 = require("@frontmcp/utils");
|
|
5869
5788
|
var StorageConsentStore = class {
|
|
5870
5789
|
storage;
|
|
5871
5790
|
namespace;
|
|
@@ -5874,7 +5793,7 @@ var StorageConsentStore = class {
|
|
|
5874
5793
|
this.namespace = options.namespace ?? "remembered-consent";
|
|
5875
5794
|
this.storageIsNamespaced = this.isNamespacedStorage(storage);
|
|
5876
5795
|
const namespacedStorage = this.storageIsNamespaced ? storage.namespace(this.namespace) : storage;
|
|
5877
|
-
this.storage = new
|
|
5796
|
+
this.storage = new import_utils31.TypedStorage(namespacedStorage);
|
|
5878
5797
|
}
|
|
5879
5798
|
async get(userSub, clientId) {
|
|
5880
5799
|
return this.storage.get(this.key(userSub, clientId));
|
|
@@ -5896,7 +5815,7 @@ var StorageConsentStore = class {
|
|
|
5896
5815
|
|
|
5897
5816
|
// libs/auth/src/session/encrypted-authorization-vault.ts
|
|
5898
5817
|
var import_lazy_zod5 = require("@frontmcp/lazy-zod");
|
|
5899
|
-
var
|
|
5818
|
+
var import_utils32 = require("@frontmcp/utils");
|
|
5900
5819
|
var redisVaultEntrySchema = import_lazy_zod5.z.object({
|
|
5901
5820
|
/** Vault ID */
|
|
5902
5821
|
id: import_lazy_zod5.z.string(),
|
|
@@ -5921,7 +5840,7 @@ var redisVaultEntrySchema = import_lazy_zod5.z.object({
|
|
|
5921
5840
|
/** Encrypted sensitive data blob */
|
|
5922
5841
|
encrypted: encryptedDataSchema
|
|
5923
5842
|
});
|
|
5924
|
-
var encryptionContextStorage = new
|
|
5843
|
+
var encryptionContextStorage = new import_utils32.AsyncLocalStorage();
|
|
5925
5844
|
var EncryptedRedisVault = class {
|
|
5926
5845
|
constructor(redis, encryption, namespace = "vault:") {
|
|
5927
5846
|
this.redis = redis;
|
|
@@ -6057,7 +5976,7 @@ var EncryptedRedisVault = class {
|
|
|
6057
5976
|
async create(params) {
|
|
6058
5977
|
const now = Date.now();
|
|
6059
5978
|
const entry = {
|
|
6060
|
-
id: (0,
|
|
5979
|
+
id: (0, import_utils32.randomUUID)(),
|
|
6061
5980
|
userSub: params.userSub,
|
|
6062
5981
|
userEmail: params.userEmail,
|
|
6063
5982
|
userName: params.userName,
|
|
@@ -6116,7 +6035,7 @@ var EncryptedRedisVault = class {
|
|
|
6116
6035
|
}
|
|
6117
6036
|
const now = Date.now();
|
|
6118
6037
|
const pendingAuth = {
|
|
6119
|
-
id: (0,
|
|
6038
|
+
id: (0, import_utils32.randomUUID)(),
|
|
6120
6039
|
appId: params.appId,
|
|
6121
6040
|
toolId: params.toolId,
|
|
6122
6041
|
authUrl: params.authUrl,
|
|
@@ -6294,7 +6213,7 @@ function createEncryptedVault(redis, config = {}) {
|
|
|
6294
6213
|
}
|
|
6295
6214
|
|
|
6296
6215
|
// libs/auth/src/session/token.refresh.ts
|
|
6297
|
-
var
|
|
6216
|
+
var import_utils33 = require("@frontmcp/utils");
|
|
6298
6217
|
function toEpochSeconds(exp) {
|
|
6299
6218
|
if (exp == null) return void 0;
|
|
6300
6219
|
if (exp instanceof Date) return Math.floor(exp.getTime() / 1e3);
|
|
@@ -6316,7 +6235,7 @@ function tryJwtExp(token) {
|
|
|
6316
6235
|
const parts = token.split(".");
|
|
6317
6236
|
if (parts.length < 2) return void 0;
|
|
6318
6237
|
try {
|
|
6319
|
-
const json = JSON.parse(new TextDecoder().decode((0,
|
|
6238
|
+
const json = JSON.parse(new TextDecoder().decode((0, import_utils33.base64urlDecode)(parts[1])));
|
|
6320
6239
|
const e = json?.exp;
|
|
6321
6240
|
return typeof e === "number" ? toEpochSeconds(e) : void 0;
|
|
6322
6241
|
} catch {
|
|
@@ -6379,7 +6298,7 @@ var progressiveAuthStateSchema = import_lazy_zod6.z.object({
|
|
|
6379
6298
|
});
|
|
6380
6299
|
|
|
6381
6300
|
// libs/auth/src/authorization/authorization.class.ts
|
|
6382
|
-
var
|
|
6301
|
+
var import_utils36 = require("@frontmcp/utils");
|
|
6383
6302
|
var AuthorizationBase = class _AuthorizationBase {
|
|
6384
6303
|
id;
|
|
6385
6304
|
isAnonymous;
|
|
@@ -6427,14 +6346,14 @@ var AuthorizationBase = class _AuthorizationBase {
|
|
|
6427
6346
|
* @param fingerprint - Optional client fingerprint for tracking
|
|
6428
6347
|
*/
|
|
6429
6348
|
createTransportSession(protocol, fingerprint) {
|
|
6430
|
-
const sessionId = (0,
|
|
6349
|
+
const sessionId = (0, import_utils36.randomUUID)();
|
|
6431
6350
|
const session = {
|
|
6432
6351
|
id: sessionId,
|
|
6433
6352
|
authorizationId: this.id,
|
|
6434
6353
|
protocol,
|
|
6435
6354
|
createdAt: Date.now(),
|
|
6436
6355
|
expiresAt: this.expiresAt,
|
|
6437
|
-
nodeId: (0,
|
|
6356
|
+
nodeId: (0, import_utils36.getMachineId)(),
|
|
6438
6357
|
clientFingerprint: fingerprint
|
|
6439
6358
|
};
|
|
6440
6359
|
this.#sessions.set(session.id, session);
|
|
@@ -6575,7 +6494,7 @@ var AuthorizationBase = class _AuthorizationBase {
|
|
|
6575
6494
|
*/
|
|
6576
6495
|
static isJwtHeader(segment) {
|
|
6577
6496
|
try {
|
|
6578
|
-
const bytes = (0,
|
|
6497
|
+
const bytes = (0, import_utils36.base64urlDecode)(segment);
|
|
6579
6498
|
const text = new TextDecoder().decode(bytes);
|
|
6580
6499
|
const parsed = JSON.parse(text);
|
|
6581
6500
|
return typeof parsed === "object" && parsed !== null && typeof parsed.alg === "string";
|
|
@@ -6627,7 +6546,7 @@ var AuthorizationBase = class _AuthorizationBase {
|
|
|
6627
6546
|
};
|
|
6628
6547
|
|
|
6629
6548
|
// libs/auth/src/authorization/public.authorization.ts
|
|
6630
|
-
var
|
|
6549
|
+
var import_utils37 = require("@frontmcp/utils");
|
|
6631
6550
|
var PublicAuthorization = class _PublicAuthorization extends AuthorizationBase {
|
|
6632
6551
|
mode = "public";
|
|
6633
6552
|
/**
|
|
@@ -6663,7 +6582,7 @@ var PublicAuthorization = class _PublicAuthorization extends AuthorizationBase {
|
|
|
6663
6582
|
allowedTools = "all",
|
|
6664
6583
|
allowedPrompts = "all"
|
|
6665
6584
|
} = ctx;
|
|
6666
|
-
const uuid = (0,
|
|
6585
|
+
const uuid = (0, import_utils37.randomUUID)();
|
|
6667
6586
|
const sub = `${prefix}:${uuid}`;
|
|
6668
6587
|
const user = {
|
|
6669
6588
|
sub,
|
|
@@ -6747,7 +6666,7 @@ var PublicAuthorization = class _PublicAuthorization extends AuthorizationBase {
|
|
|
6747
6666
|
};
|
|
6748
6667
|
|
|
6749
6668
|
// libs/auth/src/authorization/transparent.authorization.ts
|
|
6750
|
-
var
|
|
6669
|
+
var import_utils38 = require("@frontmcp/utils");
|
|
6751
6670
|
var TransparentAuthorization = class _TransparentAuthorization extends AuthorizationBase {
|
|
6752
6671
|
mode = "transparent";
|
|
6753
6672
|
/**
|
|
@@ -6847,7 +6766,7 @@ var TransparentAuthorization = class _TransparentAuthorization extends Authoriza
|
|
|
6847
6766
|
static generateAuthorizationId(token) {
|
|
6848
6767
|
const parts = token.split(".");
|
|
6849
6768
|
const signature = parts[2] || token;
|
|
6850
|
-
return (0,
|
|
6769
|
+
return (0, import_utils38.sha256Hex)(signature).substring(0, 16);
|
|
6851
6770
|
}
|
|
6852
6771
|
/**
|
|
6853
6772
|
* Get the issuer from the token claims
|
|
@@ -6873,11 +6792,11 @@ var TransparentAuthorization = class _TransparentAuthorization extends Authoriza
|
|
|
6873
6792
|
};
|
|
6874
6793
|
|
|
6875
6794
|
// libs/auth/src/utils/authorization-id.utils.ts
|
|
6876
|
-
var
|
|
6795
|
+
var import_utils39 = require("@frontmcp/utils");
|
|
6877
6796
|
function deriveAuthorizationId(token) {
|
|
6878
6797
|
const parts = token.split(".");
|
|
6879
6798
|
const signature = parts[2] || token;
|
|
6880
|
-
return (0,
|
|
6799
|
+
return (0, import_utils39.sha256Hex)(signature).substring(0, 16);
|
|
6881
6800
|
}
|
|
6882
6801
|
|
|
6883
6802
|
// libs/auth/src/authorization/orchestrated.authorization.ts
|
|
@@ -7246,7 +7165,7 @@ var OrchestratedAuthAccessorAdapter = class {
|
|
|
7246
7165
|
};
|
|
7247
7166
|
|
|
7248
7167
|
// libs/auth/src/authorization/dcr-client.registry.ts
|
|
7249
|
-
var
|
|
7168
|
+
var import_utils40 = require("@frontmcp/utils");
|
|
7250
7169
|
var DEFAULT_MAX_DYNAMIC_CLIENTS = 1e3;
|
|
7251
7170
|
function globMatch(pattern, input) {
|
|
7252
7171
|
let p = 0;
|
|
@@ -7372,7 +7291,7 @@ var DcrClientRegistry = class {
|
|
|
7372
7291
|
if (!isConfidential) return "ok";
|
|
7373
7292
|
if (!client.client_secret) return "invalid";
|
|
7374
7293
|
if (!presentedSecret) return "invalid";
|
|
7375
|
-
return (0,
|
|
7294
|
+
return (0, import_utils40.timingSafeEqual)((0, import_utils40.sha256)(presentedSecret), (0, import_utils40.sha256)(client.client_secret)) ? "ok" : "invalid";
|
|
7376
7295
|
}
|
|
7377
7296
|
/** Whether a client id is known to the registry. */
|
|
7378
7297
|
has(clientId) {
|
|
@@ -7460,7 +7379,7 @@ var DcrClientRegistry = class {
|
|
|
7460
7379
|
if (typeof presented !== "string" || presented.length === 0) {
|
|
7461
7380
|
return false;
|
|
7462
7381
|
}
|
|
7463
|
-
return (0,
|
|
7382
|
+
return (0, import_utils40.timingSafeEqual)((0, import_utils40.sha256)(presented), (0, import_utils40.sha256)(expected));
|
|
7464
7383
|
}
|
|
7465
7384
|
};
|
|
7466
7385
|
|
|
@@ -7789,6 +7708,7 @@ var cimdConfigSchema = import_lazy_zod9.z.object({
|
|
|
7789
7708
|
init_cimd_cache();
|
|
7790
7709
|
|
|
7791
7710
|
// libs/auth/src/cimd/cimd.service.ts
|
|
7711
|
+
var import_utils41 = require("@frontmcp/utils");
|
|
7792
7712
|
init_cimd_cache();
|
|
7793
7713
|
var CimdService = class {
|
|
7794
7714
|
config;
|
|
@@ -7963,7 +7883,7 @@ var CimdService = class {
|
|
|
7963
7883
|
httpStatus: 304
|
|
7964
7884
|
});
|
|
7965
7885
|
}
|
|
7966
|
-
if (
|
|
7886
|
+
if ((0, import_utils41.isRedirectResponse)(response)) {
|
|
7967
7887
|
const location = response.headers.get("location");
|
|
7968
7888
|
if (!location) {
|
|
7969
7889
|
throw new CimdFetchError(clientId, "Redirect response missing Location header", {
|
|
@@ -9548,7 +9468,7 @@ var CredentialCache = class {
|
|
|
9548
9468
|
var AUTH_PROVIDERS_ACCESSOR = /* @__PURE__ */ Symbol.for("frontmcp:AUTH_PROVIDERS_ACCESSOR");
|
|
9549
9469
|
|
|
9550
9470
|
// libs/auth/src/vault/auth-providers.accessor.impl.ts
|
|
9551
|
-
var
|
|
9471
|
+
var import_utils44 = require("@frontmcp/utils");
|
|
9552
9472
|
var AuthProvidersAccessorImpl = class {
|
|
9553
9473
|
constructor(registry, vault, cache, loader, context, logger) {
|
|
9554
9474
|
this.registry = registry;
|
|
@@ -9763,7 +9683,7 @@ var AuthProvidersAccessorImpl = class {
|
|
|
9763
9683
|
return { [credential.headerName]: credential.key };
|
|
9764
9684
|
case "basic": {
|
|
9765
9685
|
const value = `${credential.username}:${credential.password}`;
|
|
9766
|
-
const encoded = credential.encodedValue ?? (0,
|
|
9686
|
+
const encoded = credential.encodedValue ?? (0, import_utils44.base64Encode)(new TextEncoder().encode(value));
|
|
9767
9687
|
return { Authorization: `Basic ${encoded}` };
|
|
9768
9688
|
}
|
|
9769
9689
|
case "custom":
|
|
@@ -10351,6 +10271,18 @@ var noopRelationshipResolver = {
|
|
|
10351
10271
|
return false;
|
|
10352
10272
|
}
|
|
10353
10273
|
};
|
|
10274
|
+
function resolveAuthUser(authInfo) {
|
|
10275
|
+
const extraUser = authInfo?.extra?.["user"];
|
|
10276
|
+
if (authInfo?.user) return authInfo.user;
|
|
10277
|
+
return typeof extraUser === "object" && extraUser !== null ? extraUser : {};
|
|
10278
|
+
}
|
|
10279
|
+
function isAnonymousSubject(sub) {
|
|
10280
|
+
return typeof sub !== "string" || sub === "" || sub.startsWith("anon:");
|
|
10281
|
+
}
|
|
10282
|
+
function signedInSubject(rawSub, mappedSub) {
|
|
10283
|
+
if (rawSub !== void 0 && isAnonymousSubject(rawSub)) return void 0;
|
|
10284
|
+
return [mappedSub, rawSub].find((candidate) => !isAnonymousSubject(candidate));
|
|
10285
|
+
}
|
|
10354
10286
|
var AuthoritiesContextBuilder = class {
|
|
10355
10287
|
claimsMapping;
|
|
10356
10288
|
claimsResolver;
|
|
@@ -10368,7 +10300,7 @@ var AuthoritiesContextBuilder = class {
|
|
|
10368
10300
|
const resolved = this.claimsResolver(authInfo);
|
|
10369
10301
|
return {
|
|
10370
10302
|
user: {
|
|
10371
|
-
sub: authInfo
|
|
10303
|
+
sub: signedInSubject(resolveAuthUser(authInfo).sub),
|
|
10372
10304
|
roles: resolved.roles,
|
|
10373
10305
|
permissions: resolved.permissions,
|
|
10374
10306
|
claims: resolved.claims
|
|
@@ -10378,7 +10310,7 @@ var AuthoritiesContextBuilder = class {
|
|
|
10378
10310
|
relationships: this.relationshipResolver
|
|
10379
10311
|
};
|
|
10380
10312
|
}
|
|
10381
|
-
const user = authInfo
|
|
10313
|
+
const user = resolveAuthUser(authInfo);
|
|
10382
10314
|
const authorization = authInfo?.extra?.["authorization"];
|
|
10383
10315
|
const authorizationClaims = authorization?.["claims"] ?? {};
|
|
10384
10316
|
const rawClaims = {
|
|
@@ -10399,10 +10331,10 @@ var AuthoritiesContextBuilder = class {
|
|
|
10399
10331
|
} else {
|
|
10400
10332
|
permissions = toStringArray(user["permissions"] ?? []);
|
|
10401
10333
|
}
|
|
10402
|
-
const
|
|
10334
|
+
const mappedSub = this.claimsMapping?.userId ? resolveDotPath(rawClaims, this.claimsMapping.userId) : void 0;
|
|
10403
10335
|
return {
|
|
10404
10336
|
user: {
|
|
10405
|
-
sub,
|
|
10337
|
+
sub: signedInSubject(user.sub, mappedSub),
|
|
10406
10338
|
roles,
|
|
10407
10339
|
permissions,
|
|
10408
10340
|
claims: rawClaims
|
|
@@ -10434,15 +10366,17 @@ var FrontMcpAuthContextImpl = class {
|
|
|
10434
10366
|
roles;
|
|
10435
10367
|
permissions;
|
|
10436
10368
|
constructor(source, claimsMapping) {
|
|
10437
|
-
const rawUser = source
|
|
10438
|
-
const
|
|
10369
|
+
const rawUser = resolveAuthUser(source);
|
|
10370
|
+
const mappedSub = claimsMapping?.userId ? resolveDotPath(this.buildRawClaims(source), claimsMapping.userId) : void 0;
|
|
10371
|
+
const signedInSub = signedInSubject(rawUser.sub, mappedSub);
|
|
10372
|
+
const sub = signedInSub ?? (typeof rawUser.sub === "string" ? rawUser.sub : "");
|
|
10439
10373
|
this.user = Object.freeze({
|
|
10440
10374
|
sub,
|
|
10441
10375
|
name: rawUser.name,
|
|
10442
10376
|
email: rawUser.email,
|
|
10443
10377
|
picture: rawUser.picture
|
|
10444
10378
|
});
|
|
10445
|
-
this.isAnonymous =
|
|
10379
|
+
this.isAnonymous = signedInSub === void 0;
|
|
10446
10380
|
const extraAuth = source.extra?.["authorization"];
|
|
10447
10381
|
this.mode = String(
|
|
10448
10382
|
source.extra?.["authMode"] ?? extraAuth?.["mode"] ?? (source.extra?.["isPublic"] === true || rawUser["anonymous"] === true ? "public" : "authenticated")
|
|
@@ -10500,7 +10434,7 @@ var FrontMcpAuthContextImpl = class {
|
|
|
10500
10434
|
* Mirrors the logic in AuthoritiesContextBuilder.build().
|
|
10501
10435
|
*/
|
|
10502
10436
|
buildRawClaims(source) {
|
|
10503
|
-
const rawUser = source
|
|
10437
|
+
const rawUser = resolveAuthUser(source);
|
|
10504
10438
|
const extraAuth = source.extra?.["authorization"];
|
|
10505
10439
|
const authorizationClaims = extraAuth?.["claims"] ?? {};
|
|
10506
10440
|
return {
|
|
@@ -10719,8 +10653,9 @@ function resolveValue(value, ctx) {
|
|
|
10719
10653
|
}
|
|
10720
10654
|
function applyOperator(actual, op, expected) {
|
|
10721
10655
|
switch (op) {
|
|
10656
|
+
// A missing expected value (e.g. an absent fromInput) never matches, even an absent actual value
|
|
10722
10657
|
case "eq":
|
|
10723
|
-
return actual === expected;
|
|
10658
|
+
return expected !== void 0 && actual === expected;
|
|
10724
10659
|
case "neq":
|
|
10725
10660
|
return actual !== expected;
|
|
10726
10661
|
case "in":
|
|
@@ -10769,7 +10704,7 @@ function evaluateAbac(policy, ctx) {
|
|
|
10769
10704
|
for (const [path, expectedRaw] of Object.entries(policy.match)) {
|
|
10770
10705
|
const actual = resolveDotPath(envelope, path);
|
|
10771
10706
|
const expected = resolveValue(expectedRaw, ctx);
|
|
10772
|
-
if (actual !== expected) {
|
|
10707
|
+
if (expected === void 0 || actual !== expected) {
|
|
10773
10708
|
return {
|
|
10774
10709
|
granted: false,
|
|
10775
10710
|
deniedBy: `attributes.match: '${path}' expected '${String(expected)}' but got '${String(actual)}'`,
|
|
@@ -10809,6 +10744,15 @@ function resolveResourceId(ref, ctx) {
|
|
|
10809
10744
|
return void 0;
|
|
10810
10745
|
}
|
|
10811
10746
|
async function evaluateSingleRebac(policy, ctx) {
|
|
10747
|
+
const userSub = ctx.user.sub;
|
|
10748
|
+
if (userSub === void 0 || isAnonymousSubject(userSub)) {
|
|
10749
|
+
return {
|
|
10750
|
+
granted: false,
|
|
10751
|
+
deniedBy: `relationships: an anonymous caller cannot be '${policy.type}' of ${policy.resource}`,
|
|
10752
|
+
denial: { kind: "relationships", path: "user.sub", expected: "a signed-in caller", actual: void 0 },
|
|
10753
|
+
evaluatedPolicies: ["relationships"]
|
|
10754
|
+
};
|
|
10755
|
+
}
|
|
10812
10756
|
const resourceId = resolveResourceId(policy.resourceId, ctx);
|
|
10813
10757
|
if (resourceId === void 0) {
|
|
10814
10758
|
return {
|
|
@@ -10823,11 +10767,11 @@ async function evaluateSingleRebac(policy, ctx) {
|
|
|
10823
10767
|
evaluatedPolicies: ["relationships"]
|
|
10824
10768
|
};
|
|
10825
10769
|
}
|
|
10826
|
-
const hasRelationship = await ctx.relationships.check(policy.type, policy.resource, resourceId,
|
|
10770
|
+
const hasRelationship = await ctx.relationships.check(policy.type, policy.resource, resourceId, userSub, ctx);
|
|
10827
10771
|
if (!hasRelationship) {
|
|
10828
10772
|
return {
|
|
10829
10773
|
granted: false,
|
|
10830
|
-
deniedBy: `relationships: user '${
|
|
10774
|
+
deniedBy: `relationships: user '${userSub}' is not '${policy.type}' of ${policy.resource}:${resourceId}`,
|
|
10831
10775
|
denial: {
|
|
10832
10776
|
kind: "relationships",
|
|
10833
10777
|
path: "relationships",
|
|
@@ -11501,6 +11445,7 @@ var FetchCredentialMiddleware = class {
|
|
|
11501
11445
|
headerApplier,
|
|
11502
11446
|
hkdfSha256,
|
|
11503
11447
|
incrementalAuthConfigSchema,
|
|
11448
|
+
isAnonymousSubject,
|
|
11504
11449
|
isCimdClientId,
|
|
11505
11450
|
isFrontMcpCredentials,
|
|
11506
11451
|
isJwt,
|