@frontmcp/auth 1.12.0 → 1.12.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 +7 -0
- package/authorities/authorities.context.d.ts.map +1 -1
- package/authorities/authorities.engine.d.ts +10 -4
- package/authorities/authorities.engine.d.ts.map +1 -1
- package/authorities/authorities.evaluator.d.ts +2 -0
- package/authorities/authorities.evaluator.d.ts.map +1 -1
- package/authorities/authorities.validation.d.ts.map +1 -1
- package/authorization/dcr-client.registry.d.ts.map +1 -1
- package/authorization/public.authorization.d.ts +3 -2
- package/authorization/public.authorization.d.ts.map +1 -1
- package/browser/authorities/authorities.context.mjs +5 -0
- package/browser/authorities/authorities.engine.mjs +45 -9
- package/browser/authorities/authorities.evaluator.mjs +36 -10
- package/browser/authorities/authorities.validation.mjs +3 -1
- package/browser/authorization/authorization.class.mjs +3 -3
- package/browser/authorization/dcr-client.registry.mjs +8 -0
- package/browser/authorization/public.authorization.mjs +10 -4
- package/browser/cimd/cimd-redis.cache.mjs +2 -0
- package/browser/cimd/cimd.service.mjs +16 -7
- package/browser/cimd/cimd.validator.mjs +6 -2
- package/browser/detection/auth-provider-detection.mjs +15 -0
- package/browser/jwks/jwks.service.mjs +9 -9
- package/browser/people/loopback-token.auth.mjs +10 -3
- package/browser/session/authorization.store.mjs +56 -2
- package/browser/session/consent.store.mjs +2 -1
- package/browser/session/credentials-accessor.impl.mjs +4 -1
- package/browser/session/incremental-auth-ticket.mjs +2 -0
- package/browser/session/redis-session.store.mjs +6 -4
- package/browser/session/secure-store-backends.mjs +2 -1
- package/browser/session/secure-store.factory.mjs +7 -1
- package/browser/session/storage/storage-authorization-vault.mjs +8 -2
- package/browser/session/storage-authorization.store.mjs +49 -3
- package/browser/session/storage-consent.store.mjs +3 -1
- package/browser/session/token-storage.factory.mjs +5 -2
- package/browser/session/transport-session.types.mjs +6 -0
- package/browser/session/utils/session-crypto.utils.mjs +14 -1
- package/browser/session/vercel-kv-session.store.mjs +6 -4
- package/cimd/cimd-redis.cache.d.ts.map +1 -1
- package/cimd/cimd.service.d.ts +1 -0
- package/cimd/cimd.service.d.ts.map +1 -1
- package/cimd/cimd.validator.d.ts +2 -0
- package/cimd/cimd.validator.d.ts.map +1 -1
- package/detection/auth-provider-detection.d.ts.map +1 -1
- package/esm/index.mjs +5 -5
- package/esm/package.json +5 -5
- package/index.js +5 -5
- package/jwks/jwks.service.d.ts.map +1 -1
- package/package.json +5 -5
- package/people/loopback-token.auth.d.ts +5 -1
- package/people/loopback-token.auth.d.ts.map +1 -1
- package/session/authorization.store.d.ts +23 -0
- package/session/authorization.store.d.ts.map +1 -1
- package/session/consent.store.d.ts +3 -1
- package/session/consent.store.d.ts.map +1 -1
- package/session/credentials-accessor.impl.d.ts.map +1 -1
- package/session/incremental-auth-ticket.d.ts +4 -0
- package/session/incremental-auth-ticket.d.ts.map +1 -1
- package/session/redis-session.store.d.ts.map +1 -1
- package/session/secure-store-backends.d.ts +3 -4
- package/session/secure-store-backends.d.ts.map +1 -1
- package/session/secure-store.factory.d.ts +2 -1
- package/session/secure-store.factory.d.ts.map +1 -1
- package/session/storage/storage-authorization-vault.d.ts.map +1 -1
- package/session/storage-authorization.store.d.ts +8 -0
- package/session/storage-authorization.store.d.ts.map +1 -1
- package/session/storage-consent.store.d.ts.map +1 -1
- package/session/token-storage.factory.d.ts +2 -1
- package/session/token-storage.factory.d.ts.map +1 -1
- package/session/transport-session.types.d.ts +5 -0
- package/session/transport-session.types.d.ts.map +1 -1
- package/session/utils/session-crypto.utils.d.ts +2 -1
- package/session/utils/session-crypto.utils.d.ts.map +1 -1
- package/session/vercel-kv-session.store.d.ts.map +1 -1
|
@@ -17,6 +17,11 @@ import type { AuthoritiesEvaluationContext, RelationshipResolver } from './autho
|
|
|
17
17
|
* resolveDotPath({ a: 1 }, 'a.b') // undefined
|
|
18
18
|
*/
|
|
19
19
|
export declare function resolveDotPath(obj: Record<string, unknown>, path: string): unknown;
|
|
20
|
+
/**
|
|
21
|
+
* The resolver of a context built without `relationshipResolver`. Its `check` answers false, and the
|
|
22
|
+
* engine reports a relationship rule checked against it as unresolved, so a `not` around it never grants.
|
|
23
|
+
*/
|
|
24
|
+
export declare const noopRelationshipResolver: RelationshipResolver;
|
|
20
25
|
/**
|
|
21
26
|
* Function signature for custom claims resolution.
|
|
22
27
|
*/
|
|
@@ -70,6 +75,8 @@ export declare class AuthoritiesContextBuilder {
|
|
|
70
75
|
private readonly claimsResolver;
|
|
71
76
|
private readonly relationshipResolver;
|
|
72
77
|
constructor(options?: AuthoritiesContextBuilderOptions);
|
|
78
|
+
/** Whether a relationship resolver was configured; without one every relationship check denies as unresolved. */
|
|
79
|
+
get hasRelationshipResolver(): boolean;
|
|
73
80
|
/**
|
|
74
81
|
* Build an evaluation context from AuthInfo and tool input.
|
|
75
82
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorities.context.d.ts","sourceRoot":"","sources":["../../src/authorities/authorities.context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,KAAK,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE9F;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAelF;
|
|
1
|
+
{"version":3,"file":"authorities.context.d.ts","sourceRoot":"","sources":["../../src/authorities/authorities.context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,KAAK,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE9F;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAelF;AAgBD;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAE,oBAItC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK;IAClE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAC1F,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,SAAS,GAAG,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAI9G;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAExD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAGxF;AAQD;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAC/C,aAAa,CAAC,EAAE,wBAAwB,CAAC;IACzC,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAED;;GAEG;AACH,qBAAa,yBAAyB;IACpC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAuC;IACrE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA+B;IAC9D,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAuB;gBAEhD,OAAO,GAAE,gCAAqC;IAM1D,iHAAiH;IACjH,IAAI,uBAAuB,IAAI,OAAO,CAErC;IAED;;OAEG;IACH,KAAK,CACH,QAAQ,EAAE,OAAO,CAAC,YAAY,CAAC,EAC/B,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAc,GACxC,4BAA4B;CAwEhC"}
|
|
@@ -24,15 +24,21 @@ export declare class AuthoritiesEngine {
|
|
|
24
24
|
constructor(profiles: AuthoritiesProfileRegistry, evaluators: AuthoritiesEvaluatorRegistry);
|
|
25
25
|
/**
|
|
26
26
|
* Problems with an `authorities` value declared on an entry (see {@link findAuthoritiesRuleProblems}),
|
|
27
|
-
*
|
|
28
|
-
* starts; {@link evaluate} denies such a value
|
|
27
|
+
* each profile name it uses that no registered profile has, and each custom evaluator it names that
|
|
28
|
+
* is not registered. The server reports them when it starts; {@link evaluate} denies such a value.
|
|
29
29
|
*/
|
|
30
30
|
findRuleProblems(authorities: unknown): string[];
|
|
31
31
|
/**
|
|
32
|
-
* Problems with every registered profile's rule (see {@link findAuthoritiesRuleProblems})
|
|
33
|
-
* The server reports them when it starts.
|
|
32
|
+
* Problems with every registered profile's rule (see {@link findAuthoritiesRuleProblems}), and each
|
|
33
|
+
* custom evaluator a profile names that is not registered. The server reports them when it starts.
|
|
34
34
|
*/
|
|
35
35
|
findProfileProblems(): string[];
|
|
36
|
+
/** The registered profiles whose rule checks relationships, at any depth. */
|
|
37
|
+
profilesCheckingRelationships(): string[];
|
|
38
|
+
/** Whether an entry's rule object checks relationships, at any depth (a profile it names is not looked into). */
|
|
39
|
+
ruleChecksRelationships(authorities: unknown): boolean;
|
|
40
|
+
/** A problem for each custom evaluator a rule names, at any depth, that is not registered. */
|
|
41
|
+
private unknownEvaluatorProblems;
|
|
36
42
|
/**
|
|
37
43
|
* Evaluate an AuthoritiesMetadata value (string, string[], or policy object).
|
|
38
44
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorities.engine.d.ts","sourceRoot":"","sources":["../../src/authorities/authorities.engine.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACvG,OAAO,KAAK,EACV,4BAA4B,EAC5B,mBAAmB,EACnB,yBAAyB,EACzB,iBAAiB,EAElB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"authorities.engine.d.ts","sourceRoot":"","sources":["../../src/authorities/authorities.engine.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACvG,OAAO,KAAK,EACV,4BAA4B,EAC5B,mBAAmB,EACnB,yBAAyB,EACzB,iBAAiB,EAElB,MAAM,qBAAqB,CAAC;AAiE7B;;;;;;;;;GASG;AACH,qBAAa,iBAAiB;IAK1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAL7B,iGAAiG;IACjG,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmC;gBAG7C,QAAQ,EAAE,0BAA0B,EACpC,UAAU,EAAE,4BAA4B;IAG3D;;;;OAIG;IACH,gBAAgB,CAAC,WAAW,EAAE,OAAO,GAAG,MAAM,EAAE;IAYhD;;;OAGG;IACH,mBAAmB,IAAI,MAAM,EAAE;IAQ/B,6EAA6E;IAC7E,6BAA6B,IAAI,MAAM,EAAE;IAMzC,iHAAiH;IACjH,uBAAuB,CAAC,WAAW,EAAE,OAAO,GAAG,OAAO;IAItD,8FAA8F;IAC9F,OAAO,CAAC,wBAAwB;IAMhC;;;;OAIG;IACG,QAAQ,CAAC,WAAW,EAAE,mBAAmB,EAAE,GAAG,EAAE,4BAA4B,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAkB/G;;OAEG;YACW,eAAe;IAyB7B,yEAAyE;IACzE,OAAO,CAAC,UAAU;IAUlB;;OAEG;YACW,oBAAoB;IAIlC;;OAEG;YACW,cAAc;IA+C5B;;OAEG;IACH,OAAO,CAAC,cAAc;IA4BtB;;OAEG;YACW,cAAc;IAmB5B;;;;OAIG;YACW,cAAc;IAyB5B;;OAEG;YACW,aAAa;IAO3B;;OAEG;YACW,aAAa;IA4B3B;;OAEG;YACW,WAAW;IAiBzB;;;OAGG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,yBAAyB,GAAG,SAAS;CAGpE"}
|
|
@@ -12,6 +12,8 @@ export declare function evaluateRbacRoles(policy: RbacRolesPolicy, ctx: Authorit
|
|
|
12
12
|
* Evaluate RBAC permissions policy against user permissions.
|
|
13
13
|
*/
|
|
14
14
|
export declare function evaluateRbacPermissions(policy: RbacPermissionsPolicy, ctx: AuthoritiesEvaluationContext): AuthoritiesResult;
|
|
15
|
+
/** Why a `matches` check cannot run on these values in bounded time, or undefined when it can. */
|
|
16
|
+
export declare function unmatchableReason(actual: unknown, pattern: unknown): string | undefined;
|
|
15
17
|
/**
|
|
16
18
|
* Evaluate ABAC policy.
|
|
17
19
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorities.evaluator.d.ts","sourceRoot":"","sources":["../../src/authorities/authorities.evaluator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"authorities.evaluator.d.ts","sourceRoot":"","sources":["../../src/authorities/authorities.evaluator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EACV,UAAU,EACV,4BAA4B,EAC5B,iBAAiB,EACjB,qBAAqB,EACrB,eAAe,EACf,WAAW,EACX,aAAa,EACd,MAAM,qBAAqB,CAAC;AAM7B;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,EAAE,4BAA4B,GAAG,iBAAiB,CA4B/G;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,qBAAqB,EAC7B,GAAG,EAAE,4BAA4B,GAChC,iBAAiB,CA4BnB;AA8DD,kGAAkG;AAClG,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAUvF;AA+CD;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,4BAA4B,GAAG,iBAAiB,CA4DrG;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,4BAA4B,GAAG,MAAM,GAAG,SAAS,CAc3G;AA4DD;;;GAGG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,WAAW,GAAG,WAAW,EAAE,EACnC,GAAG,EAAE,4BAA4B,GAChC,OAAO,CAAC,iBAAiB,CAAC,CAY5B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorities.validation.d.ts","sourceRoot":"","sources":["../../src/authorities/authorities.validation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"authorities.validation.d.ts","sourceRoot":"","sources":["../../src/authorities/authorities.validation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAkPH;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,OAAO,GAAG,MAAM,EAAE,CAY1E;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,CAEtF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dcr-client.registry.d.ts","sourceRoot":"","sources":["../../src/authorization/dcr-client.registry.ts"],"names":[],"mappings":"AAgBA,6DAA6D;AAC7D,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0BAA0B,EACtB,MAAM,GACN,qBAAqB,GACrB,oBAAoB,GACpB,iBAAiB,GACjB,iBAAiB,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,GAAG,EAAE,OAAO,CAAC;IACb,uEAAuE;IACvE,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,qEAAqE;AACrE,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uBAAuB,CAAC,EAAE,MAAM,GAAG,qBAAqB,GAAG,oBAAoB,CAAC;IAChF,UAAU,CAAC,EAAE,KAAK,CAAC,oBAAoB,GAAG,eAAe,CAAC,CAAC;IAC3D,aAAa,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,2DAA2D;AAC3D,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,wBAAwB,EAAE,CAAC;IACrC;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAKD;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAyBjE;
|
|
1
|
+
{"version":3,"file":"dcr-client.registry.d.ts","sourceRoot":"","sources":["../../src/authorization/dcr-client.registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -44,6 +44,7 @@ export interface PublicAuthorizationCreateCtx {
|
|
|
44
44
|
* - Ideal for development, docs, public wikis, and read-only resources
|
|
45
45
|
*/
|
|
46
46
|
export declare class PublicAuthorization extends AuthorizationBase {
|
|
47
|
+
#private;
|
|
47
48
|
readonly mode: AuthMode;
|
|
48
49
|
/**
|
|
49
50
|
* Issuer identifier for the anonymous authorization
|
|
@@ -73,11 +74,11 @@ export declare class PublicAuthorization extends AuthorizationBase {
|
|
|
73
74
|
*/
|
|
74
75
|
getToken(_providerId?: string): Promise<string>;
|
|
75
76
|
/**
|
|
76
|
-
* Check if all tools are allowed (
|
|
77
|
+
* Check if all tools are allowed (`allowedTools: 'all'`); an empty list allows none
|
|
77
78
|
*/
|
|
78
79
|
get allowsAllTools(): boolean;
|
|
79
80
|
/**
|
|
80
|
-
* Check if all prompts are allowed (
|
|
81
|
+
* Check if all prompts are allowed (`allowedPrompts: 'all'`); an empty list allows none
|
|
81
82
|
*/
|
|
82
83
|
get allowsAllPrompts(): boolean;
|
|
83
84
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public.authorization.d.ts","sourceRoot":"","sources":["../../src/authorization/public.authorization.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAoC,MAAM,uBAAuB,CAAC;AAExF;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,YAAY,CAAC,EAAE,KAAK,GAAG,MAAM,EAAE,CAAC;IAEhC;;;OAGG;IACH,cAAc,CAAC,EAAE,KAAK,GAAG,MAAM,EAAE,CAAC;CACnC;AAED;;;;;;;;GAQG;AACH,qBAAa,mBAAoB,SAAQ,iBAAiB
|
|
1
|
+
{"version":3,"file":"public.authorization.d.ts","sourceRoot":"","sources":["../../src/authorization/public.authorization.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAoC,MAAM,uBAAuB,CAAC;AAExF;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,YAAY,CAAC,EAAE,KAAK,GAAG,MAAM,EAAE,CAAC;IAEhC;;;OAGG;IACH,cAAc,CAAC,EAAE,KAAK,GAAG,MAAM,EAAE,CAAC;CACnC;AAED;;;;;;;;GAQG;AACH,qBAAa,mBAAoB,SAAQ,iBAAiB;;IACxD,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAY;IAEnC;;OAEG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAKzB,OAAO;IAOP;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,MAAM,CAAC,GAAG,GAAE,4BAAiC,GAAG,mBAAmB;IA+D1E;;;;OAIG;IACG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAOrD;;OAEG;IACH,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED;;OAEG;IACH,IAAI,gBAAgB,IAAI,OAAO,CAE9B;IAED;;OAEG;IACM,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAO/C;;OAEG;IACM,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;CAMpD"}
|
|
@@ -53,6 +53,10 @@ var AuthoritiesContextBuilder = class {
|
|
|
53
53
|
this.claimsResolver = options.claimsResolver;
|
|
54
54
|
this.relationshipResolver = options.relationshipResolver ?? noopRelationshipResolver;
|
|
55
55
|
}
|
|
56
|
+
/** Whether a relationship resolver was configured; without one every relationship check denies as unresolved. */
|
|
57
|
+
get hasRelationshipResolver() {
|
|
58
|
+
return this.relationshipResolver !== noopRelationshipResolver;
|
|
59
|
+
}
|
|
56
60
|
/**
|
|
57
61
|
* Build an evaluation context from AuthInfo and tool input.
|
|
58
62
|
*/
|
|
@@ -111,6 +115,7 @@ var AuthoritiesContextBuilder = class {
|
|
|
111
115
|
export {
|
|
112
116
|
AuthoritiesContextBuilder,
|
|
113
117
|
isAnonymousSubject,
|
|
118
|
+
noopRelationshipResolver,
|
|
114
119
|
resolveAuthUser,
|
|
115
120
|
resolveDotPath,
|
|
116
121
|
signedInSubject
|
|
@@ -15,6 +15,24 @@ function invalidRule(problems, policies = []) {
|
|
|
15
15
|
evaluatedPolicies: policies
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
+
function customEvaluatorNames(rule, seen = /* @__PURE__ */ new Set()) {
|
|
19
|
+
if (typeof rule !== "object" || rule === null || Array.isArray(rule) || seen.has(rule)) return [];
|
|
20
|
+
seen.add(rule);
|
|
21
|
+
const { custom, allOf, anyOf, not } = rule;
|
|
22
|
+
const nested = [...Array.isArray(allOf) ? allOf : [], ...Array.isArray(anyOf) ? anyOf : [], not];
|
|
23
|
+
return [
|
|
24
|
+
...typeof custom === "object" && custom !== null ? Object.keys(custom) : [],
|
|
25
|
+
...nested.flatMap((inner) => customEvaluatorNames(inner, seen))
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
function checksRelationships(rule, seen = /* @__PURE__ */ new Set()) {
|
|
29
|
+
if (typeof rule !== "object" || rule === null || Array.isArray(rule) || seen.has(rule)) return false;
|
|
30
|
+
seen.add(rule);
|
|
31
|
+
const { relationships, allOf, anyOf, not } = rule;
|
|
32
|
+
if (relationships !== void 0) return true;
|
|
33
|
+
const nested = [...Array.isArray(allOf) ? allOf : [], ...Array.isArray(anyOf) ? anyOf : [], not];
|
|
34
|
+
return nested.some((inner) => checksRelationships(inner, seen));
|
|
35
|
+
}
|
|
18
36
|
async function evaluateEvery(items, evaluateItem, evaluatedPolicies = []) {
|
|
19
37
|
let unresolved;
|
|
20
38
|
for (const item of items) {
|
|
@@ -37,23 +55,40 @@ var AuthoritiesEngine = class {
|
|
|
37
55
|
ruleProblems = /* @__PURE__ */ new WeakMap();
|
|
38
56
|
/**
|
|
39
57
|
* Problems with an `authorities` value declared on an entry (see {@link findAuthoritiesRuleProblems}),
|
|
40
|
-
*
|
|
41
|
-
* starts; {@link evaluate} denies such a value
|
|
58
|
+
* each profile name it uses that no registered profile has, and each custom evaluator it names that
|
|
59
|
+
* is not registered. The server reports them when it starts; {@link evaluate} denies such a value.
|
|
42
60
|
*/
|
|
43
61
|
findRuleProblems(authorities) {
|
|
44
62
|
const problems = findAuthoritiesRuleProblems(authorities);
|
|
45
63
|
if (problems.length > 0) return problems;
|
|
46
64
|
const names = typeof authorities === "string" ? [authorities] : Array.isArray(authorities) ? authorities : [];
|
|
47
|
-
return
|
|
65
|
+
return [
|
|
66
|
+
...names.filter((name) => typeof name === "string" && !this.profiles.has(name)).map((name) => `names an unknown profile "${name}"`),
|
|
67
|
+
...this.unknownEvaluatorProblems(authorities)
|
|
68
|
+
];
|
|
48
69
|
}
|
|
49
70
|
/**
|
|
50
|
-
* Problems with every registered profile's rule (see {@link findAuthoritiesRuleProblems})
|
|
51
|
-
* The server reports them when it starts.
|
|
71
|
+
* Problems with every registered profile's rule (see {@link findAuthoritiesRuleProblems}), and each
|
|
72
|
+
* custom evaluator a profile names that is not registered. The server reports them when it starts.
|
|
52
73
|
*/
|
|
53
74
|
findProfileProblems() {
|
|
54
|
-
return Object.entries(this.profiles.getAll()).flatMap(
|
|
55
|
-
|
|
56
|
-
|
|
75
|
+
return Object.entries(this.profiles.getAll()).flatMap(([name, policy]) => {
|
|
76
|
+
const problems = findAuthoritiesProfileProblems(name, policy);
|
|
77
|
+
if (problems.length > 0) return problems;
|
|
78
|
+
return this.unknownEvaluatorProblems(policy).map((problem) => `profile "${name}": ${problem}`);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/** The registered profiles whose rule checks relationships, at any depth. */
|
|
82
|
+
profilesCheckingRelationships() {
|
|
83
|
+
return Object.entries(this.profiles.getAll()).filter(([, policy]) => checksRelationships(policy)).map(([name]) => name);
|
|
84
|
+
}
|
|
85
|
+
/** Whether an entry's rule object checks relationships, at any depth (a profile it names is not looked into). */
|
|
86
|
+
ruleChecksRelationships(authorities) {
|
|
87
|
+
return checksRelationships(authorities);
|
|
88
|
+
}
|
|
89
|
+
/** A problem for each custom evaluator a rule names, at any depth, that is not registered. */
|
|
90
|
+
unknownEvaluatorProblems(rule) {
|
|
91
|
+
return [...new Set(customEvaluatorNames(rule))].filter((name) => !this.evaluators.has(name)).map((name) => `names an unknown custom evaluator "${name}" (register it in authorities.evaluators)`);
|
|
57
92
|
}
|
|
58
93
|
/**
|
|
59
94
|
* Evaluate an AuthoritiesMetadata value (string, string[], or policy object).
|
|
@@ -183,7 +218,8 @@ var AuthoritiesEngine = class {
|
|
|
183
218
|
if (!evaluator) {
|
|
184
219
|
return {
|
|
185
220
|
granted: false,
|
|
186
|
-
|
|
221
|
+
unresolved: true,
|
|
222
|
+
deniedBy: `custom evaluator '${name}' is not registered (add it to authorities.evaluators)`,
|
|
187
223
|
evaluatedPolicies: [`custom.${name}`]
|
|
188
224
|
};
|
|
189
225
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// libs/auth/src/authorities/authorities.evaluator.ts
|
|
2
|
-
import {
|
|
2
|
+
import { isPatternSafe } from "@frontmcp/utils";
|
|
3
|
+
import { isAnonymousSubject, noopRelationshipResolver, resolveDotPath } from "./authorities.context.mjs";
|
|
3
4
|
function evaluateRbacRoles(policy, ctx) {
|
|
4
5
|
const userRoles = ctx.user.roles;
|
|
5
6
|
if (policy.all) {
|
|
@@ -87,6 +88,19 @@ function unresolvedAttribute(policyPath, denialPath, path, value, actual) {
|
|
|
87
88
|
evaluatedPolicies: [policyPath]
|
|
88
89
|
};
|
|
89
90
|
}
|
|
91
|
+
var MAX_PATTERN_LENGTH = 256;
|
|
92
|
+
var MAX_MATCHED_LENGTH = 1e4;
|
|
93
|
+
function unmatchableReason(actual, pattern) {
|
|
94
|
+
if (typeof pattern !== "string") return void 0;
|
|
95
|
+
if (pattern.length > MAX_PATTERN_LENGTH) return `the pattern is longer than ${MAX_PATTERN_LENGTH} characters`;
|
|
96
|
+
if (!isPatternSafe(pattern)) {
|
|
97
|
+
return "the pattern is not a valid regular expression, or its matching time can grow very fast with the value";
|
|
98
|
+
}
|
|
99
|
+
if (typeof actual === "string" && actual.length > MAX_MATCHED_LENGTH) {
|
|
100
|
+
return `the value is longer than ${MAX_MATCHED_LENGTH} characters`;
|
|
101
|
+
}
|
|
102
|
+
return void 0;
|
|
103
|
+
}
|
|
90
104
|
function applyOperator(actual, op, expected) {
|
|
91
105
|
switch (op) {
|
|
92
106
|
case "eq":
|
|
@@ -116,14 +130,7 @@ function applyOperator(actual, op, expected) {
|
|
|
116
130
|
case "exists":
|
|
117
131
|
return expected ? actual !== void 0 && actual !== null : actual === void 0 || actual === null;
|
|
118
132
|
case "matches":
|
|
119
|
-
|
|
120
|
-
if (expected.length > 256) return false;
|
|
121
|
-
if (/(\+\+|\*\*|\{\d+,\d*\}\{)/.test(expected)) return false;
|
|
122
|
-
try {
|
|
123
|
-
return new RegExp(expected).test(actual);
|
|
124
|
-
} catch {
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
133
|
+
return typeof actual === "string" && typeof expected === "string" && new RegExp(expected).test(actual);
|
|
127
134
|
default:
|
|
128
135
|
return false;
|
|
129
136
|
}
|
|
@@ -159,6 +166,7 @@ function evaluateAbac(policy, ctx) {
|
|
|
159
166
|
const actual = resolveDotPath(envelope, condition.path);
|
|
160
167
|
const expected = resolveValue(condition.value, ctx);
|
|
161
168
|
const reported = reportableActual(condition.path, actual);
|
|
169
|
+
const unmatchable = condition.op === "matches" ? unmatchableReason(actual, expected) : void 0;
|
|
162
170
|
if (expected === void 0) {
|
|
163
171
|
unresolved ??= unresolvedAttribute(
|
|
164
172
|
"attributes.conditions",
|
|
@@ -167,6 +175,14 @@ function evaluateAbac(policy, ctx) {
|
|
|
167
175
|
condition.value,
|
|
168
176
|
reported
|
|
169
177
|
);
|
|
178
|
+
} else if (unmatchable !== void 0) {
|
|
179
|
+
unresolved ??= {
|
|
180
|
+
granted: false,
|
|
181
|
+
unresolved: true,
|
|
182
|
+
deniedBy: `attributes.conditions: '${condition.path}' could not be checked: ${unmatchable}`,
|
|
183
|
+
denial: { kind: "attributes", path: denialPath, actual: reported },
|
|
184
|
+
evaluatedPolicies: ["attributes.conditions"]
|
|
185
|
+
};
|
|
170
186
|
} else if (!applyOperator(actual, condition.op, expected)) {
|
|
171
187
|
return {
|
|
172
188
|
granted: false,
|
|
@@ -201,6 +217,15 @@ async function evaluateSingleRebac(policy, ctx) {
|
|
|
201
217
|
evaluatedPolicies: ["relationships"]
|
|
202
218
|
};
|
|
203
219
|
}
|
|
220
|
+
if (ctx.relationships === noopRelationshipResolver) {
|
|
221
|
+
return {
|
|
222
|
+
granted: false,
|
|
223
|
+
unresolved: true,
|
|
224
|
+
deniedBy: `relationships: '${policy.type}' of ${policy.resource} could not be checked: no relationship resolver is configured (set authorities.relationshipResolver)`,
|
|
225
|
+
denial: { kind: "relationships", path: "relationships", expected: `${policy.type}:${policy.resource}` },
|
|
226
|
+
evaluatedPolicies: ["relationships"]
|
|
227
|
+
};
|
|
228
|
+
}
|
|
204
229
|
const resourceId = resolveResourceId(policy.resourceId, ctx);
|
|
205
230
|
if (resourceId === void 0) {
|
|
206
231
|
return {
|
|
@@ -247,5 +272,6 @@ export {
|
|
|
247
272
|
evaluateRbacPermissions,
|
|
248
273
|
evaluateRbacRoles,
|
|
249
274
|
evaluateRebac,
|
|
250
|
-
resolveResourceId
|
|
275
|
+
resolveResourceId,
|
|
276
|
+
unmatchableReason
|
|
251
277
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// libs/auth/src/authorities/authorities.validation.ts
|
|
2
|
+
import { unmatchableReason } from "./authorities.evaluator.mjs";
|
|
2
3
|
var ABAC_OPERATORS = /* @__PURE__ */ new Set([
|
|
3
4
|
"eq",
|
|
4
5
|
"neq",
|
|
@@ -82,7 +83,8 @@ function checkConditionValue(op, condition, path) {
|
|
|
82
83
|
const expected = OPERATOR_VALUES[op];
|
|
83
84
|
if (expected && !expected.accepts(value)) return [`${path} must be ${expected.needs} for "${op}"`];
|
|
84
85
|
if (Array.isArray(value) && value.length === 0 && (op === "in" || op === "notIn")) return [`${path} is empty`];
|
|
85
|
-
|
|
86
|
+
const unmatchable = op === "matches" ? unmatchableReason(void 0, value) : void 0;
|
|
87
|
+
return unmatchable === void 0 ? [] : [`${path} cannot be used: ${unmatchable}`];
|
|
86
88
|
}
|
|
87
89
|
function checkAttributes(value, path) {
|
|
88
90
|
if (!isPlainObject(value)) return [`${path} must be an object with "match" or "conditions"`];
|
|
@@ -108,13 +108,13 @@ var AuthorizationBase = class _AuthorizationBase {
|
|
|
108
108
|
* Check if a tool is authorized
|
|
109
109
|
*/
|
|
110
110
|
canAccessTool(toolId) {
|
|
111
|
-
return
|
|
111
|
+
return Object.hasOwn(this.authorizedTools, toolId) || this.authorizedToolIds.includes(toolId);
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
114
114
|
* Check if a prompt is authorized
|
|
115
115
|
*/
|
|
116
116
|
canAccessPrompt(promptId) {
|
|
117
|
-
return
|
|
117
|
+
return Object.hasOwn(this.authorizedPrompts, promptId) || this.authorizedPromptIds.includes(promptId);
|
|
118
118
|
}
|
|
119
119
|
/**
|
|
120
120
|
* Check if an app is authorized.
|
|
@@ -122,7 +122,7 @@ var AuthorizationBase = class _AuthorizationBase {
|
|
|
122
122
|
* @param appId - App ID to check
|
|
123
123
|
*/
|
|
124
124
|
isAppAuthorized(appId) {
|
|
125
|
-
return
|
|
125
|
+
return Object.hasOwn(this.authorizedApps, appId) || this.authorizedAppIds.includes(appId);
|
|
126
126
|
}
|
|
127
127
|
/**
|
|
128
128
|
* Build URL for progressive/incremental authorization.
|
|
@@ -60,9 +60,17 @@ function splitRedirectUri(s) {
|
|
|
60
60
|
}
|
|
61
61
|
return { scheme, userinfo, host, port, rest };
|
|
62
62
|
}
|
|
63
|
+
function hasAmbiguousCharacter(uri) {
|
|
64
|
+
for (let index = 0; index < uri.length; index++) {
|
|
65
|
+
const code = uri.charCodeAt(index);
|
|
66
|
+
if (code <= 32 || code === 92 || code === 127) return true;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
63
70
|
function globMatchRedirectUri(pattern, input) {
|
|
64
71
|
const p = splitRedirectUri(pattern);
|
|
65
72
|
if (!p) return globMatch(pattern, input);
|
|
73
|
+
if (hasAmbiguousCharacter(input)) return false;
|
|
66
74
|
const u = splitRedirectUri(input);
|
|
67
75
|
if (!u) return false;
|
|
68
76
|
return globMatch(p.scheme, u.scheme) && globMatch(p.userinfo, u.userinfo) && globMatch(p.host, u.host) && globMatch(p.port, u.port) && globMatch(p.rest, u.rest);
|
|
@@ -8,9 +8,13 @@ var PublicAuthorization = class _PublicAuthorization extends AuthorizationBase {
|
|
|
8
8
|
* Issuer identifier for the anonymous authorization
|
|
9
9
|
*/
|
|
10
10
|
issuer;
|
|
11
|
+
#allTools;
|
|
12
|
+
#allPrompts;
|
|
11
13
|
constructor(ctx) {
|
|
12
14
|
super(ctx);
|
|
13
15
|
this.issuer = ctx.issuer;
|
|
16
|
+
this.#allTools = ctx.allTools;
|
|
17
|
+
this.#allPrompts = ctx.allPrompts;
|
|
14
18
|
}
|
|
15
19
|
/**
|
|
16
20
|
* Create a new PublicAuthorization for anonymous access
|
|
@@ -72,6 +76,8 @@ var PublicAuthorization = class _PublicAuthorization extends AuthorizationBase {
|
|
|
72
76
|
scopes,
|
|
73
77
|
expiresAt,
|
|
74
78
|
issuer,
|
|
79
|
+
allTools: allowedTools === "all",
|
|
80
|
+
allPrompts: allowedPrompts === "all",
|
|
75
81
|
authorizedTools: allowedTools === "all" ? void 0 : authorizedTools,
|
|
76
82
|
authorizedToolIds: allowedTools === "all" ? void 0 : authorizedToolIds,
|
|
77
83
|
authorizedPrompts: allowedPrompts === "all" ? void 0 : authorizedPrompts,
|
|
@@ -89,16 +95,16 @@ var PublicAuthorization = class _PublicAuthorization extends AuthorizationBase {
|
|
|
89
95
|
);
|
|
90
96
|
}
|
|
91
97
|
/**
|
|
92
|
-
* Check if all tools are allowed (
|
|
98
|
+
* Check if all tools are allowed (`allowedTools: 'all'`); an empty list allows none
|
|
93
99
|
*/
|
|
94
100
|
get allowsAllTools() {
|
|
95
|
-
return this
|
|
101
|
+
return this.#allTools;
|
|
96
102
|
}
|
|
97
103
|
/**
|
|
98
|
-
* Check if all prompts are allowed (
|
|
104
|
+
* Check if all prompts are allowed (`allowedPrompts: 'all'`); an empty list allows none
|
|
99
105
|
*/
|
|
100
106
|
get allowsAllPrompts() {
|
|
101
|
-
return this
|
|
107
|
+
return this.#allPrompts;
|
|
102
108
|
}
|
|
103
109
|
/**
|
|
104
110
|
* Override canAccessTool to support 'all' mode
|
|
@@ -20,7 +20,9 @@ var RedisCimdCache = class {
|
|
|
20
20
|
this.keyPrefix = config.redis.keyPrefix ?? "cimd:";
|
|
21
21
|
const redisOptions = {};
|
|
22
22
|
if (config.redis.url) {
|
|
23
|
+
const { port, password, db, tls } = config.redis;
|
|
23
24
|
redisOptions.url = config.redis.url;
|
|
25
|
+
redisOptions.config = { port, password, db, tls };
|
|
24
26
|
} else if (config.redis.host) {
|
|
25
27
|
redisOptions.config = {
|
|
26
28
|
host: config.redis.host,
|
|
@@ -21,7 +21,13 @@ import {
|
|
|
21
21
|
clientJwksSchema,
|
|
22
22
|
clientMetadataDocumentSchema
|
|
23
23
|
} from "./cimd.types.mjs";
|
|
24
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
assertHostNotSsrf,
|
|
26
|
+
blocksPrivateAddressesFor,
|
|
27
|
+
hasOnlyLocalhostRedirectUris,
|
|
28
|
+
isCimdClientId,
|
|
29
|
+
validateClientIdUrl
|
|
30
|
+
} from "./cimd.validator.mjs";
|
|
25
31
|
var MAX_CACHED_KEY_SETS = 500;
|
|
26
32
|
var KEY_SET_REFRESH_COOLDOWN_MS = 3e4;
|
|
27
33
|
var CimdService = class {
|
|
@@ -130,15 +136,15 @@ var CimdService = class {
|
|
|
130
136
|
}
|
|
131
137
|
this.logger.info(`Fetching CIMD document: ${clientId}`);
|
|
132
138
|
const { document, headers } = await this.fetchMetadataDocument(clientId);
|
|
133
|
-
this.validateDocument(clientId, document);
|
|
134
|
-
if (this.securityConfig.warnOnLocalhostRedirects && hasOnlyLocalhostRedirectUris(
|
|
139
|
+
const metadata = this.validateDocument(clientId, document);
|
|
140
|
+
if (this.securityConfig.warnOnLocalhostRedirects && hasOnlyLocalhostRedirectUris(metadata.redirect_uris)) {
|
|
135
141
|
this.logger.warn(`CIMD client "${clientId}" has only localhost redirect URIs - this may be a development client`);
|
|
136
142
|
}
|
|
137
|
-
await cache.set(clientId,
|
|
143
|
+
await cache.set(clientId, metadata, headers);
|
|
138
144
|
const entry = await cache.get(clientId);
|
|
139
145
|
return {
|
|
140
146
|
isCimdClient: true,
|
|
141
|
-
metadata
|
|
147
|
+
metadata,
|
|
142
148
|
fromCache: false,
|
|
143
149
|
expiresAt: entry?.expiresAt,
|
|
144
150
|
etag: entry?.etag,
|
|
@@ -300,8 +306,9 @@ var CimdService = class {
|
|
|
300
306
|
let redirectCount = 0;
|
|
301
307
|
let isFirstRequest = true;
|
|
302
308
|
while (true) {
|
|
303
|
-
|
|
304
|
-
|
|
309
|
+
const hostname = new URL(currentUrl).hostname;
|
|
310
|
+
if (blocksPrivateAddressesFor(hostname, this.securityConfig)) {
|
|
311
|
+
await assertHostNotSsrf(hostname, url);
|
|
305
312
|
}
|
|
306
313
|
const headers = {
|
|
307
314
|
Accept: "application/json"
|
|
@@ -436,6 +443,7 @@ var CimdService = class {
|
|
|
436
443
|
*
|
|
437
444
|
* @param clientId - The URL from which the document was fetched
|
|
438
445
|
* @param document - The document to validate
|
|
446
|
+
* @returns The parsed document, with the schema's defaults for the fields it leaves out
|
|
439
447
|
*/
|
|
440
448
|
validateDocument(clientId, document) {
|
|
441
449
|
const result = clientMetadataDocumentSchema.safeParse(document);
|
|
@@ -449,6 +457,7 @@ var CimdService = class {
|
|
|
449
457
|
if (result.data.client_id !== clientId) {
|
|
450
458
|
throw new CimdClientIdMismatchError(clientId, result.data.client_id);
|
|
451
459
|
}
|
|
460
|
+
return result.data;
|
|
452
461
|
}
|
|
453
462
|
};
|
|
454
463
|
var LOOPBACK_HOSTNAMES = /* @__PURE__ */ new Set(["127.0.0.1", "::1", "[::1]", "localhost"]);
|
|
@@ -34,6 +34,10 @@ function isLocalhostHost(hostname) {
|
|
|
34
34
|
const lower = hostname.toLowerCase();
|
|
35
35
|
return lower === "localhost" || lower === "127.0.0.1" || lower === "[::1]" || lower.endsWith(".localhost");
|
|
36
36
|
}
|
|
37
|
+
function blocksPrivateAddressesFor(hostname, securityConfig) {
|
|
38
|
+
if (!(securityConfig?.blockPrivateIPs ?? true)) return false;
|
|
39
|
+
return !(securityConfig?.allowInsecureForTesting && isLocalhostHost(hostname));
|
|
40
|
+
}
|
|
37
41
|
function validateClientIdUrl(clientId, securityConfig) {
|
|
38
42
|
if (!clientId || typeof clientId !== "string") {
|
|
39
43
|
throw new InvalidClientIdUrlError(clientId || "", "client_id must be a non-empty string");
|
|
@@ -57,7 +61,6 @@ function validateClientIdUrl(clientId, securityConfig) {
|
|
|
57
61
|
);
|
|
58
62
|
}
|
|
59
63
|
const config = {
|
|
60
|
-
blockPrivateIPs: securityConfig?.blockPrivateIPs ?? true,
|
|
61
64
|
allowedDomains: securityConfig?.allowedDomains,
|
|
62
65
|
blockedDomains: securityConfig?.blockedDomains
|
|
63
66
|
};
|
|
@@ -71,7 +74,7 @@ function validateClientIdUrl(clientId, securityConfig) {
|
|
|
71
74
|
throw new CimdSecurityError(clientId, `Domain "${url.hostname}" is blocked`);
|
|
72
75
|
}
|
|
73
76
|
}
|
|
74
|
-
if (
|
|
77
|
+
if (blocksPrivateAddressesFor(url.hostname, securityConfig)) {
|
|
75
78
|
const ssrfCheck = checkSsrfProtection(url.hostname);
|
|
76
79
|
if (!ssrfCheck.allowed) {
|
|
77
80
|
throw new CimdSecurityError(clientId, ssrfCheck.reason);
|
|
@@ -209,6 +212,7 @@ function hasOnlyLocalhostRedirectUris(redirectUris) {
|
|
|
209
212
|
}
|
|
210
213
|
export {
|
|
211
214
|
assertHostNotSsrf,
|
|
215
|
+
blocksPrivateAddressesFor,
|
|
212
216
|
checkSsrfProtection,
|
|
213
217
|
hasOnlyLocalhostRedirectUris,
|
|
214
218
|
isCimdClientId,
|
|
@@ -81,6 +81,13 @@ function detectAuthProviders(parentAuth, apps) {
|
|
|
81
81
|
}
|
|
82
82
|
const providerId = deriveProviderId(app.auth);
|
|
83
83
|
const existing = providers.get(providerId);
|
|
84
|
+
const providerUrl = getProviderUrl(app.auth);
|
|
85
|
+
if (existing && providerUrl && existing.providerUrl && !sameIssuer(providerUrl, existing.providerUrl)) {
|
|
86
|
+
const owner = existing.isParentProvider ? "the server" : `app "${existing.appIds[0]}"`;
|
|
87
|
+
validationErrors.push(
|
|
88
|
+
`App "${app.id}" names the identity provider ${providerUrl} and ${owner} names ${existing.providerUrl}, but both get the provider id "${providerId}", so a token from one would be accepted for the other. Set a distinct providerConfig.id on each.`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
84
91
|
if (existing) {
|
|
85
92
|
existing.appIds.push(app.id);
|
|
86
93
|
const newScopes = extractScopes(app.auth);
|
|
@@ -137,6 +144,14 @@ function staticParentEnforces(parentAuth, appAuth) {
|
|
|
137
144
|
const sameChallenge = parent.data.header.toLowerCase() === app.data.header.toLowerCase() && parent.data.scheme === app.data.scheme;
|
|
138
145
|
return sameChallenge && parent.data.tokens.every((token) => app.data.tokens.includes(token));
|
|
139
146
|
}
|
|
147
|
+
function sameIssuer(first, second) {
|
|
148
|
+
return issuerIdentity(first) === issuerIdentity(second);
|
|
149
|
+
}
|
|
150
|
+
function issuerIdentity(url) {
|
|
151
|
+
let identity = URL.canParse(url) ? new URL(url).href : url;
|
|
152
|
+
while (identity.endsWith("/")) identity = identity.slice(0, -1);
|
|
153
|
+
return identity;
|
|
154
|
+
}
|
|
140
155
|
function getProviderUrl(options) {
|
|
141
156
|
if (isTransparentMode(options)) {
|
|
142
157
|
return options.provider;
|
|
@@ -9,7 +9,14 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
// libs/auth/src/jwks/jwks.service.ts
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
bytesToHex,
|
|
14
|
+
isProduction,
|
|
15
|
+
isRedirectResponse,
|
|
16
|
+
randomBytes,
|
|
17
|
+
readTextWithLimit,
|
|
18
|
+
rsaVerify
|
|
19
|
+
} from "@frontmcp/utils";
|
|
13
20
|
import { resolveAndCheckHostname } from "../cimd/cimd.validator.mjs";
|
|
14
21
|
import { noopAuthLogger } from "../common/auth-logger.interface.mjs";
|
|
15
22
|
import { loadJose } from "./jose.loader.mjs";
|
|
@@ -438,14 +445,7 @@ var JwksService = class {
|
|
|
438
445
|
continue;
|
|
439
446
|
}
|
|
440
447
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
441
|
-
const
|
|
442
|
-
if (contentLength && Number(contentLength) > MAX_JWKS_FETCH_BYTES) {
|
|
443
|
-
throw new Error("response too large");
|
|
444
|
-
}
|
|
445
|
-
const text = await res.text();
|
|
446
|
-
if (text.length > MAX_JWKS_FETCH_BYTES) {
|
|
447
|
-
throw new Error("response too large");
|
|
448
|
-
}
|
|
448
|
+
const text = await readTextWithLimit(res, MAX_JWKS_FETCH_BYTES);
|
|
449
449
|
return JSON.parse(text);
|
|
450
450
|
}
|
|
451
451
|
} finally {
|
|
@@ -87,13 +87,20 @@ var LoopbackTokenAuth = class {
|
|
|
87
87
|
const previous = this.failures.get(client);
|
|
88
88
|
const count = previous && now - previous.lastAt <= lockoutMs ? previous.count + 1 : 1;
|
|
89
89
|
const locked = count >= maxFailedAttempts;
|
|
90
|
-
|
|
90
|
+
this.failures.delete(client);
|
|
91
|
+
if (this.failures.size >= MAX_TRACKED_CLIENTS) this.forgetOldestClients(now);
|
|
91
92
|
this.failures.set(client, { count: locked ? 0 : count, lastAt: now, lockedUntil: locked ? now + lockoutMs : 0 });
|
|
92
93
|
return locked ? LOCKED : INVALID;
|
|
93
94
|
}
|
|
94
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Clients are kept in the order of their last failure, so the idle ones come first: drop them, and stop at the
|
|
97
|
+
* first active one, unless the map is still full, when that oldest active one goes too.
|
|
98
|
+
*/
|
|
99
|
+
forgetOldestClients(now) {
|
|
95
100
|
for (const [client, failures] of this.failures) {
|
|
96
|
-
|
|
101
|
+
const idle = failures.lockedUntil <= now && now - failures.lastAt > this.options.lockoutMs;
|
|
102
|
+
if (!idle && this.failures.size < MAX_TRACKED_CLIENTS) return;
|
|
103
|
+
this.failures.delete(client);
|
|
97
104
|
}
|
|
98
105
|
}
|
|
99
106
|
};
|