@frontmcp/auth 1.7.2 → 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/detection/auth-provider-detection.d.ts +2 -0
- package/detection/auth-provider-detection.d.ts.map +1 -1
- package/errors/auth-internal.errors.d.ts +12 -0
- package/errors/auth-internal.errors.d.ts.map +1 -1
- package/errors/index.d.ts +1 -1
- package/errors/index.d.ts.map +1 -1
- package/esm/index.mjs +306 -247
- package/esm/package.json +5 -5
- package/index.d.ts +1 -1
- package/index.d.ts.map +1 -1
- package/index.js +459 -396
- package/jwks/jwks.service.d.ts.map +1 -1
- package/options/index.d.ts +4 -2
- package/options/index.d.ts.map +1 -1
- package/options/interfaces.d.ts +20 -2
- package/options/interfaces.d.ts.map +1 -1
- package/options/schema.d.ts +13 -1
- package/options/schema.d.ts.map +1 -1
- package/options/static.schema.d.ts +27 -0
- package/options/static.schema.d.ts.map +1 -0
- package/options/typecheck.d.ts.map +1 -1
- package/options/utils.d.ts +7 -2
- package/options/utils.d.ts.map +1 -1
- package/package.json +5 -5
- package/session/authorization-vault.d.ts +1 -1
package/esm/index.mjs
CHANGED
|
@@ -225,10 +225,14 @@ import {
|
|
|
225
225
|
bytesToHex,
|
|
226
226
|
createKeyPersistence,
|
|
227
227
|
isProduction,
|
|
228
|
+
isRedirectResponse,
|
|
228
229
|
randomBytes,
|
|
229
230
|
rsaVerify
|
|
230
231
|
} from "@frontmcp/utils";
|
|
231
232
|
|
|
233
|
+
// libs/auth/src/cimd/cimd.validator.ts
|
|
234
|
+
import { classifyIpAddress } from "@frontmcp/utils";
|
|
235
|
+
|
|
232
236
|
// libs/auth/src/cimd/cimd.errors.ts
|
|
233
237
|
var CimdError = class extends Error {
|
|
234
238
|
/**
|
|
@@ -492,70 +496,38 @@ function isIpAddress(hostname) {
|
|
|
492
496
|
}
|
|
493
497
|
return false;
|
|
494
498
|
}
|
|
499
|
+
var BLOCKED_RANGE_REASONS = /* @__PURE__ */ new Map([
|
|
500
|
+
["0.0.0.0/8", "Current network addresses (0.x.x.x) are not allowed"],
|
|
501
|
+
["10.0.0.0/8", "Private IP addresses (10.x.x.x) are not allowed"],
|
|
502
|
+
["100.64.0.0/10", "Carrier-grade NAT addresses (100.64-127.x.x) are not allowed"],
|
|
503
|
+
["127.0.0.0/8", "Loopback addresses (127.x.x.x) are not allowed"],
|
|
504
|
+
["169.254.0.0/16", "Link-local addresses (169.254.x.x) are not allowed"],
|
|
505
|
+
["172.16.0.0/12", "Private IP addresses (172.16-31.x.x) are not allowed"],
|
|
506
|
+
["192.0.0.0/24", "IETF protocol-assignment addresses (192.0.0.0/24) are not allowed"],
|
|
507
|
+
["192.168.0.0/16", "Private IP addresses (192.168.x.x) are not allowed"],
|
|
508
|
+
["224.0.0.0/4", "Multicast addresses are not allowed"],
|
|
509
|
+
["255.255.255.255/32", "Broadcast address is not allowed"],
|
|
510
|
+
["::/128", "IPv6 unspecified address (::) is not allowed"],
|
|
511
|
+
["::1/128", "IPv6 loopback address (::1) is not allowed"],
|
|
512
|
+
["fc00::/7", "IPv6 unique local addresses (fc00::/7) are not allowed"],
|
|
513
|
+
["fe80::/10", "IPv6 link-local addresses (fe80::/10) are not allowed"]
|
|
514
|
+
]);
|
|
495
515
|
function checkIpAddress(ip) {
|
|
496
|
-
const
|
|
497
|
-
if (
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
const parts = ip.split(".").map(Number);
|
|
504
|
-
if (parts.length !== 4 || parts.some((p) => isNaN(p) || p < 0 || p > 255)) {
|
|
505
|
-
return { allowed: false, reason: "Invalid IPv4 address" };
|
|
506
|
-
}
|
|
507
|
-
const [a, b, c, d] = parts;
|
|
508
|
-
if (a === 127) {
|
|
509
|
-
return { allowed: false, reason: "Loopback addresses (127.x.x.x) are not allowed" };
|
|
510
|
-
}
|
|
511
|
-
if (a === 10) {
|
|
512
|
-
return { allowed: false, reason: "Private IP addresses (10.x.x.x) are not allowed" };
|
|
513
|
-
}
|
|
514
|
-
if (a === 172 && b >= 16 && b <= 31) {
|
|
515
|
-
return { allowed: false, reason: "Private IP addresses (172.16-31.x.x) are not allowed" };
|
|
516
|
-
}
|
|
517
|
-
if (a === 192 && b === 168) {
|
|
518
|
-
return { allowed: false, reason: "Private IP addresses (192.168.x.x) are not allowed" };
|
|
519
|
-
}
|
|
520
|
-
if (a === 169 && b === 254) {
|
|
521
|
-
return { allowed: false, reason: "Link-local addresses (169.254.x.x) are not allowed" };
|
|
522
|
-
}
|
|
523
|
-
if (a === 100 && b >= 64 && b <= 127) {
|
|
524
|
-
return { allowed: false, reason: "Carrier-grade NAT addresses (100.64-127.x.x) are not allowed" };
|
|
525
|
-
}
|
|
526
|
-
if (a === 192 && b === 0 && c === 0) {
|
|
527
|
-
return { allowed: false, reason: "IETF protocol-assignment addresses (192.0.0.0/24) are not allowed" };
|
|
528
|
-
}
|
|
529
|
-
if (a === 0) {
|
|
530
|
-
return { allowed: false, reason: "Current network addresses (0.x.x.x) are not allowed" };
|
|
531
|
-
}
|
|
532
|
-
if (a === 255 && b === 255 && c === 255 && d === 255) {
|
|
533
|
-
return { allowed: false, reason: "Broadcast address is not allowed" };
|
|
534
|
-
}
|
|
535
|
-
if (a >= 224 && a <= 239) {
|
|
536
|
-
return { allowed: false, reason: "Multicast addresses are not allowed" };
|
|
537
|
-
}
|
|
538
|
-
return { allowed: true, reason: "" };
|
|
539
|
-
}
|
|
540
|
-
function checkIpv6(ip) {
|
|
541
|
-
const normalizedIp = ip.toLowerCase();
|
|
542
|
-
if (normalizedIp === "::1") {
|
|
543
|
-
return { allowed: false, reason: "IPv6 loopback address (::1) is not allowed" };
|
|
544
|
-
}
|
|
545
|
-
if (normalizedIp === "::" || normalizedIp === "0:0:0:0:0:0:0:0") {
|
|
546
|
-
return { allowed: false, reason: "IPv6 unspecified address (::) is not allowed" };
|
|
547
|
-
}
|
|
548
|
-
if (normalizedIp.startsWith("fe8") || normalizedIp.startsWith("fe9") || normalizedIp.startsWith("fea") || normalizedIp.startsWith("feb")) {
|
|
549
|
-
return { allowed: false, reason: "IPv6 link-local addresses (fe80::/10) are not allowed" };
|
|
550
|
-
}
|
|
551
|
-
if (normalizedIp.startsWith("fc") || normalizedIp.startsWith("fd")) {
|
|
552
|
-
return { allowed: false, reason: "IPv6 unique local addresses (fc00::/7) are not allowed" };
|
|
516
|
+
const classification = classifyIpAddress(ip);
|
|
517
|
+
if (!classification) {
|
|
518
|
+
const bareIp = ip.replace(/^\[|\]$/g, "");
|
|
519
|
+
return {
|
|
520
|
+
allowed: false,
|
|
521
|
+
reason: bareIp.includes(":") ? `Malformed IPv6 address: ${bareIp}` : "Invalid IPv4 address"
|
|
522
|
+
};
|
|
553
523
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
return checkIpv4(ipv4MappedMatch[1]);
|
|
524
|
+
if (classification.range === "public") {
|
|
525
|
+
return { allowed: true, reason: "" };
|
|
557
526
|
}
|
|
558
|
-
return {
|
|
527
|
+
return {
|
|
528
|
+
allowed: false,
|
|
529
|
+
reason: BLOCKED_RANGE_REASONS.get(classification.cidr) ?? `Addresses in the ${classification.range} range (${classification.cidr}) are not allowed`
|
|
530
|
+
};
|
|
559
531
|
}
|
|
560
532
|
function isDomainInList(hostname, domainList) {
|
|
561
533
|
const lowerHostname = hostname.toLowerCase();
|
|
@@ -964,11 +936,10 @@ var JwksService = class {
|
|
|
964
936
|
// Follow redirects manually so each hop is re-validated (scheme + SSRF).
|
|
965
937
|
redirect: "manual"
|
|
966
938
|
});
|
|
967
|
-
if (res
|
|
939
|
+
if (isRedirectResponse(res)) {
|
|
968
940
|
const location = res.headers.get("location");
|
|
969
|
-
if (!location
|
|
970
|
-
|
|
971
|
-
}
|
|
941
|
+
if (!location) throw new Error("redirect without a readable Location header");
|
|
942
|
+
if (hop >= MAX_JWKS_FETCH_HOPS) throw new Error("too many redirects");
|
|
972
943
|
currentUrl = new URL(location, currentUrl).toString();
|
|
973
944
|
continue;
|
|
974
945
|
}
|
|
@@ -3544,6 +3515,22 @@ var SessionSecretRequiredError = class extends AuthInternalError {
|
|
|
3544
3515
|
super(`Session secret is required for ${component}`, "SESSION_SECRET_REQUIRED");
|
|
3545
3516
|
}
|
|
3546
3517
|
};
|
|
3518
|
+
var JwtSecretRequiredError = class extends AuthInternalError {
|
|
3519
|
+
constructor(mode) {
|
|
3520
|
+
super(
|
|
3521
|
+
`JWT_SECRET is required in production for auth.mode "${mode}". Without it the server signs tokens with a random per-process secret, so every restart invalidates outstanding tokens and any second instance or isolate rejects tokens the first one minted.`,
|
|
3522
|
+
"JWT_SECRET_REQUIRED"
|
|
3523
|
+
);
|
|
3524
|
+
}
|
|
3525
|
+
};
|
|
3526
|
+
var JwtSecretWeakError = class extends AuthInternalError {
|
|
3527
|
+
constructor(byteLength) {
|
|
3528
|
+
super(
|
|
3529
|
+
`JWT_SECRET is ${byteLength} bytes; HS256 requires at least 32 (RFC 7518 \xA73.2). A shorter secret is guessable, and whoever guesses it can forge tokens for this server. Generate one with \`openssl rand -hex 32\`.`,
|
|
3530
|
+
"JWT_SECRET_INVALID"
|
|
3531
|
+
);
|
|
3532
|
+
}
|
|
3533
|
+
};
|
|
3547
3534
|
var CredentialProviderAlreadyRegisteredError = class extends AuthInternalError {
|
|
3548
3535
|
constructor(name) {
|
|
3549
3536
|
super(`Credential provider "${name}" is already registered`, "CREDENTIAL_PROVIDER_ALREADY_REGISTERED");
|
|
@@ -7420,6 +7407,7 @@ init_cimd_cache();
|
|
|
7420
7407
|
|
|
7421
7408
|
// libs/auth/src/cimd/cimd.service.ts
|
|
7422
7409
|
init_cimd_cache();
|
|
7410
|
+
import { isRedirectResponse as isRedirectResponse2 } from "@frontmcp/utils";
|
|
7423
7411
|
var CimdService = class {
|
|
7424
7412
|
config;
|
|
7425
7413
|
cacheConfig;
|
|
@@ -7593,7 +7581,7 @@ var CimdService = class {
|
|
|
7593
7581
|
httpStatus: 304
|
|
7594
7582
|
});
|
|
7595
7583
|
}
|
|
7596
|
-
if (response
|
|
7584
|
+
if (isRedirectResponse2(response)) {
|
|
7597
7585
|
const location = response.headers.get("location");
|
|
7598
7586
|
if (!location) {
|
|
7599
7587
|
throw new CimdFetchError(clientId, "Redirect response missing Location header", {
|
|
@@ -8210,10 +8198,48 @@ var publicAuthOptionsSchema = z11.object({
|
|
|
8210
8198
|
signKey: jwkSchema.or(z11.instanceof(Uint8Array)).optional()
|
|
8211
8199
|
});
|
|
8212
8200
|
|
|
8213
|
-
// libs/auth/src/options/
|
|
8201
|
+
// libs/auth/src/options/static.schema.ts
|
|
8214
8202
|
import { z as z12 } from "@frontmcp/lazy-zod";
|
|
8215
|
-
var
|
|
8216
|
-
mode: z12.literal("
|
|
8203
|
+
var staticAuthOptionsSchema = z12.object({
|
|
8204
|
+
mode: z12.literal("static"),
|
|
8205
|
+
/**
|
|
8206
|
+
* Accepted credentials. Compared against the presented one in constant time,
|
|
8207
|
+
* over SHA-256 digests so neither the value nor its length leaks by timing.
|
|
8208
|
+
*
|
|
8209
|
+
* Read these from the environment — never commit them.
|
|
8210
|
+
*/
|
|
8211
|
+
tokens: z12.array(z12.string().min(1)).min(1),
|
|
8212
|
+
/**
|
|
8213
|
+
* Request header carrying the credential.
|
|
8214
|
+
* @default 'authorization'
|
|
8215
|
+
*/
|
|
8216
|
+
header: z12.string().min(1).default("authorization"),
|
|
8217
|
+
/**
|
|
8218
|
+
* Scheme prefix to strip before comparing, matched case-insensitively. Set to
|
|
8219
|
+
* an empty string for headers that carry a bare token (e.g. `x-api-key`).
|
|
8220
|
+
* @default 'Bearer'
|
|
8221
|
+
*/
|
|
8222
|
+
scheme: z12.string().default("Bearer"),
|
|
8223
|
+
/**
|
|
8224
|
+
* Scopes granted to a request that presents a valid token.
|
|
8225
|
+
* @default ['static']
|
|
8226
|
+
*/
|
|
8227
|
+
scopes: z12.array(z12.string()).default(["static"]),
|
|
8228
|
+
/**
|
|
8229
|
+
* Realm reported in the `WWW-Authenticate` challenge on failure.
|
|
8230
|
+
* @default 'mcp'
|
|
8231
|
+
*/
|
|
8232
|
+
realm: z12.string().default("mcp"),
|
|
8233
|
+
/**
|
|
8234
|
+
* Tool/prompt access configuration, same shape the other modes accept.
|
|
8235
|
+
*/
|
|
8236
|
+
publicAccess: publicAccessConfigSchema.optional()
|
|
8237
|
+
});
|
|
8238
|
+
|
|
8239
|
+
// libs/auth/src/options/transparent.schema.ts
|
|
8240
|
+
import { z as z13 } from "@frontmcp/lazy-zod";
|
|
8241
|
+
var transparentAuthOptionsSchema = z13.object({
|
|
8242
|
+
mode: z13.literal("transparent"),
|
|
8217
8243
|
/**
|
|
8218
8244
|
* Flattened remote provider fields (provider, clientId, clientSecret, scopes, providerConfig)
|
|
8219
8245
|
*/
|
|
@@ -8222,28 +8248,28 @@ var transparentAuthOptionsSchema = z12.object({
|
|
|
8222
8248
|
* Expected token audience
|
|
8223
8249
|
* If not set, defaults to the resource URL
|
|
8224
8250
|
*/
|
|
8225
|
-
expectedAudience:
|
|
8251
|
+
expectedAudience: z13.union([z13.string(), z13.array(z13.string())]).optional(),
|
|
8226
8252
|
/**
|
|
8227
8253
|
* Require an `aud` claim (reject audience-less tokens). Defaults to `false`
|
|
8228
8254
|
* for IdP compatibility; set `true` to reject tokens with no audience.
|
|
8229
8255
|
*/
|
|
8230
|
-
requireAudience:
|
|
8256
|
+
requireAudience: z13.boolean().optional(),
|
|
8231
8257
|
/**
|
|
8232
8258
|
* Required scopes for access
|
|
8233
8259
|
* Empty array means any valid token is accepted
|
|
8234
8260
|
* @default []
|
|
8235
8261
|
*/
|
|
8236
|
-
requiredScopes:
|
|
8262
|
+
requiredScopes: z13.array(z13.string()).default([]),
|
|
8237
8263
|
/**
|
|
8238
8264
|
* Allow anonymous fallback when no token is provided
|
|
8239
8265
|
* @default false
|
|
8240
8266
|
*/
|
|
8241
|
-
allowAnonymous:
|
|
8267
|
+
allowAnonymous: z13.boolean().default(false),
|
|
8242
8268
|
/**
|
|
8243
8269
|
* Scopes granted to anonymous sessions (when allowAnonymous=true)
|
|
8244
8270
|
* @default ['anonymous']
|
|
8245
8271
|
*/
|
|
8246
|
-
anonymousScopes:
|
|
8272
|
+
anonymousScopes: z13.array(z13.string()).default(["anonymous"]),
|
|
8247
8273
|
/**
|
|
8248
8274
|
* Public access config for anonymous users (when allowAnonymous=true)
|
|
8249
8275
|
*/
|
|
@@ -8251,13 +8277,13 @@ var transparentAuthOptionsSchema = z12.object({
|
|
|
8251
8277
|
});
|
|
8252
8278
|
|
|
8253
8279
|
// libs/auth/src/options/orchestrated.schema.ts
|
|
8254
|
-
import { z as
|
|
8255
|
-
var authSlotSchema =
|
|
8256
|
-
var authUiMapSchema =
|
|
8257
|
-
var authExtraHandlerSchema =
|
|
8280
|
+
import { z as z14 } from "@frontmcp/lazy-zod";
|
|
8281
|
+
var authSlotSchema = z14.enum(["login", "consent", "incremental", "federated", "error"]);
|
|
8282
|
+
var authUiMapSchema = z14.partialRecord(authSlotSchema, z14.string().min(1));
|
|
8283
|
+
var authExtraHandlerSchema = z14.custom((v) => typeof v === "function", {
|
|
8258
8284
|
message: "auth.extras entries must be handler functions (input, ctx) => AuthExtraResult."
|
|
8259
8285
|
});
|
|
8260
|
-
var authExtrasMapSchema =
|
|
8286
|
+
var authExtrasMapSchema = z14.record(z14.string().min(1), authExtraHandlerSchema);
|
|
8261
8287
|
var sharedAuthFields = {
|
|
8262
8288
|
local: localSigningConfigSchema.optional(),
|
|
8263
8289
|
tokenStorage: tokenStorageConfigSchema.default("memory"),
|
|
@@ -8265,13 +8291,13 @@ var sharedAuthFields = {
|
|
|
8265
8291
|
// Optional (no default) so an unset value resolves to the in-memory encrypted
|
|
8266
8292
|
// default at runtime; existing configs are unaffected.
|
|
8267
8293
|
secureStore: secureStoreConfigSchema.optional(),
|
|
8268
|
-
allowDefaultPublic:
|
|
8269
|
-
anonymousScopes:
|
|
8294
|
+
allowDefaultPublic: z14.boolean().default(false),
|
|
8295
|
+
anonymousScopes: z14.array(z14.string()).default(["anonymous"]),
|
|
8270
8296
|
publicAccess: publicAccessConfigSchema.optional(),
|
|
8271
8297
|
consent: consentConfigSchema.optional(),
|
|
8272
8298
|
federatedAuth: federatedAuthConfigSchema.optional(),
|
|
8273
8299
|
refresh: tokenRefreshConfigSchema.optional(),
|
|
8274
|
-
expectedAudience:
|
|
8300
|
+
expectedAudience: z14.union([z14.string(), z14.array(z14.string())]).optional(),
|
|
8275
8301
|
incrementalAuth: incrementalAuthConfigSchema.optional(),
|
|
8276
8302
|
/**
|
|
8277
8303
|
* Require the OAuth client to be KNOWN (registered via DCR / pre-registered,
|
|
@@ -8287,46 +8313,46 @@ var sharedAuthFields = {
|
|
|
8287
8313
|
* matching for every client. Clients on a configured
|
|
8288
8314
|
* `dcr.allowedRedirectUris` allowlist still pass.
|
|
8289
8315
|
*/
|
|
8290
|
-
requireRegisteredClients:
|
|
8316
|
+
requireRegisteredClients: z14.boolean().default(false),
|
|
8291
8317
|
cimd: cimdConfigSchema.optional(),
|
|
8292
8318
|
// #469 — custom auth UI as a slot→file map + extras name→handler map (per-app
|
|
8293
8319
|
// under splitByApp). Both optional; omitting them serves the built-in pages.
|
|
8294
8320
|
ui: authUiMapSchema.optional(),
|
|
8295
8321
|
extras: authExtrasMapSchema.optional()
|
|
8296
8322
|
};
|
|
8297
|
-
var loginFieldSchema =
|
|
8298
|
-
type:
|
|
8299
|
-
label:
|
|
8300
|
-
required:
|
|
8301
|
-
placeholder:
|
|
8302
|
-
options:
|
|
8323
|
+
var loginFieldSchema = z14.object({
|
|
8324
|
+
type: z14.enum(["text", "password", "email", "select", "hidden"]),
|
|
8325
|
+
label: z14.string().optional(),
|
|
8326
|
+
required: z14.boolean().optional(),
|
|
8327
|
+
placeholder: z14.string().optional(),
|
|
8328
|
+
options: z14.array(z14.object({ value: z14.string(), label: z14.string() })).optional()
|
|
8303
8329
|
});
|
|
8304
|
-
var loginConfigSchema =
|
|
8305
|
-
title:
|
|
8306
|
-
subtitle:
|
|
8307
|
-
logoUri:
|
|
8308
|
-
fields:
|
|
8330
|
+
var loginConfigSchema = z14.object({
|
|
8331
|
+
title: z14.string().optional(),
|
|
8332
|
+
subtitle: z14.string().optional(),
|
|
8333
|
+
logoUri: z14.string().optional(),
|
|
8334
|
+
fields: z14.record(z14.string(), loginFieldSchema).optional(),
|
|
8309
8335
|
// Full HTML override — a function, validated structurally like other callbacks
|
|
8310
8336
|
// in the codebase (see http/cors handlers using z.custom<…>).
|
|
8311
|
-
render:
|
|
8337
|
+
render: z14.custom((v) => typeof v === "function", {
|
|
8312
8338
|
message: "login.render must be a function"
|
|
8313
8339
|
}).optional(),
|
|
8314
|
-
subject:
|
|
8315
|
-
fromField:
|
|
8316
|
-
strategy:
|
|
8340
|
+
subject: z14.object({
|
|
8341
|
+
fromField: z14.string().optional(),
|
|
8342
|
+
strategy: z14.enum(["per-session", "per-account"]).optional()
|
|
8317
8343
|
}).optional()
|
|
8318
8344
|
});
|
|
8319
|
-
var authenticateFnSchema =
|
|
8345
|
+
var authenticateFnSchema = z14.custom((v) => typeof v === "function", {
|
|
8320
8346
|
message: "authenticate must be a function"
|
|
8321
8347
|
});
|
|
8322
|
-
var localAuthSchema =
|
|
8323
|
-
mode:
|
|
8348
|
+
var localAuthSchema = z14.object({
|
|
8349
|
+
mode: z14.literal("local"),
|
|
8324
8350
|
...sharedAuthFields,
|
|
8325
8351
|
// #468 — opt-out of the email requirement at /oauth/callback for
|
|
8326
8352
|
// single-operator local setups. Defaults preserve the historical behavior
|
|
8327
8353
|
// (email required) so existing configs are unaffected.
|
|
8328
|
-
requireEmail:
|
|
8329
|
-
anonymousSubject:
|
|
8354
|
+
requireEmail: z14.boolean().default(true),
|
|
8355
|
+
anonymousSubject: z14.string().min(1).default("local-operator"),
|
|
8330
8356
|
// Checkpoint 3a — declarative login customization + custom verification.
|
|
8331
8357
|
// Both optional; omitting them preserves the default login path exactly.
|
|
8332
8358
|
login: loginConfigSchema.optional(),
|
|
@@ -8334,7 +8360,7 @@ var localAuthSchema = z13.object({
|
|
|
8334
8360
|
// Multi-OAuth-provider orchestration — declare upstream providers (GitHub,
|
|
8335
8361
|
// Slack, Jira, …) to federate at /oauth/authorize. Optional; omitting it
|
|
8336
8362
|
// preserves the default single-operator local login exactly.
|
|
8337
|
-
providers:
|
|
8363
|
+
providers: z14.array(upstreamProviderSchema).optional(),
|
|
8338
8364
|
// Local-AS Dynamic Client Registration control surface (#462) — gate
|
|
8339
8365
|
// `POST /oauth/register`, allowlist redirect_uris/client_ids, require an
|
|
8340
8366
|
// initial access token, and seed pre-registered trusted clients. Optional;
|
|
@@ -8342,8 +8368,8 @@ var localAuthSchema = z13.object({
|
|
|
8342
8368
|
// no allowlist; no initial access token).
|
|
8343
8369
|
dcr: localDcrConfigSchema.optional()
|
|
8344
8370
|
});
|
|
8345
|
-
var remoteAuthSchema =
|
|
8346
|
-
mode:
|
|
8371
|
+
var remoteAuthSchema = z14.object({
|
|
8372
|
+
mode: z14.literal("remote"),
|
|
8347
8373
|
...flatRemoteProviderFields,
|
|
8348
8374
|
...sharedAuthFields
|
|
8349
8375
|
});
|
|
@@ -8351,21 +8377,22 @@ var orchestratedLocalSchema = localAuthSchema;
|
|
|
8351
8377
|
var orchestratedRemoteSchema = remoteAuthSchema;
|
|
8352
8378
|
|
|
8353
8379
|
// libs/auth/src/options/schema.ts
|
|
8354
|
-
import { z as
|
|
8355
|
-
var authOptionsSchema =
|
|
8380
|
+
import { z as z15 } from "@frontmcp/lazy-zod";
|
|
8381
|
+
var authOptionsSchema = z15.union([
|
|
8356
8382
|
publicAuthOptionsSchema,
|
|
8383
|
+
staticAuthOptionsSchema,
|
|
8357
8384
|
transparentAuthOptionsSchema,
|
|
8358
8385
|
localAuthSchema,
|
|
8359
8386
|
remoteAuthSchema
|
|
8360
8387
|
]);
|
|
8361
8388
|
|
|
8362
8389
|
// libs/auth/src/options/app-auth.schema.ts
|
|
8363
|
-
import { z as
|
|
8390
|
+
import { z as z16 } from "@frontmcp/lazy-zod";
|
|
8364
8391
|
var standaloneOptionSchema = {
|
|
8365
|
-
standalone:
|
|
8366
|
-
excludeFromParent:
|
|
8392
|
+
standalone: z16.boolean().optional(),
|
|
8393
|
+
excludeFromParent: z16.boolean().optional()
|
|
8367
8394
|
};
|
|
8368
|
-
var appAuthOptionsSchema =
|
|
8395
|
+
var appAuthOptionsSchema = z16.union([
|
|
8369
8396
|
publicAuthOptionsSchema.extend(standaloneOptionSchema),
|
|
8370
8397
|
transparentAuthOptionsSchema.extend(standaloneOptionSchema),
|
|
8371
8398
|
localAuthSchema.extend(standaloneOptionSchema),
|
|
@@ -8379,6 +8406,9 @@ function parseAuthOptions(input) {
|
|
|
8379
8406
|
function isPublicMode(options) {
|
|
8380
8407
|
return options.mode === "public";
|
|
8381
8408
|
}
|
|
8409
|
+
function isStaticMode(options) {
|
|
8410
|
+
return options.mode === "static";
|
|
8411
|
+
}
|
|
8382
8412
|
function isTransparentMode(options) {
|
|
8383
8413
|
return options.mode === "transparent";
|
|
8384
8414
|
}
|
|
@@ -8405,108 +8435,108 @@ function allowsPublicAccess(options) {
|
|
|
8405
8435
|
}
|
|
8406
8436
|
|
|
8407
8437
|
// libs/auth/src/consent/consent.types.ts
|
|
8408
|
-
import { z as
|
|
8409
|
-
var consentToolItemSchema =
|
|
8438
|
+
import { z as z17 } from "@frontmcp/lazy-zod";
|
|
8439
|
+
var consentToolItemSchema = z17.object({
|
|
8410
8440
|
/** Tool ID (e.g., 'slack:send_message') */
|
|
8411
|
-
id:
|
|
8441
|
+
id: z17.string().min(1),
|
|
8412
8442
|
/** Tool name for display */
|
|
8413
|
-
name:
|
|
8443
|
+
name: z17.string().min(1),
|
|
8414
8444
|
/** Tool description */
|
|
8415
|
-
description:
|
|
8445
|
+
description: z17.string().optional(),
|
|
8416
8446
|
/** App ID this tool belongs to */
|
|
8417
|
-
appId:
|
|
8447
|
+
appId: z17.string().min(1),
|
|
8418
8448
|
/** App name for display */
|
|
8419
|
-
appName:
|
|
8449
|
+
appName: z17.string().min(1),
|
|
8420
8450
|
/** Whether the tool is selected by default */
|
|
8421
|
-
defaultSelected:
|
|
8451
|
+
defaultSelected: z17.boolean().default(true),
|
|
8422
8452
|
/** Whether this tool requires specific scopes */
|
|
8423
|
-
requiredScopes:
|
|
8453
|
+
requiredScopes: z17.array(z17.string()).optional(),
|
|
8424
8454
|
/** Category for grouping (e.g., 'read', 'write', 'admin') */
|
|
8425
|
-
category:
|
|
8455
|
+
category: z17.string().optional()
|
|
8426
8456
|
});
|
|
8427
|
-
var consentSelectionSchema =
|
|
8457
|
+
var consentSelectionSchema = z17.object({
|
|
8428
8458
|
/** Selected tool IDs */
|
|
8429
|
-
selectedTools:
|
|
8459
|
+
selectedTools: z17.array(z17.string()),
|
|
8430
8460
|
/** Whether all tools were selected */
|
|
8431
|
-
allSelected:
|
|
8461
|
+
allSelected: z17.boolean(),
|
|
8432
8462
|
/** Timestamp when consent was given */
|
|
8433
|
-
consentedAt:
|
|
8463
|
+
consentedAt: z17.string().datetime(),
|
|
8434
8464
|
/** Consent version for tracking changes */
|
|
8435
|
-
consentVersion:
|
|
8465
|
+
consentVersion: z17.string().default("1.0")
|
|
8436
8466
|
});
|
|
8437
|
-
var consentStateSchema =
|
|
8467
|
+
var consentStateSchema = z17.object({
|
|
8438
8468
|
/** Whether consent flow is enabled */
|
|
8439
|
-
enabled:
|
|
8469
|
+
enabled: z17.boolean(),
|
|
8440
8470
|
/** Available tools for consent */
|
|
8441
|
-
availableTools:
|
|
8471
|
+
availableTools: z17.array(consentToolItemSchema),
|
|
8442
8472
|
/** Pre-selected tools (from previous consent or defaults) */
|
|
8443
|
-
preselectedTools:
|
|
8473
|
+
preselectedTools: z17.array(z17.string()).optional(),
|
|
8444
8474
|
/** Whether to show all tools or group by app */
|
|
8445
|
-
groupByApp:
|
|
8475
|
+
groupByApp: z17.boolean().default(true),
|
|
8446
8476
|
/** Custom consent message */
|
|
8447
|
-
customMessage:
|
|
8477
|
+
customMessage: z17.string().optional()
|
|
8448
8478
|
});
|
|
8449
|
-
var federatedProviderItemSchema =
|
|
8479
|
+
var federatedProviderItemSchema = z17.object({
|
|
8450
8480
|
/** Provider ID (derived or explicit) */
|
|
8451
|
-
id:
|
|
8481
|
+
id: z17.string().min(1),
|
|
8452
8482
|
/** Provider display name */
|
|
8453
|
-
name:
|
|
8483
|
+
name: z17.string().min(1),
|
|
8454
8484
|
/** Provider description */
|
|
8455
|
-
description:
|
|
8485
|
+
description: z17.string().optional(),
|
|
8456
8486
|
/** Provider icon URL or emoji */
|
|
8457
|
-
icon:
|
|
8487
|
+
icon: z17.string().optional(),
|
|
8458
8488
|
/** Provider type */
|
|
8459
|
-
type:
|
|
8489
|
+
type: z17.enum(["local", "remote", "transparent"]),
|
|
8460
8490
|
/** OAuth provider URL (for remote providers) */
|
|
8461
|
-
providerUrl:
|
|
8491
|
+
providerUrl: z17.string().url().optional(),
|
|
8462
8492
|
/** Apps using this provider */
|
|
8463
|
-
appIds:
|
|
8493
|
+
appIds: z17.array(z17.string()),
|
|
8464
8494
|
/** App names using this provider */
|
|
8465
|
-
appNames:
|
|
8495
|
+
appNames: z17.array(z17.string()),
|
|
8466
8496
|
/** Scopes required by this provider */
|
|
8467
|
-
scopes:
|
|
8497
|
+
scopes: z17.array(z17.string()),
|
|
8468
8498
|
/** Whether this is the primary/parent provider */
|
|
8469
|
-
isPrimary:
|
|
8499
|
+
isPrimary: z17.boolean(),
|
|
8470
8500
|
/** Whether this provider is optional (can be skipped) */
|
|
8471
|
-
isOptional:
|
|
8501
|
+
isOptional: z17.boolean().default(false)
|
|
8472
8502
|
});
|
|
8473
|
-
var federatedLoginStateSchema =
|
|
8503
|
+
var federatedLoginStateSchema = z17.object({
|
|
8474
8504
|
/** All available providers */
|
|
8475
|
-
providers:
|
|
8505
|
+
providers: z17.array(federatedProviderItemSchema),
|
|
8476
8506
|
/** Primary provider ID (if any) */
|
|
8477
|
-
primaryProviderId:
|
|
8507
|
+
primaryProviderId: z17.string().optional(),
|
|
8478
8508
|
/** Whether user can skip optional providers */
|
|
8479
|
-
allowSkip:
|
|
8509
|
+
allowSkip: z17.boolean().default(true),
|
|
8480
8510
|
/** Pre-selected provider IDs (from previous session) */
|
|
8481
|
-
preselectedProviders:
|
|
8511
|
+
preselectedProviders: z17.array(z17.string()).optional()
|
|
8482
8512
|
});
|
|
8483
|
-
var federatedSelectionSchema =
|
|
8513
|
+
var federatedSelectionSchema = z17.object({
|
|
8484
8514
|
/** Selected provider IDs */
|
|
8485
|
-
selectedProviders:
|
|
8515
|
+
selectedProviders: z17.array(z17.string()),
|
|
8486
8516
|
/** Skipped provider IDs */
|
|
8487
|
-
skippedProviders:
|
|
8517
|
+
skippedProviders: z17.array(z17.string()),
|
|
8488
8518
|
/** Provider-specific metadata */
|
|
8489
|
-
providerMetadata:
|
|
8519
|
+
providerMetadata: z17.record(z17.string(), z17.unknown()).optional()
|
|
8490
8520
|
});
|
|
8491
8521
|
|
|
8492
8522
|
// libs/auth/src/detection/auth-provider-detection.ts
|
|
8493
|
-
import { z as
|
|
8494
|
-
var detectedAuthProviderSchema =
|
|
8495
|
-
id:
|
|
8496
|
-
providerUrl:
|
|
8497
|
-
mode:
|
|
8498
|
-
appIds:
|
|
8499
|
-
scopes:
|
|
8500
|
-
isParentProvider:
|
|
8523
|
+
import { z as z18 } from "@frontmcp/lazy-zod";
|
|
8524
|
+
var detectedAuthProviderSchema = z18.object({
|
|
8525
|
+
id: z18.string(),
|
|
8526
|
+
providerUrl: z18.string().optional(),
|
|
8527
|
+
mode: z18.enum(["public", "static", "transparent", "local", "remote"]),
|
|
8528
|
+
appIds: z18.array(z18.string()),
|
|
8529
|
+
scopes: z18.array(z18.string()),
|
|
8530
|
+
isParentProvider: z18.boolean()
|
|
8501
8531
|
});
|
|
8502
|
-
var authProviderDetectionResultSchema =
|
|
8503
|
-
providers:
|
|
8504
|
-
requiresOrchestration:
|
|
8505
|
-
parentProviderId:
|
|
8506
|
-
childProviderIds:
|
|
8507
|
-
uniqueProviderCount:
|
|
8508
|
-
validationErrors:
|
|
8509
|
-
warnings:
|
|
8532
|
+
var authProviderDetectionResultSchema = z18.object({
|
|
8533
|
+
providers: z18.map(z18.string(), detectedAuthProviderSchema),
|
|
8534
|
+
requiresOrchestration: z18.boolean(),
|
|
8535
|
+
parentProviderId: z18.string().optional(),
|
|
8536
|
+
childProviderIds: z18.array(z18.string()),
|
|
8537
|
+
uniqueProviderCount: z18.number(),
|
|
8538
|
+
validationErrors: z18.array(z18.string()),
|
|
8539
|
+
warnings: z18.array(z18.string())
|
|
8510
8540
|
});
|
|
8511
8541
|
function deriveProviderId(options) {
|
|
8512
8542
|
if (isPublicMode(options)) {
|
|
@@ -8895,43 +8925,43 @@ var AudienceValidator = class _AudienceValidator {
|
|
|
8895
8925
|
};
|
|
8896
8926
|
|
|
8897
8927
|
// libs/auth/src/vault/auth-providers.types.ts
|
|
8898
|
-
import { z as
|
|
8899
|
-
var credentialScopeSchema =
|
|
8900
|
-
var loadingStrategySchema =
|
|
8901
|
-
var getCredentialOptionsSchema =
|
|
8902
|
-
forceRefresh:
|
|
8903
|
-
scopes:
|
|
8904
|
-
timeout:
|
|
8928
|
+
import { z as z19 } from "@frontmcp/lazy-zod";
|
|
8929
|
+
var credentialScopeSchema = z19.enum(["global", "user", "session"]);
|
|
8930
|
+
var loadingStrategySchema = z19.enum(["eager", "lazy"]);
|
|
8931
|
+
var getCredentialOptionsSchema = z19.object({
|
|
8932
|
+
forceRefresh: z19.boolean().optional(),
|
|
8933
|
+
scopes: z19.array(z19.string()).optional(),
|
|
8934
|
+
timeout: z19.number().positive().optional()
|
|
8905
8935
|
}).strict();
|
|
8906
|
-
var credentialProviderConfigSchema =
|
|
8907
|
-
name:
|
|
8908
|
-
description:
|
|
8936
|
+
var credentialProviderConfigSchema = z19.object({
|
|
8937
|
+
name: z19.string().min(1),
|
|
8938
|
+
description: z19.string().optional(),
|
|
8909
8939
|
scope: credentialScopeSchema,
|
|
8910
8940
|
loading: loadingStrategySchema,
|
|
8911
|
-
cacheTtl:
|
|
8941
|
+
cacheTtl: z19.number().nonnegative().optional(),
|
|
8912
8942
|
// Functions validated at runtime, not via Zod (Zod 4 compatibility)
|
|
8913
|
-
factory:
|
|
8914
|
-
refresh:
|
|
8915
|
-
toHeaders:
|
|
8916
|
-
metadata:
|
|
8917
|
-
required:
|
|
8943
|
+
factory: z19.any(),
|
|
8944
|
+
refresh: z19.any().optional(),
|
|
8945
|
+
toHeaders: z19.any().optional(),
|
|
8946
|
+
metadata: z19.record(z19.string(), z19.unknown()).optional(),
|
|
8947
|
+
required: z19.boolean().optional()
|
|
8918
8948
|
}).strict();
|
|
8919
|
-
var authProviderMappingSchema =
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
name:
|
|
8923
|
-
required:
|
|
8924
|
-
scopes:
|
|
8925
|
-
alias:
|
|
8949
|
+
var authProviderMappingSchema = z19.union([
|
|
8950
|
+
z19.string(),
|
|
8951
|
+
z19.object({
|
|
8952
|
+
name: z19.string().min(1),
|
|
8953
|
+
required: z19.boolean().optional().default(true),
|
|
8954
|
+
scopes: z19.array(z19.string()).optional(),
|
|
8955
|
+
alias: z19.string().optional()
|
|
8926
8956
|
}).strict()
|
|
8927
8957
|
]);
|
|
8928
|
-
var authProvidersVaultOptionsSchema =
|
|
8929
|
-
enabled:
|
|
8930
|
-
useSharedStorage:
|
|
8931
|
-
namespace:
|
|
8932
|
-
defaultCacheTtl:
|
|
8933
|
-
maxCredentialsPerSession:
|
|
8934
|
-
providers:
|
|
8958
|
+
var authProvidersVaultOptionsSchema = z19.object({
|
|
8959
|
+
enabled: z19.boolean().optional(),
|
|
8960
|
+
useSharedStorage: z19.boolean().optional().default(true),
|
|
8961
|
+
namespace: z19.string().optional().default("authproviders:"),
|
|
8962
|
+
defaultCacheTtl: z19.number().nonnegative().optional().default(36e5),
|
|
8963
|
+
maxCredentialsPerSession: z19.number().positive().optional().default(100),
|
|
8964
|
+
providers: z19.array(credentialProviderConfigSchema).optional()
|
|
8935
8965
|
}).strict();
|
|
8936
8966
|
|
|
8937
8967
|
// libs/auth/src/vault/credential-helpers.ts
|
|
@@ -9939,6 +9969,18 @@ var noopRelationshipResolver = {
|
|
|
9939
9969
|
return false;
|
|
9940
9970
|
}
|
|
9941
9971
|
};
|
|
9972
|
+
function resolveAuthUser(authInfo) {
|
|
9973
|
+
const extraUser = authInfo?.extra?.["user"];
|
|
9974
|
+
if (authInfo?.user) return authInfo.user;
|
|
9975
|
+
return typeof extraUser === "object" && extraUser !== null ? extraUser : {};
|
|
9976
|
+
}
|
|
9977
|
+
function isAnonymousSubject(sub) {
|
|
9978
|
+
return typeof sub !== "string" || sub === "" || sub.startsWith("anon:");
|
|
9979
|
+
}
|
|
9980
|
+
function signedInSubject(rawSub, mappedSub) {
|
|
9981
|
+
if (rawSub !== void 0 && isAnonymousSubject(rawSub)) return void 0;
|
|
9982
|
+
return [mappedSub, rawSub].find((candidate) => !isAnonymousSubject(candidate));
|
|
9983
|
+
}
|
|
9942
9984
|
var AuthoritiesContextBuilder = class {
|
|
9943
9985
|
claimsMapping;
|
|
9944
9986
|
claimsResolver;
|
|
@@ -9956,7 +9998,7 @@ var AuthoritiesContextBuilder = class {
|
|
|
9956
9998
|
const resolved = this.claimsResolver(authInfo);
|
|
9957
9999
|
return {
|
|
9958
10000
|
user: {
|
|
9959
|
-
sub: authInfo
|
|
10001
|
+
sub: signedInSubject(resolveAuthUser(authInfo).sub),
|
|
9960
10002
|
roles: resolved.roles,
|
|
9961
10003
|
permissions: resolved.permissions,
|
|
9962
10004
|
claims: resolved.claims
|
|
@@ -9966,7 +10008,7 @@ var AuthoritiesContextBuilder = class {
|
|
|
9966
10008
|
relationships: this.relationshipResolver
|
|
9967
10009
|
};
|
|
9968
10010
|
}
|
|
9969
|
-
const user = authInfo
|
|
10011
|
+
const user = resolveAuthUser(authInfo);
|
|
9970
10012
|
const authorization = authInfo?.extra?.["authorization"];
|
|
9971
10013
|
const authorizationClaims = authorization?.["claims"] ?? {};
|
|
9972
10014
|
const rawClaims = {
|
|
@@ -9987,10 +10029,10 @@ var AuthoritiesContextBuilder = class {
|
|
|
9987
10029
|
} else {
|
|
9988
10030
|
permissions = toStringArray(user["permissions"] ?? []);
|
|
9989
10031
|
}
|
|
9990
|
-
const
|
|
10032
|
+
const mappedSub = this.claimsMapping?.userId ? resolveDotPath(rawClaims, this.claimsMapping.userId) : void 0;
|
|
9991
10033
|
return {
|
|
9992
10034
|
user: {
|
|
9993
|
-
sub,
|
|
10035
|
+
sub: signedInSubject(user.sub, mappedSub),
|
|
9994
10036
|
roles,
|
|
9995
10037
|
permissions,
|
|
9996
10038
|
claims: rawClaims
|
|
@@ -10022,15 +10064,17 @@ var FrontMcpAuthContextImpl = class {
|
|
|
10022
10064
|
roles;
|
|
10023
10065
|
permissions;
|
|
10024
10066
|
constructor(source, claimsMapping) {
|
|
10025
|
-
const rawUser = source
|
|
10026
|
-
const
|
|
10067
|
+
const rawUser = resolveAuthUser(source);
|
|
10068
|
+
const mappedSub = claimsMapping?.userId ? resolveDotPath(this.buildRawClaims(source), claimsMapping.userId) : void 0;
|
|
10069
|
+
const signedInSub = signedInSubject(rawUser.sub, mappedSub);
|
|
10070
|
+
const sub = signedInSub ?? (typeof rawUser.sub === "string" ? rawUser.sub : "");
|
|
10027
10071
|
this.user = Object.freeze({
|
|
10028
10072
|
sub,
|
|
10029
10073
|
name: rawUser.name,
|
|
10030
10074
|
email: rawUser.email,
|
|
10031
10075
|
picture: rawUser.picture
|
|
10032
10076
|
});
|
|
10033
|
-
this.isAnonymous =
|
|
10077
|
+
this.isAnonymous = signedInSub === void 0;
|
|
10034
10078
|
const extraAuth = source.extra?.["authorization"];
|
|
10035
10079
|
this.mode = String(
|
|
10036
10080
|
source.extra?.["authMode"] ?? extraAuth?.["mode"] ?? (source.extra?.["isPublic"] === true || rawUser["anonymous"] === true ? "public" : "authenticated")
|
|
@@ -10088,7 +10132,7 @@ var FrontMcpAuthContextImpl = class {
|
|
|
10088
10132
|
* Mirrors the logic in AuthoritiesContextBuilder.build().
|
|
10089
10133
|
*/
|
|
10090
10134
|
buildRawClaims(source) {
|
|
10091
|
-
const rawUser = source
|
|
10135
|
+
const rawUser = resolveAuthUser(source);
|
|
10092
10136
|
const extraAuth = source.extra?.["authorization"];
|
|
10093
10137
|
const authorizationClaims = extraAuth?.["claims"] ?? {};
|
|
10094
10138
|
return {
|
|
@@ -10153,20 +10197,20 @@ function resolveRequiredScopes(denial, mapping, authorities) {
|
|
|
10153
10197
|
}
|
|
10154
10198
|
|
|
10155
10199
|
// libs/auth/src/authorities/authorities.schema.ts
|
|
10156
|
-
import { z as
|
|
10157
|
-
var rbacRolesPolicySchema =
|
|
10158
|
-
all:
|
|
10159
|
-
any:
|
|
10200
|
+
import { z as z20 } from "@frontmcp/lazy-zod";
|
|
10201
|
+
var rbacRolesPolicySchema = z20.object({
|
|
10202
|
+
all: z20.array(z20.string().min(1)).optional(),
|
|
10203
|
+
any: z20.array(z20.string().min(1)).optional()
|
|
10160
10204
|
}).refine((data) => data.all !== void 0 || data.any !== void 0, {
|
|
10161
10205
|
message: 'Roles policy must have at least "all" or "any"'
|
|
10162
10206
|
});
|
|
10163
|
-
var rbacPermissionsPolicySchema =
|
|
10164
|
-
all:
|
|
10165
|
-
any:
|
|
10207
|
+
var rbacPermissionsPolicySchema = z20.object({
|
|
10208
|
+
all: z20.array(z20.string().min(1)).optional(),
|
|
10209
|
+
any: z20.array(z20.string().min(1)).optional()
|
|
10166
10210
|
}).refine((data) => data.all !== void 0 || data.any !== void 0, {
|
|
10167
10211
|
message: 'Permissions policy must have at least "all" or "any"'
|
|
10168
10212
|
});
|
|
10169
|
-
var abacOperatorSchema =
|
|
10213
|
+
var abacOperatorSchema = z20.enum([
|
|
10170
10214
|
"eq",
|
|
10171
10215
|
"neq",
|
|
10172
10216
|
"in",
|
|
@@ -10181,55 +10225,55 @@ var abacOperatorSchema = z19.enum([
|
|
|
10181
10225
|
"exists",
|
|
10182
10226
|
"matches"
|
|
10183
10227
|
]);
|
|
10184
|
-
var abacConditionSchema =
|
|
10185
|
-
path:
|
|
10228
|
+
var abacConditionSchema = z20.object({
|
|
10229
|
+
path: z20.string().min(1),
|
|
10186
10230
|
op: abacOperatorSchema,
|
|
10187
|
-
value:
|
|
10231
|
+
value: z20.unknown()
|
|
10188
10232
|
});
|
|
10189
|
-
var abacPolicySchema =
|
|
10190
|
-
match:
|
|
10191
|
-
conditions:
|
|
10233
|
+
var abacPolicySchema = z20.object({
|
|
10234
|
+
match: z20.record(z20.string(), z20.unknown()).optional(),
|
|
10235
|
+
conditions: z20.array(abacConditionSchema).optional()
|
|
10192
10236
|
}).refine((data) => data.match !== void 0 || data.conditions !== void 0, {
|
|
10193
10237
|
message: 'Attributes policy must have at least "match" or "conditions"'
|
|
10194
10238
|
});
|
|
10195
|
-
var resourceIdRefSchema =
|
|
10196
|
-
|
|
10197
|
-
|
|
10198
|
-
|
|
10239
|
+
var resourceIdRefSchema = z20.union([
|
|
10240
|
+
z20.string().min(1),
|
|
10241
|
+
z20.object({ fromInput: z20.string().min(1) }).strict(),
|
|
10242
|
+
z20.object({ fromClaims: z20.string().min(1) }).strict()
|
|
10199
10243
|
]);
|
|
10200
|
-
var rebacPolicySchema =
|
|
10201
|
-
type:
|
|
10202
|
-
resource:
|
|
10244
|
+
var rebacPolicySchema = z20.object({
|
|
10245
|
+
type: z20.string().min(1),
|
|
10246
|
+
resource: z20.string().min(1),
|
|
10203
10247
|
resourceId: resourceIdRefSchema
|
|
10204
10248
|
});
|
|
10205
10249
|
var basePolicyShape = {
|
|
10206
10250
|
roles: rbacRolesPolicySchema.optional(),
|
|
10207
10251
|
permissions: rbacPermissionsPolicySchema.optional(),
|
|
10208
10252
|
attributes: abacPolicySchema.optional(),
|
|
10209
|
-
relationships:
|
|
10210
|
-
custom:
|
|
10211
|
-
guards:
|
|
10212
|
-
operator:
|
|
10253
|
+
relationships: z20.union([rebacPolicySchema, z20.array(rebacPolicySchema)]).optional(),
|
|
10254
|
+
custom: z20.record(z20.string(), z20.unknown()).optional(),
|
|
10255
|
+
guards: z20.array(z20.instanceof(Function)).optional(),
|
|
10256
|
+
operator: z20.enum(["AND", "OR"]).optional()
|
|
10213
10257
|
};
|
|
10214
|
-
var authoritiesPolicySchema =
|
|
10215
|
-
() =>
|
|
10258
|
+
var authoritiesPolicySchema = z20.lazy(
|
|
10259
|
+
() => z20.object({
|
|
10216
10260
|
...basePolicyShape,
|
|
10217
10261
|
not: authoritiesPolicySchema.optional(),
|
|
10218
|
-
anyOf:
|
|
10219
|
-
allOf:
|
|
10262
|
+
anyOf: z20.array(authoritiesPolicySchema).optional(),
|
|
10263
|
+
allOf: z20.array(authoritiesPolicySchema).optional()
|
|
10220
10264
|
})
|
|
10221
10265
|
);
|
|
10222
|
-
var authoritiesMetadataSchema =
|
|
10223
|
-
|
|
10224
|
-
|
|
10266
|
+
var authoritiesMetadataSchema = z20.union([
|
|
10267
|
+
z20.string().min(1),
|
|
10268
|
+
z20.array(z20.string().min(1)).min(1),
|
|
10225
10269
|
authoritiesPolicySchema
|
|
10226
10270
|
]);
|
|
10227
|
-
var authoritiesClaimsMappingSchema =
|
|
10271
|
+
var authoritiesClaimsMappingSchema = z20.record(z20.string(), z20.string().optional()).refine((data) => Object.values(data).some((v) => v !== void 0), {
|
|
10228
10272
|
message: "Claims mapping must have at least one mapping"
|
|
10229
10273
|
});
|
|
10230
|
-
var authoritiesConfigSchema =
|
|
10274
|
+
var authoritiesConfigSchema = z20.object({
|
|
10231
10275
|
claimsMapping: authoritiesClaimsMappingSchema.optional(),
|
|
10232
|
-
profiles:
|
|
10276
|
+
profiles: z20.record(z20.string().min(1), authoritiesPolicySchema).optional()
|
|
10233
10277
|
});
|
|
10234
10278
|
|
|
10235
10279
|
// libs/auth/src/authorities/authorities.evaluator.ts
|
|
@@ -10307,8 +10351,9 @@ function resolveValue(value, ctx) {
|
|
|
10307
10351
|
}
|
|
10308
10352
|
function applyOperator(actual, op, expected) {
|
|
10309
10353
|
switch (op) {
|
|
10354
|
+
// A missing expected value (e.g. an absent fromInput) never matches, even an absent actual value
|
|
10310
10355
|
case "eq":
|
|
10311
|
-
return actual === expected;
|
|
10356
|
+
return expected !== void 0 && actual === expected;
|
|
10312
10357
|
case "neq":
|
|
10313
10358
|
return actual !== expected;
|
|
10314
10359
|
case "in":
|
|
@@ -10357,7 +10402,7 @@ function evaluateAbac(policy, ctx) {
|
|
|
10357
10402
|
for (const [path, expectedRaw] of Object.entries(policy.match)) {
|
|
10358
10403
|
const actual = resolveDotPath(envelope, path);
|
|
10359
10404
|
const expected = resolveValue(expectedRaw, ctx);
|
|
10360
|
-
if (actual !== expected) {
|
|
10405
|
+
if (expected === void 0 || actual !== expected) {
|
|
10361
10406
|
return {
|
|
10362
10407
|
granted: false,
|
|
10363
10408
|
deniedBy: `attributes.match: '${path}' expected '${String(expected)}' but got '${String(actual)}'`,
|
|
@@ -10397,6 +10442,15 @@ function resolveResourceId(ref, ctx) {
|
|
|
10397
10442
|
return void 0;
|
|
10398
10443
|
}
|
|
10399
10444
|
async function evaluateSingleRebac(policy, ctx) {
|
|
10445
|
+
const userSub = ctx.user.sub;
|
|
10446
|
+
if (userSub === void 0 || isAnonymousSubject(userSub)) {
|
|
10447
|
+
return {
|
|
10448
|
+
granted: false,
|
|
10449
|
+
deniedBy: `relationships: an anonymous caller cannot be '${policy.type}' of ${policy.resource}`,
|
|
10450
|
+
denial: { kind: "relationships", path: "user.sub", expected: "a signed-in caller", actual: void 0 },
|
|
10451
|
+
evaluatedPolicies: ["relationships"]
|
|
10452
|
+
};
|
|
10453
|
+
}
|
|
10400
10454
|
const resourceId = resolveResourceId(policy.resourceId, ctx);
|
|
10401
10455
|
if (resourceId === void 0) {
|
|
10402
10456
|
return {
|
|
@@ -10411,11 +10465,11 @@ async function evaluateSingleRebac(policy, ctx) {
|
|
|
10411
10465
|
evaluatedPolicies: ["relationships"]
|
|
10412
10466
|
};
|
|
10413
10467
|
}
|
|
10414
|
-
const hasRelationship = await ctx.relationships.check(policy.type, policy.resource, resourceId,
|
|
10468
|
+
const hasRelationship = await ctx.relationships.check(policy.type, policy.resource, resourceId, userSub, ctx);
|
|
10415
10469
|
if (!hasRelationship) {
|
|
10416
10470
|
return {
|
|
10417
10471
|
granted: false,
|
|
10418
|
-
deniedBy: `relationships: user '${
|
|
10472
|
+
deniedBy: `relationships: user '${userSub}' is not '${policy.type}' of ${policy.resource}:${resourceId}`,
|
|
10419
10473
|
denial: {
|
|
10420
10474
|
kind: "relationships",
|
|
10421
10475
|
path: "relationships",
|
|
@@ -10932,6 +10986,8 @@ export {
|
|
|
10932
10986
|
InMemoryStoreRequiredError,
|
|
10933
10987
|
InvalidClientIdUrlError,
|
|
10934
10988
|
JwksService,
|
|
10989
|
+
JwtSecretRequiredError,
|
|
10990
|
+
JwtSecretWeakError,
|
|
10935
10991
|
LazyCredentialLoader,
|
|
10936
10992
|
NoProviderIdError,
|
|
10937
10993
|
NullOrchestratedAuthAccessor,
|
|
@@ -11086,6 +11142,7 @@ export {
|
|
|
11086
11142
|
headerApplier,
|
|
11087
11143
|
hkdfSha2564 as hkdfSha256,
|
|
11088
11144
|
incrementalAuthConfigSchema,
|
|
11145
|
+
isAnonymousSubject,
|
|
11089
11146
|
isCimdClientId,
|
|
11090
11147
|
isFrontMcpCredentials,
|
|
11091
11148
|
isJwt,
|
|
@@ -11102,6 +11159,7 @@ export {
|
|
|
11102
11159
|
isSoonExpiring,
|
|
11103
11160
|
isSoonExpiringProvider,
|
|
11104
11161
|
isSqliteTokenStorage,
|
|
11162
|
+
isStaticMode,
|
|
11105
11163
|
isTransparentMode,
|
|
11106
11164
|
jsonWebKeySetSchema,
|
|
11107
11165
|
jwkParametersSchema,
|
|
@@ -11161,6 +11219,7 @@ export {
|
|
|
11161
11219
|
startNextProvider,
|
|
11162
11220
|
statefulHttpTransportStateSchema,
|
|
11163
11221
|
statelessHttpTransportStateSchema,
|
|
11222
|
+
staticAuthOptionsSchema,
|
|
11164
11223
|
storedSessionSchema,
|
|
11165
11224
|
streamableHttpTransportStateSchema,
|
|
11166
11225
|
toEpochSeconds,
|