@authup/server-core 1.0.0-beta.60 → 1.0.0-beta.62

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.
Files changed (172) hide show
  1. package/dist/adapters/database/domains/client-scope/entity.d.ts.map +1 -1
  2. package/dist/adapters/database/domains/client-scope/entity.mjs +16 -8
  3. package/dist/adapters/database/domains/client-scope/entity.mjs.map +1 -1
  4. package/dist/adapters/database/domains/identity-provider-account/entity.d.ts.map +1 -1
  5. package/dist/adapters/database/domains/identity-provider-account/entity.mjs +1 -1
  6. package/dist/adapters/database/domains/identity-provider-account/entity.mjs.map +1 -1
  7. package/dist/adapters/database/migrations/mysql/1786631686318-SortIndexesAndAccountUniqueness.d.ts +50 -0
  8. package/dist/adapters/database/migrations/mysql/1786631686318-SortIndexesAndAccountUniqueness.d.ts.map +1 -0
  9. package/dist/adapters/database/migrations/mysql/1786631686318-SortIndexesAndAccountUniqueness.mjs +86 -0
  10. package/dist/adapters/database/migrations/mysql/1786631686318-SortIndexesAndAccountUniqueness.mjs.map +1 -0
  11. package/dist/adapters/database/migrations/postgres/1786631686318-SortIndexesAndAccountUniqueness.d.ts +42 -0
  12. package/dist/adapters/database/migrations/postgres/1786631686318-SortIndexesAndAccountUniqueness.d.ts.map +1 -0
  13. package/dist/adapters/database/migrations/postgres/1786631686318-SortIndexesAndAccountUniqueness.mjs +80 -0
  14. package/dist/adapters/database/migrations/postgres/1786631686318-SortIndexesAndAccountUniqueness.mjs.map +1 -0
  15. package/dist/adapters/http/adapters/oauth2/grant-types/utils/credentials.mjs +1 -1
  16. package/dist/adapters/http/adapters/oauth2/grant-types/utils/guess.mjs +1 -1
  17. package/dist/adapters/http/controllers/entities/client/module.mjs +2 -2
  18. package/dist/adapters/http/controllers/entities/identity-provider/module.d.ts +12 -8
  19. package/dist/adapters/http/controllers/entities/identity-provider/module.d.ts.map +1 -1
  20. package/dist/adapters/http/controllers/entities/identity-provider/module.mjs +137 -88
  21. package/dist/adapters/http/controllers/entities/identity-provider/module.mjs.map +1 -1
  22. package/dist/adapters/http/controllers/entities/identity-provider/types.d.ts +7 -5
  23. package/dist/adapters/http/controllers/entities/identity-provider/types.d.ts.map +1 -1
  24. package/dist/adapters/http/controllers/entities/realm/module.mjs +2 -2
  25. package/dist/adapters/http/controllers/entities/session/module.mjs +2 -2
  26. package/dist/adapters/http/controllers/entities/user/module.mjs +2 -2
  27. package/dist/adapters/http/controllers/entities/user-authenticator/module.mjs +2 -2
  28. package/dist/adapters/http/controllers/workflows/authorize/module.d.ts.map +1 -1
  29. package/dist/adapters/http/controllers/workflows/authorize/module.mjs +9 -5
  30. package/dist/adapters/http/controllers/workflows/authorize/module.mjs.map +1 -1
  31. package/dist/adapters/http/controllers/workflows/token/utils/extract.mjs +2 -1
  32. package/dist/adapters/http/controllers/workflows/token/utils/extract.mjs.map +1 -1
  33. package/dist/adapters/http/middleware/built-in/assets.mjs +1 -1
  34. package/dist/adapters/http/request/helpers/param-id.d.ts.map +1 -1
  35. package/dist/adapters/http/request/helpers/param-id.mjs +2 -1
  36. package/dist/adapters/http/request/helpers/param-id.mjs.map +1 -1
  37. package/dist/adapters/http/ui/account-console/module.mjs +1 -1
  38. package/dist/adapters/http/ui/auth-console/http-client.mjs +1 -1
  39. package/dist/adapters/http/ui/auth-console/module.mjs +1 -1
  40. package/dist/adapters/http/ui/console-packages/module.d.ts +1 -1
  41. package/dist/adapters/http/ui/console-packages/module.mjs +2 -2
  42. package/dist/adapters/http/ui/console-packages/module.mjs.map +1 -1
  43. package/dist/adapters/http/ui/shared/html.d.ts.map +1 -1
  44. package/dist/adapters/http/ui/shared/html.mjs +1 -0
  45. package/dist/adapters/http/ui/shared/html.mjs.map +1 -1
  46. package/dist/app/index.mjs +3 -1
  47. package/dist/app/modules/config/read/env.mjs +1 -1
  48. package/dist/app/modules/database/repositories/event/repository.d.ts +2 -2
  49. package/dist/app/modules/database/repositories/event/repository.d.ts.map +1 -1
  50. package/dist/app/modules/database/repositories/event/repository.mjs +23 -6
  51. package/dist/app/modules/database/repositories/event/repository.mjs.map +1 -1
  52. package/dist/app/modules/database/repositories/identity-provider-account/repository.d.ts.map +1 -1
  53. package/dist/app/modules/database/repositories/identity-provider-account/repository.mjs +9 -1
  54. package/dist/app/modules/database/repositories/identity-provider-account/repository.mjs.map +1 -1
  55. package/dist/app/modules/http/modules/controller.d.ts.map +1 -1
  56. package/dist/app/modules/http/modules/controller.mjs +18 -6
  57. package/dist/app/modules/http/modules/controller.mjs.map +1 -1
  58. package/dist/app/modules/identity/constants.d.ts +2 -1
  59. package/dist/app/modules/identity/constants.d.ts.map +1 -1
  60. package/dist/app/modules/identity/constants.mjs +1 -0
  61. package/dist/app/modules/identity/constants.mjs.map +1 -1
  62. package/dist/app/modules/identity/index.mjs +3 -1
  63. package/dist/app/modules/identity/module.d.ts.map +1 -1
  64. package/dist/app/modules/identity/module.mjs +13 -3
  65. package/dist/app/modules/identity/module.mjs.map +1 -1
  66. package/dist/app/modules/identity/repositories/constants.d.ts +4 -0
  67. package/dist/app/modules/identity/repositories/constants.d.ts.map +1 -0
  68. package/dist/app/modules/identity/repositories/constants.mjs +12 -0
  69. package/dist/app/modules/identity/repositories/constants.mjs.map +1 -0
  70. package/dist/app/modules/identity/repositories/index.d.ts +1 -0
  71. package/dist/app/modules/identity/repositories/index.d.ts.map +1 -1
  72. package/dist/app/modules/identity/repositories/index.mjs +3 -1
  73. package/dist/app/modules/identity/repositories/provider/index.d.ts +1 -0
  74. package/dist/app/modules/identity/repositories/provider/index.d.ts.map +1 -1
  75. package/dist/app/modules/identity/repositories/provider/index.mjs +2 -1
  76. package/dist/app/modules/identity/repositories/provider/link.d.ts +9 -0
  77. package/dist/app/modules/identity/repositories/provider/link.d.ts.map +1 -0
  78. package/dist/app/modules/identity/repositories/provider/link.mjs +34 -0
  79. package/dist/app/modules/identity/repositories/provider/link.mjs.map +1 -0
  80. package/dist/app/modules/identity/repositories/user.d.ts +1 -0
  81. package/dist/app/modules/identity/repositories/user.d.ts.map +1 -1
  82. package/dist/app/modules/identity/repositories/user.mjs +3 -0
  83. package/dist/app/modules/identity/repositories/user.mjs.map +1 -1
  84. package/dist/app/modules/index.mjs +3 -1
  85. package/dist/app/modules/oauth2/repositories/authorize/state/repository.d.ts +1 -2
  86. package/dist/app/modules/oauth2/repositories/authorize/state/repository.d.ts.map +1 -1
  87. package/dist/app/modules/oauth2/repositories/authorize/state/repository.mjs +7 -16
  88. package/dist/app/modules/oauth2/repositories/authorize/state/repository.mjs.map +1 -1
  89. package/dist/app/modules/runtime/module.mjs +2 -2
  90. package/dist/cli/commands/migration.mjs +1 -1
  91. package/dist/core/entities/client-scope/schema.d.ts.map +1 -1
  92. package/dist/core/entities/client-scope/schema.mjs +16 -1
  93. package/dist/core/entities/client-scope/schema.mjs.map +1 -1
  94. package/dist/core/entities/event/constants.d.ts +9 -0
  95. package/dist/core/entities/event/constants.d.ts.map +1 -1
  96. package/dist/core/entities/event/constants.mjs +9 -1
  97. package/dist/core/entities/event/constants.mjs.map +1 -1
  98. package/dist/core/entities/event/index.mjs +2 -2
  99. package/dist/core/entities/event/types.d.ts +11 -2
  100. package/dist/core/entities/event/types.d.ts.map +1 -1
  101. package/dist/core/entities/identity-provider-account/types.d.ts +6 -0
  102. package/dist/core/entities/identity-provider-account/types.d.ts.map +1 -1
  103. package/dist/core/entities/identity-provider-account/types.mjs.map +1 -1
  104. package/dist/core/entities/index.mjs +2 -2
  105. package/dist/core/identity/entities/user/types.d.ts +1 -0
  106. package/dist/core/identity/entities/user/types.d.ts.map +1 -1
  107. package/dist/core/identity/index.mjs +12 -11
  108. package/dist/core/identity/provider/account/constants.d.ts +10 -0
  109. package/dist/core/identity/provider/account/constants.d.ts.map +1 -0
  110. package/dist/core/identity/provider/account/constants.mjs +16 -0
  111. package/dist/core/identity/provider/account/constants.mjs.map +1 -0
  112. package/dist/core/identity/provider/account/index.d.ts +1 -0
  113. package/dist/core/identity/provider/account/index.d.ts.map +1 -1
  114. package/dist/core/identity/provider/account/index.mjs +2 -1
  115. package/dist/core/identity/provider/account/module.d.ts +3 -0
  116. package/dist/core/identity/provider/account/module.d.ts.map +1 -1
  117. package/dist/core/identity/provider/account/module.mjs +54 -27
  118. package/dist/core/identity/provider/account/module.mjs.map +1 -1
  119. package/dist/core/identity/provider/account/types.d.ts +42 -0
  120. package/dist/core/identity/provider/account/types.d.ts.map +1 -1
  121. package/dist/core/identity/provider/authentication/index.mjs +1 -1
  122. package/dist/core/identity/provider/authentication/protocols/oauth2/module.d.ts +18 -0
  123. package/dist/core/identity/provider/authentication/protocols/oauth2/module.d.ts.map +1 -1
  124. package/dist/core/identity/provider/authentication/protocols/oauth2/module.mjs +55 -2
  125. package/dist/core/identity/provider/authentication/protocols/oauth2/module.mjs.map +1 -1
  126. package/dist/core/identity/provider/authentication/protocols/oauth2/types.d.ts +2 -0
  127. package/dist/core/identity/provider/authentication/protocols/oauth2/types.d.ts.map +1 -1
  128. package/dist/core/identity/provider/authentication/protocols/open-id/module.d.ts +0 -3
  129. package/dist/core/identity/provider/authentication/protocols/open-id/module.d.ts.map +1 -1
  130. package/dist/core/identity/provider/authentication/protocols/open-id/module.mjs +0 -17
  131. package/dist/core/identity/provider/authentication/protocols/open-id/module.mjs.map +1 -1
  132. package/dist/core/identity/provider/index.mjs +12 -11
  133. package/dist/core/index.mjs +15 -12
  134. package/dist/core/oauth2/authorization/code-request/verifier/module.d.ts.map +1 -1
  135. package/dist/core/oauth2/authorization/code-request/verifier/module.mjs +9 -2
  136. package/dist/core/oauth2/authorization/code-request/verifier/module.mjs.map +1 -1
  137. package/dist/core/oauth2/authorization/state/module.mjs +1 -2
  138. package/dist/core/oauth2/authorization/state/module.mjs.map +1 -1
  139. package/dist/core/oauth2/authorization/state/types.d.ts +5 -2
  140. package/dist/core/oauth2/authorization/state/types.d.ts.map +1 -1
  141. package/dist/core/oauth2/end-session/service.d.ts.map +1 -1
  142. package/dist/core/oauth2/end-session/service.mjs +1 -0
  143. package/dist/core/oauth2/end-session/service.mjs.map +1 -1
  144. package/dist/core/oauth2/federated-login/index.d.ts +3 -0
  145. package/dist/core/oauth2/federated-login/index.d.ts.map +1 -0
  146. package/dist/core/oauth2/federated-login/index.mjs +6 -0
  147. package/dist/core/oauth2/federated-login/module.d.ts +20 -0
  148. package/dist/core/oauth2/federated-login/module.d.ts.map +1 -0
  149. package/dist/core/oauth2/federated-login/module.mjs +137 -0
  150. package/dist/core/oauth2/federated-login/module.mjs.map +1 -0
  151. package/dist/core/oauth2/federated-login/types.d.ts +138 -0
  152. package/dist/core/oauth2/federated-login/types.d.ts.map +1 -0
  153. package/dist/core/oauth2/federated-login/types.mjs +31 -0
  154. package/dist/core/oauth2/federated-login/types.mjs.map +1 -0
  155. package/dist/core/oauth2/index.d.ts +1 -0
  156. package/dist/core/oauth2/index.d.ts.map +1 -1
  157. package/dist/core/oauth2/index.mjs +4 -1
  158. package/dist/core/provisioning/entities/policy/validator.d.ts.map +1 -1
  159. package/dist/core/provisioning/entities/policy/validator.mjs +2 -1
  160. package/dist/core/provisioning/entities/policy/validator.mjs.map +1 -1
  161. package/dist/core/provisioning/entities/realm/wildcard-validator.d.ts.map +1 -1
  162. package/dist/core/provisioning/entities/realm/wildcard-validator.mjs +2 -1
  163. package/dist/core/provisioning/entities/realm/wildcard-validator.mjs.map +1 -1
  164. package/dist/core/security/policy/evaluator.mjs.map +1 -1
  165. package/dist/index.mjs +3 -1
  166. package/dist/node_modules/@ebec/core/dist/index.mjs +130 -0
  167. package/dist/node_modules/@ebec/core/dist/index.mjs.map +1 -0
  168. package/dist/swagger.json +146 -18
  169. package/dist/utils/error.d.ts.map +1 -1
  170. package/dist/utils/error.mjs +5 -6
  171. package/dist/utils/error.mjs.map +1 -1
  172. package/package.json +28 -28
@@ -0,0 +1,20 @@
1
+ import type { Logger } from '@authup/server-kit';
2
+ import type { IEventService, IRealmRepository } from '../../entities/index.ts';
3
+ import type { IIdentityProviderAccountManager } from '../../identity/provider/account/types.ts';
4
+ import type { IOAuth2AccessPolicyEvaluator } from '../access-policy/index.ts';
5
+ import type { IOAuth2AuthorizationCodeIssuer, IOAuth2AuthorizationCodeRequestVerifier } from '../authorization/index.ts';
6
+ import type { IOAuth2FederatedLoginService, OAuth2FederatedLoginAuthenticatorFactory, OAuth2FederatedLoginCompleteInput, OAuth2FederatedLoginCompleteResult, OAuth2FederatedLoginServiceContext, OAuth2FederatedLoginServiceOptions } from './types.ts';
7
+ export declare class OAuth2FederatedLoginService implements IOAuth2FederatedLoginService {
8
+ protected options: OAuth2FederatedLoginServiceOptions;
9
+ protected accountManager: IIdentityProviderAccountManager;
10
+ protected realmRepository: IRealmRepository;
11
+ protected codeRequestVerifier: IOAuth2AuthorizationCodeRequestVerifier;
12
+ protected codeIssuer: IOAuth2AuthorizationCodeIssuer;
13
+ protected accessPolicyEvaluator?: IOAuth2AccessPolicyEvaluator;
14
+ protected eventService?: IEventService;
15
+ protected logger?: Logger;
16
+ protected authenticatorFactory: OAuth2FederatedLoginAuthenticatorFactory;
17
+ constructor(ctx: OAuth2FederatedLoginServiceContext);
18
+ complete(input: OAuth2FederatedLoginCompleteInput): Promise<OAuth2FederatedLoginCompleteResult>;
19
+ }
20
+ //# sourceMappingURL=module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../src/core/oauth2/federated-login/module.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAG/E,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,0CAA0C,CAAC;AAGhG,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AAC9E,OAAO,KAAK,EACR,8BAA8B,EAC9B,uCAAuC,EAE1C,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EACR,4BAA4B,EAC5B,wCAAwC,EACxC,iCAAiC,EACjC,kCAAkC,EAClC,kCAAkC,EAClC,kCAAkC,EACrC,MAAM,YAAY,CAAC;AAGpB,qBAAa,2BAA4B,YAAW,4BAA4B;IAC5E,SAAS,CAAC,OAAO,EAAG,kCAAkC,CAAC;IAEvD,SAAS,CAAC,cAAc,EAAG,+BAA+B,CAAC;IAE3D,SAAS,CAAC,eAAe,EAAG,gBAAgB,CAAC;IAE7C,SAAS,CAAC,mBAAmB,EAAG,uCAAuC,CAAC;IAExE,SAAS,CAAC,UAAU,EAAG,8BAA8B,CAAC;IAEtD,SAAS,CAAC,qBAAqB,CAAC,EAAG,4BAA4B,CAAC;IAEhE,SAAS,CAAC,YAAY,CAAC,EAAG,aAAa,CAAC;IAExC,SAAS,CAAC,MAAM,CAAC,EAAG,MAAM,CAAC;IAE3B,SAAS,CAAC,oBAAoB,EAAG,wCAAwC,CAAC;gBAE9D,GAAG,EAAE,kCAAkC;IAqB7C,QAAQ,CAAC,KAAK,EAAE,iCAAiC,GAAG,OAAO,CAAC,kCAAkC,CAAC;CA0LxG"}
@@ -0,0 +1,137 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { toIdentityPolicyData } from "../../identity/permission/identity-policy-data.mjs";
5
+ import { createIdentityProviderOAuth2Authenticator } from "../../identity/provider/authentication/factory.mjs";
6
+ import { OAuth2FederatedLoginRefusal } from "./types.mjs";
7
+ import { isSafeRedirectURLScheme } from "@authup/kit";
8
+ import { EventName, EventRefType, EventScope, IdentityType, SessionAuthMethod } from "@authup/core-kit";
9
+ import { InternalError } from "@authup/errors";
10
+ import { OAuth2ErrorCode, OAuth2RequestError, isOAuth2Error } from "@authup/specs";
11
+ //#region src/core/oauth2/federated-login/module.ts
12
+ var OAuth2FederatedLoginService = class {
13
+ options;
14
+ accountManager;
15
+ realmRepository;
16
+ codeRequestVerifier;
17
+ codeIssuer;
18
+ accessPolicyEvaluator;
19
+ eventService;
20
+ logger;
21
+ authenticatorFactory;
22
+ constructor(ctx) {
23
+ this.options = ctx.options;
24
+ this.accountManager = ctx.accountManager;
25
+ this.realmRepository = ctx.realmRepository;
26
+ this.codeRequestVerifier = ctx.codeRequestVerifier;
27
+ this.codeIssuer = ctx.codeIssuer;
28
+ this.accessPolicyEvaluator = ctx.accessPolicyEvaluator;
29
+ this.eventService = ctx.eventService;
30
+ this.logger = ctx.logger;
31
+ this.authenticatorFactory = ctx.authenticatorFactory ?? ((provider, options) => createIdentityProviderOAuth2Authenticator({
32
+ accountManager: this.accountManager,
33
+ provider,
34
+ logger: options.logger,
35
+ options: {
36
+ baseURL: options.baseURL,
37
+ clientId: options.clientId
38
+ }
39
+ }));
40
+ }
41
+ async complete(input) {
42
+ const { code, codeRequest, provider } = input;
43
+ let verified;
44
+ try {
45
+ verified = await this.codeRequestVerifier.verify(codeRequest);
46
+ } catch (e) {
47
+ if (!isOAuth2Error(e)) throw e;
48
+ return {
49
+ kind: "refused",
50
+ refusal: OAuth2FederatedLoginRefusal.CODE_REQUEST,
51
+ codeRequest
52
+ };
53
+ }
54
+ if (provider.realmId && verified.client.realmId !== provider.realmId) throw OAuth2RequestError.malformed("The provider and client realm do not match.");
55
+ const redirectUri = verified.data.redirect_uri;
56
+ if (!verified.redirectUriVerified || !redirectUri) throw OAuth2RequestError.malformed("The redirect_uri was not verified.");
57
+ if (!isSafeRedirectURLScheme(redirectUri)) throw new InternalError("The redirect_uri scheme is not allowed.");
58
+ if (!provider.enabled) return {
59
+ kind: "refused",
60
+ refusal: OAuth2FederatedLoginRefusal.PROVIDER_DISABLED,
61
+ error: OAuth2ErrorCode.LOGIN_REQUIRED,
62
+ codeRequest: verified.data
63
+ };
64
+ const user = await this.authenticatorFactory(provider, {
65
+ baseURL: this.options.baseURL,
66
+ clientId: verified.data.client_id,
67
+ logger: this.logger
68
+ }).authenticate({ code });
69
+ if (!user.active) return {
70
+ kind: "refused",
71
+ refusal: OAuth2FederatedLoginRefusal.USER_INACTIVE,
72
+ error: OAuth2ErrorCode.ACCESS_DENIED,
73
+ codeRequest: verified.data
74
+ };
75
+ const realm = await this.realmRepository.resolve(provider.realmId, true);
76
+ if (verified.client.accessPolicyId) {
77
+ let allowed = false;
78
+ const subject = toIdentityPolicyData({
79
+ type: IdentityType.USER,
80
+ data: {
81
+ ...user,
82
+ realm
83
+ }
84
+ });
85
+ if (this.accessPolicyEvaluator && subject) allowed = await this.accessPolicyEvaluator.evaluate(verified.client.accessPolicyId, subject);
86
+ if (!allowed) return {
87
+ kind: "refused",
88
+ refusal: OAuth2FederatedLoginRefusal.ACCESS_DENIED,
89
+ error: OAuth2ErrorCode.ACCESS_DENIED,
90
+ codeRequest: verified.data
91
+ };
92
+ }
93
+ const authorizationCode = await this.codeIssuer.issue(verified.data, {
94
+ type: IdentityType.USER,
95
+ data: {
96
+ ...user,
97
+ realm
98
+ }
99
+ }, { authMethod: SessionAuthMethod.EXTERNAL });
100
+ await this.eventService?.record({
101
+ scope: EventScope.OAUTH2,
102
+ name: EventName.AUTHORIZE,
103
+ refType: EventRefType.CLIENT,
104
+ refId: verified.data.client_id ?? null,
105
+ clientId: verified.data.client_id ?? null,
106
+ sessionId: null,
107
+ actorType: IdentityType.USER,
108
+ actorId: user.id,
109
+ actorName: user.name,
110
+ realmId: verified.data.realm_id ?? realm.id,
111
+ requestIpAddress: input.request?.ipAddress ?? null,
112
+ requestUserAgent: input.request?.userAgent ?? null,
113
+ data: {
114
+ reason: "federated",
115
+ providerId: provider.id,
116
+ providerName: provider.name,
117
+ ...verified.data.scope ? { scope: verified.data.scope } : {}
118
+ }
119
+ });
120
+ return {
121
+ kind: "issued",
122
+ redirectUri,
123
+ code: authorizationCode.id,
124
+ state: verified.data.state,
125
+ codeRequest: verified.data,
126
+ client: {
127
+ id: verified.client.id,
128
+ name: verified.client.name,
129
+ displayName: verified.client.displayName ?? null
130
+ }
131
+ };
132
+ }
133
+ };
134
+ //#endregion
135
+ export { OAuth2FederatedLoginService };
136
+
137
+ //# sourceMappingURL=module.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.mjs","names":["isSafeRedirectURLScheme","EventName","EventRefType","EventScope","IdentityType","SessionAuthMethod","InternalError","OAuth2ErrorCode","OAuth2RequestError","isOAuth2Error","createIdentityProviderOAuth2Authenticator","toIdentityPolicyData","OAuth2FederatedLoginRefusal","OAuth2FederatedLoginService","options","accountManager","realmRepository","codeRequestVerifier","codeIssuer","accessPolicyEvaluator","eventService","logger","authenticatorFactory","ctx","provider","baseURL","clientId","complete","input","code","codeRequest","verified","verify","e","kind","refusal","CODE_REQUEST","realmId","client","malformed","redirectUri","data","redirect_uri","redirectUriVerified","enabled","PROVIDER_DISABLED","error","LOGIN_REQUIRED","authenticator","client_id","user","authenticate","active","USER_INACTIVE","ACCESS_DENIED","realm","resolve","accessPolicyId","allowed","subject","type","USER","evaluate","authorizationCode","issue","authMethod","EXTERNAL","record","scope","OAUTH2","name","AUTHORIZE","refType","CLIENT","refId","sessionId","actorType","actorId","id","actorName","realm_id","requestIpAddress","request","ipAddress","requestUserAgent","userAgent","reason","providerId","providerName","state","displayName"],"sources":["../../../../src/core/oauth2/federated-login/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { isSafeRedirectURLScheme } from '@authup/kit';\nimport {\n EventName,\n EventRefType,\n EventScope,\n IdentityType,\n SessionAuthMethod,\n} from '@authup/core-kit';\nimport { InternalError } from '@authup/errors';\nimport type { Logger } from '@authup/server-kit';\nimport { OAuth2ErrorCode, OAuth2RequestError, isOAuth2Error } from '@authup/specs';\nimport type { IEventService, IRealmRepository } from '../../entities/index.ts';\n// Deep imports, never the `core/identity` barrel: it reaches back into\n// this module through the core barrel, and the cycle would TDZ-crash.\nimport type { IIdentityProviderAccountManager } from '../../identity/provider/account/types.ts';\nimport { createIdentityProviderOAuth2Authenticator } from '../../identity/provider/authentication/factory.ts';\nimport { toIdentityPolicyData } from '../../identity/permission/identity-policy-data.ts';\nimport type { IOAuth2AccessPolicyEvaluator } from '../access-policy/index.ts';\nimport type {\n IOAuth2AuthorizationCodeIssuer,\n IOAuth2AuthorizationCodeRequestVerifier,\n OAuth2AuthorizationCodeRequestVerificationResult,\n} from '../authorization/index.ts';\nimport type {\n IOAuth2FederatedLoginService,\n OAuth2FederatedLoginAuthenticatorFactory,\n OAuth2FederatedLoginCompleteInput,\n OAuth2FederatedLoginCompleteResult,\n OAuth2FederatedLoginServiceContext,\n OAuth2FederatedLoginServiceOptions,\n} from './types.ts';\nimport { OAuth2FederatedLoginRefusal } from './types.ts';\n\nexport class OAuth2FederatedLoginService implements IOAuth2FederatedLoginService {\n protected options : OAuth2FederatedLoginServiceOptions;\n\n protected accountManager : IIdentityProviderAccountManager;\n\n protected realmRepository : IRealmRepository;\n\n protected codeRequestVerifier : IOAuth2AuthorizationCodeRequestVerifier;\n\n protected codeIssuer : IOAuth2AuthorizationCodeIssuer;\n\n protected accessPolicyEvaluator? : IOAuth2AccessPolicyEvaluator;\n\n protected eventService? : IEventService;\n\n protected logger? : Logger;\n\n protected authenticatorFactory : OAuth2FederatedLoginAuthenticatorFactory;\n\n constructor(ctx: OAuth2FederatedLoginServiceContext) {\n this.options = ctx.options;\n this.accountManager = ctx.accountManager;\n this.realmRepository = ctx.realmRepository;\n this.codeRequestVerifier = ctx.codeRequestVerifier;\n this.codeIssuer = ctx.codeIssuer;\n this.accessPolicyEvaluator = ctx.accessPolicyEvaluator;\n this.eventService = ctx.eventService;\n this.logger = ctx.logger;\n this.authenticatorFactory = ctx.authenticatorFactory ??\n ((provider, options) => createIdentityProviderOAuth2Authenticator({\n accountManager: this.accountManager,\n provider,\n logger: options.logger,\n options: {\n baseURL: options.baseURL,\n clientId: options.clientId,\n },\n }));\n }\n\n async complete(input: OAuth2FederatedLoginCompleteInput): Promise<OAuth2FederatedLoginCompleteResult> {\n const {\n code, \n codeRequest, \n provider, \n } = input;\n\n // Re-verify the request BEFORE the provider's single-use code is\n // spent. It re-resolves the client (active, grant allowlist, scopes)\n // and re-matches the redirect_uri against the client's registered\n // patterns, so a client deactivated (or a pattern removed) while the\n // person was away at the provider cannot still receive a code, and a\n // refused completion provisions no user.\n //\n // Only a refusal is reported as one; a server failure keeps throwing.\n let verified : OAuth2AuthorizationCodeRequestVerificationResult;\n try {\n verified = await this.codeRequestVerifier.verify(codeRequest);\n } catch (e) {\n if (!isOAuth2Error(e)) {\n throw e;\n }\n\n return {\n kind: 'refused',\n refusal: OAuth2FederatedLoginRefusal.CODE_REQUEST,\n codeRequest,\n };\n }\n\n // The provider and the client must share a realm, the completion-side\n // half of the plan-041 binding. It rests on the client the\n // verification just resolved rather than the `realm_id` carried on\n // the state blob: that value is only there because `authorize-out`\n // stores the VERIFIED request, so a guard reading it would disappear\n // silently for any state that reached here without the stamp. A\n // realm-less (global) provider matches every client by design.\n if (provider.realmId && verified.client.realmId !== provider.realmId) {\n throw OAuth2RequestError.malformed('The provider and client realm do not match.');\n }\n\n // A stored code request always carries a redirect_uri (that mount is\n // required in OAuth2AuthorizationCodeRequestValidator, unlike\n // `state`), so a verified request is a verified redirect target. The\n // guard keeps the redirect decision resting on the match itself,\n // which is the only thing here that knows the uri was ever matched.\n const redirectUri = verified.data.redirect_uri;\n if (!verified.redirectUriVerified || !redirectUri) {\n throw OAuth2RequestError.malformed('The redirect_uri was not verified.');\n }\n\n // A non-http(s) target is navigated from the interstitial page, which\n // `location.assign`s it and renders it as an href, so a\n // script-capable scheme would execute on the IdP origin. The client\n // validator and the code-request verifier both refuse such a scheme;\n // this fails closed should either gap, and it runs before the\n // provider's single-use code is spent, a user provisioned or a code\n // minted.\n if (!isSafeRedirectURLScheme(redirectUri)) {\n throw new InternalError('The redirect_uri scheme is not allowed.');\n }\n\n // The provider must still be enabled, the rule the link path already\n // applies. Disabling a provider has to stop logins in flight too,\n // otherwise it only stops new ones. Checked before the exchange, so\n // the refusal contacts the provider not at all.\n if (!provider.enabled) {\n return {\n kind: 'refused',\n refusal: OAuth2FederatedLoginRefusal.PROVIDER_DISABLED,\n error: OAuth2ErrorCode.LOGIN_REQUIRED,\n codeRequest: verified.data,\n };\n }\n\n const authenticator = this.authenticatorFactory(provider, {\n baseURL: this.options.baseURL,\n clientId: verified.data.client_id,\n logger: this.logger,\n });\n\n const user = await authenticator.authenticate({ code });\n\n // The local login path refuses an inactive user (EntityInactiveError),\n // and a federated login must not be the way around that. Only\n // reachable for an already-linked user: a first login provisions an\n // active one.\n if (!user.active) {\n return {\n kind: 'refused',\n refusal: OAuth2FederatedLoginRefusal.USER_INACTIVE,\n error: OAuth2ErrorCode.ACCESS_DENIED,\n codeRequest: verified.data,\n };\n }\n\n const realm = await this.realmRepository.resolve(provider.realmId, true);\n\n // Application access policy (plan 052), federated leg. A policy id\n // with no wired evaluator denies (fail closed).\n if (verified.client.accessPolicyId) {\n let allowed = false;\n\n const subject = toIdentityPolicyData({\n type: IdentityType.USER,\n data: {\n ...user,\n realm,\n },\n });\n if (this.accessPolicyEvaluator && subject) {\n allowed = await this.accessPolicyEvaluator.evaluate(\n verified.client.accessPolicyId,\n subject,\n );\n }\n\n if (!allowed) {\n return {\n kind: 'refused',\n refusal: OAuth2FederatedLoginRefusal.ACCESS_DENIED,\n error: OAuth2ErrorCode.ACCESS_DENIED,\n codeRequest: verified.data,\n };\n }\n }\n\n // The WHOLE verified request reaches the issuer, never a hand-picked\n // subset: it carries code_challenge / code_challenge_method and nonce\n // (plus acr_values, which no redemption path reads yet). A code that\n // lost its PKCE challenge cannot be redeemed by a public client at\n // all (`PKCE is required for public clients`), which is every console\n // client.\n const authorizationCode = await this.codeIssuer.issue(\n verified.data,\n {\n type: IdentityType.USER,\n data: {\n ...user,\n realm,\n },\n },\n { authMethod: SessionAuthMethod.EXTERNAL },\n );\n\n // The interactive path records this in OAuth2Authorization.authorize();\n // this leg issues its code directly, so without an emit here a\n // federated authorization leaves no trace in auth_events while every\n // other one does. `reason: federated` is what tells the two apart:\n // there was no consent step to report. No session exists yet (the\n // /token exchange creates it), hence a null sessionId. Metrics stay\n // uninstrumented on this leg, as they already are.\n await this.eventService?.record({\n scope: EventScope.OAUTH2,\n name: EventName.AUTHORIZE,\n refType: EventRefType.CLIENT,\n refId: verified.data.client_id ?? null,\n clientId: verified.data.client_id ?? null,\n sessionId: null,\n actorType: IdentityType.USER,\n actorId: user.id,\n actorName: user.name,\n realmId: verified.data.realm_id ?? realm.id,\n requestIpAddress: input.request?.ipAddress ?? null,\n requestUserAgent: input.request?.userAgent ?? null,\n data: {\n reason: 'federated',\n providerId: provider.id,\n providerName: provider.name,\n ...(verified.data.scope ? { scope: verified.data.scope } : {}),\n },\n });\n\n return {\n kind: 'issued',\n redirectUri,\n code: authorizationCode.id,\n state: verified.data.state,\n codeRequest: verified.data,\n client: {\n id: verified.client.id,\n name: verified.client.name,\n displayName: verified.client.displayName ?? null,\n },\n };\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,138 @@
1
+ import type { OAuth2AuthorizationCodeRequest, OAuth2IdentityProvider, OpenIDIdentityProvider, User } from '@authup/core-kit';
2
+ import type { OAuth2ErrorCode } from '@authup/specs';
3
+ import type { Logger } from '@authup/server-kit';
4
+ import type { IIdentityProviderAccountManager } from '../../identity/provider/account/types.ts';
5
+ import type { IOAuth2Authenticator } from '../../identity/provider/authentication/protocols/index.ts';
6
+ import type { IEventService, IRealmRepository } from '../../entities/index.ts';
7
+ import type { IOAuth2AccessPolicyEvaluator } from '../access-policy/index.ts';
8
+ import type { IOAuth2AuthorizationCodeIssuer, IOAuth2AuthorizationCodeRequestVerifier } from '../authorization/index.ts';
9
+ export type OAuth2FederatedLoginCompleteInput = {
10
+ /**
11
+ * The provider the callback belongs to, already resolved and
12
+ * protocol-checked by the caller.
13
+ */
14
+ provider: OAuth2IdentityProvider | OpenIDIdentityProvider;
15
+ /**
16
+ * The RP's original authorization request, as `authorize-out` stored
17
+ * it on the state blob. It is re-verified here rather than trusted.
18
+ */
19
+ codeRequest: OAuth2AuthorizationCodeRequest;
20
+ /**
21
+ * The provider's single-use authorization code.
22
+ */
23
+ code: string;
24
+ request?: {
25
+ ipAddress?: string | null;
26
+ userAgent?: string | null;
27
+ };
28
+ };
29
+ /**
30
+ * Why a completion was refused. Every value is a REFUSAL — a decision the
31
+ * person at the browser has to be told about on the hosted login page —
32
+ * never a server failure, which keeps throwing.
33
+ */
34
+ export declare enum OAuth2FederatedLoginRefusal {
35
+ /**
36
+ * The stored code request no longer verifies (client deactivated or
37
+ * unknown, grant allowlist, scopes, redirect_uri mismatch). The hosted
38
+ * page re-runs the same verifier and renders the same refusal, so
39
+ * nothing is echoed and the bounce carries no marker.
40
+ */
41
+ CODE_REQUEST = "codeRequest",
42
+ /**
43
+ * The provider was disabled while the person was away at it.
44
+ */
45
+ PROVIDER_DISABLED = "providerDisabled",
46
+ /**
47
+ * The linked local user is inactive. The local login path refuses one
48
+ * too, and a federated login must not be the way around that.
49
+ */
50
+ USER_INACTIVE = "userInactive",
51
+ /**
52
+ * The client's access policy denied the identity (plan 052).
53
+ */
54
+ ACCESS_DENIED = "accessDenied"
55
+ }
56
+ export type OAuth2FederatedLoginRefusedResult = {
57
+ kind: 'refused';
58
+ refusal: OAuth2FederatedLoginRefusal;
59
+ /**
60
+ * The marker the hosted authorize page maps onto its own copy, when
61
+ * the refusal has one. Absent means "render the request again", which
62
+ * re-runs the verifier and states the reason itself.
63
+ */
64
+ error?: `${OAuth2ErrorCode}`;
65
+ /**
66
+ * The request to re-render. The verified copy when re-verification got
67
+ * that far, else the stored one.
68
+ */
69
+ codeRequest: OAuth2AuthorizationCodeRequest;
70
+ };
71
+ export type OAuth2FederatedLoginIssuedResult = {
72
+ kind: 'issued';
73
+ /**
74
+ * The verified redirect target of the RP. Its scheme passed
75
+ * `isSafeRedirectURLScheme`, so a caller may navigate it.
76
+ */
77
+ redirectUri: string;
78
+ /**
79
+ * The authorization code id the RP redeems at `/token`.
80
+ */
81
+ code: string;
82
+ state?: string;
83
+ /**
84
+ * The verified request, for a caller that has to re-render the hosted
85
+ * page alongside the redirect (the custom-scheme interstitial).
86
+ */
87
+ codeRequest: OAuth2AuthorizationCodeRequest;
88
+ client: {
89
+ id: string;
90
+ name: string;
91
+ displayName: string | null;
92
+ };
93
+ };
94
+ export type OAuth2FederatedLoginCompleteResult = OAuth2FederatedLoginRefusedResult | OAuth2FederatedLoginIssuedResult;
95
+ /**
96
+ * Completes the RP's original authorization request after an external
97
+ * provider sent the browser back (issue #3446). It owns the refusal ladder
98
+ * — realm match, code-request re-verification, redirect-scheme gate,
99
+ * provider and user state, access policy — and mints the authup code.
100
+ *
101
+ * Deliberately transport-free: it decides WHAT the answer is, and returns
102
+ * it as a discriminated result. The adapter decides how that answer
103
+ * reaches the browser (a redirect, or the interstitial page a custom
104
+ * scheme needs), because only the adapter knows the difference.
105
+ */
106
+ export interface IOAuth2FederatedLoginService {
107
+ complete(input: OAuth2FederatedLoginCompleteInput): Promise<OAuth2FederatedLoginCompleteResult>;
108
+ }
109
+ export type OAuth2FederatedLoginServiceOptions = {
110
+ /**
111
+ * The public base URL. Reaches the provider authenticator, which
112
+ * builds the callback URL the external token exchange must repeat.
113
+ */
114
+ baseURL: string;
115
+ };
116
+ /**
117
+ * Builds the authenticator that exchanges the provider's code. Injected
118
+ * rather than constructed inline so the refusal ladder can be exercised
119
+ * without an external provider; it defaults to the real preset/protocol
120
+ * factory.
121
+ */
122
+ export type OAuth2FederatedLoginAuthenticatorFactory = (provider: OAuth2IdentityProvider | OpenIDIdentityProvider, options: {
123
+ baseURL: string;
124
+ clientId?: string;
125
+ logger?: Logger;
126
+ }) => IOAuth2Authenticator<User>;
127
+ export type OAuth2FederatedLoginServiceContext = {
128
+ options: OAuth2FederatedLoginServiceOptions;
129
+ accountManager: IIdentityProviderAccountManager;
130
+ realmRepository: IRealmRepository;
131
+ codeRequestVerifier: IOAuth2AuthorizationCodeRequestVerifier;
132
+ codeIssuer: IOAuth2AuthorizationCodeIssuer;
133
+ accessPolicyEvaluator?: IOAuth2AccessPolicyEvaluator;
134
+ eventService?: IEventService;
135
+ logger?: Logger;
136
+ authenticatorFactory?: OAuth2FederatedLoginAuthenticatorFactory;
137
+ };
138
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/core/oauth2/federated-login/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ //#region src/core/oauth2/federated-login/types.ts
5
+ /**
6
+ * Why a completion was refused. Every value is a REFUSAL — a decision the
7
+ * person at the browser has to be told about on the hosted login page —
8
+ * never a server failure, which keeps throwing.
9
+ */ var OAuth2FederatedLoginRefusal = /*#__PURE__*/ function(OAuth2FederatedLoginRefusal) {
10
+ /**
11
+ * The stored code request no longer verifies (client deactivated or
12
+ * unknown, grant allowlist, scopes, redirect_uri mismatch). The hosted
13
+ * page re-runs the same verifier and renders the same refusal, so
14
+ * nothing is echoed and the bounce carries no marker.
15
+ */ OAuth2FederatedLoginRefusal["CODE_REQUEST"] = "codeRequest";
16
+ /**
17
+ * The provider was disabled while the person was away at it.
18
+ */ OAuth2FederatedLoginRefusal["PROVIDER_DISABLED"] = "providerDisabled";
19
+ /**
20
+ * The linked local user is inactive. The local login path refuses one
21
+ * too, and a federated login must not be the way around that.
22
+ */ OAuth2FederatedLoginRefusal["USER_INACTIVE"] = "userInactive";
23
+ /**
24
+ * The client's access policy denied the identity (plan 052).
25
+ */ OAuth2FederatedLoginRefusal["ACCESS_DENIED"] = "accessDenied";
26
+ return OAuth2FederatedLoginRefusal;
27
+ }({});
28
+ //#endregion
29
+ export { OAuth2FederatedLoginRefusal };
30
+
31
+ //# sourceMappingURL=types.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.mjs","names":["OAuth2FederatedLoginRefusal"],"sources":["../../../../src/core/oauth2/federated-login/types.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type {\n OAuth2AuthorizationCodeRequest,\n OAuth2IdentityProvider,\n OpenIDIdentityProvider, \n User, \n} from '@authup/core-kit';\nimport type { OAuth2ErrorCode } from '@authup/specs';\nimport type { Logger } from '@authup/server-kit';\nimport type { IIdentityProviderAccountManager } from '../../identity/provider/account/types.ts';\nimport type { IOAuth2Authenticator } from '../../identity/provider/authentication/protocols/index.ts';\nimport type { IEventService, IRealmRepository } from '../../entities/index.ts';\nimport type { IOAuth2AccessPolicyEvaluator } from '../access-policy/index.ts';\nimport type {\n IOAuth2AuthorizationCodeIssuer,\n IOAuth2AuthorizationCodeRequestVerifier,\n} from '../authorization/index.ts';\n\nexport type OAuth2FederatedLoginCompleteInput = {\n /**\n * The provider the callback belongs to, already resolved and\n * protocol-checked by the caller.\n */\n provider: OAuth2IdentityProvider | OpenIDIdentityProvider,\n /**\n * The RP's original authorization request, as `authorize-out` stored\n * it on the state blob. It is re-verified here rather than trusted.\n */\n codeRequest: OAuth2AuthorizationCodeRequest,\n /**\n * The provider's single-use authorization code.\n */\n code: string,\n request?: {\n ipAddress?: string | null,\n userAgent?: string | null,\n },\n};\n\n/**\n * Why a completion was refused. Every value is a REFUSAL — a decision the\n * person at the browser has to be told about on the hosted login page —\n * never a server failure, which keeps throwing.\n */\nexport enum OAuth2FederatedLoginRefusal {\n /**\n * The stored code request no longer verifies (client deactivated or\n * unknown, grant allowlist, scopes, redirect_uri mismatch). The hosted\n * page re-runs the same verifier and renders the same refusal, so\n * nothing is echoed and the bounce carries no marker.\n */\n CODE_REQUEST = 'codeRequest',\n /**\n * The provider was disabled while the person was away at it.\n */\n PROVIDER_DISABLED = 'providerDisabled',\n /**\n * The linked local user is inactive. The local login path refuses one\n * too, and a federated login must not be the way around that.\n */\n USER_INACTIVE = 'userInactive',\n /**\n * The client's access policy denied the identity (plan 052).\n */\n ACCESS_DENIED = 'accessDenied',\n}\n\nexport type OAuth2FederatedLoginRefusedResult = {\n kind: 'refused',\n refusal: OAuth2FederatedLoginRefusal,\n /**\n * The marker the hosted authorize page maps onto its own copy, when\n * the refusal has one. Absent means \"render the request again\", which\n * re-runs the verifier and states the reason itself.\n */\n error?: `${OAuth2ErrorCode}`,\n /**\n * The request to re-render. The verified copy when re-verification got\n * that far, else the stored one.\n */\n codeRequest: OAuth2AuthorizationCodeRequest,\n};\n\nexport type OAuth2FederatedLoginIssuedResult = {\n kind: 'issued',\n /**\n * The verified redirect target of the RP. Its scheme passed\n * `isSafeRedirectURLScheme`, so a caller may navigate it.\n */\n redirectUri: string,\n /**\n * The authorization code id the RP redeems at `/token`.\n */\n code: string,\n state?: string,\n /**\n * The verified request, for a caller that has to re-render the hosted\n * page alongside the redirect (the custom-scheme interstitial).\n */\n codeRequest: OAuth2AuthorizationCodeRequest,\n client: {\n id: string,\n name: string,\n displayName: string | null,\n },\n};\n\nexport type OAuth2FederatedLoginCompleteResult = OAuth2FederatedLoginRefusedResult |\n OAuth2FederatedLoginIssuedResult;\n\n/**\n * Completes the RP's original authorization request after an external\n * provider sent the browser back (issue #3446). It owns the refusal ladder\n * — realm match, code-request re-verification, redirect-scheme gate,\n * provider and user state, access policy — and mints the authup code.\n *\n * Deliberately transport-free: it decides WHAT the answer is, and returns\n * it as a discriminated result. The adapter decides how that answer\n * reaches the browser (a redirect, or the interstitial page a custom\n * scheme needs), because only the adapter knows the difference.\n */\nexport interface IOAuth2FederatedLoginService {\n complete(input: OAuth2FederatedLoginCompleteInput) : Promise<OAuth2FederatedLoginCompleteResult>;\n}\n\nexport type OAuth2FederatedLoginServiceOptions = {\n /**\n * The public base URL. Reaches the provider authenticator, which\n * builds the callback URL the external token exchange must repeat.\n */\n baseURL: string,\n};\n\n/**\n * Builds the authenticator that exchanges the provider's code. Injected\n * rather than constructed inline so the refusal ladder can be exercised\n * without an external provider; it defaults to the real preset/protocol\n * factory.\n */\nexport type OAuth2FederatedLoginAuthenticatorFactory = (\n provider: OAuth2IdentityProvider | OpenIDIdentityProvider,\n options: {\n baseURL: string, \n clientId?: string, \n logger?: Logger \n },\n) => IOAuth2Authenticator<User>;\n\nexport type OAuth2FederatedLoginServiceContext = {\n options: OAuth2FederatedLoginServiceOptions,\n\n accountManager: IIdentityProviderAccountManager,\n realmRepository: IRealmRepository,\n codeRequestVerifier: IOAuth2AuthorizationCodeRequestVerifier,\n codeIssuer: IOAuth2AuthorizationCodeIssuer,\n\n accessPolicyEvaluator?: IOAuth2AccessPolicyEvaluator,\n eventService?: IEventService,\n logger?: Logger,\n authenticatorFactory?: OAuth2FederatedLoginAuthenticatorFactory,\n};\n"],"mappings":";;;;;;;;GAkDA,IAAO,8BAAKA,uBAAAA,6BAAAA;;;;;;IAMP,4BAAA,kBAAA;;;IAIA,4BAAA,uBAAA;;;;IAKA,4BAAA,mBAAA;;;IAIA,4BAAA,mBAAA;CAnBOA,OAAAA;AAqBX,EAAA,CAAA,CAAA"}
@@ -2,6 +2,7 @@ export * from './access-policy/index.ts';
2
2
  export * from './authorization/index.ts';
3
3
  export * from './client/index.ts';
4
4
  export * from './end-session/index.ts';
5
+ export * from './federated-login/index.ts';
5
6
  export * from './grant-types/index.ts';
6
7
  export * from './mfa-login/index.ts';
7
8
  export * from './openid/index.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/oauth2/index.ts"],"names":[],"mappings":"AAOA,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/oauth2/index.ts"],"names":[],"mappings":"AAOA,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,kBAAkB,CAAC"}
@@ -17,6 +17,9 @@ import "./authorization/index.mjs";
17
17
  import { OAuth2EndSessionService } from "./end-session/service.mjs";
18
18
  import { OAuth2EndSessionRequestValidator } from "./end-session/validator.mjs";
19
19
  import "./end-session/index.mjs";
20
+ import { OAuth2FederatedLoginRefusal } from "./federated-login/types.mjs";
21
+ import { OAuth2FederatedLoginService } from "./federated-login/module.mjs";
22
+ import "./federated-login/index.mjs";
20
23
  import { buildOAuth2BearerTokenResponse } from "./response/bearer.mjs";
21
24
  import { OAuth2AuthorizeGrant } from "./grant-types/authorize.mjs";
22
25
  import { ClientCredentialsGrant } from "./grant-types/client-credentials.mjs";
@@ -40,4 +43,4 @@ import { OAuth2TokenRevoker } from "./token/revoker/module.mjs";
40
43
  import { OAuth2TokenSigner } from "./token/signer/module.mjs";
41
44
  import { OAuth2TokenVerifier } from "./token/verifier/module.mjs";
42
45
  import "./token/index.mjs";
43
- export { ClientCredentialsGrant, IdentityGrantType, OAuth2AccessPolicyEvaluator, OAuth2AccessTokenIssuer, OAuth2Authorization, OAuth2AuthorizationCodeIssuer, OAuth2AuthorizationCodeRequestValidator, OAuth2AuthorizationCodeRequestVerifier, OAuth2AuthorizationCodeVerifier, OAuth2AuthorizationStateManager, OAuth2AuthorizeGrant, OAuth2ClientAuthenticator, OAuth2EndSessionRequestValidator, OAuth2EndSessionService, OAuth2MfaLoginService, OAuth2MfaTokenIssuer, OAuth2OpenIDClaimsBuilder, OAuth2OpenIDTokenIssuer, OAuth2RefreshTokenGrant, OAuth2RefreshTokenIssuer, OAuth2ScopeAttributesResolver, OAuth2TokenRevoker, OAuth2TokenSigner, OAuth2TokenVerifier, PasswordGrantType, SESSION_TOKEN_RELATION_MISSING_ERROR_INSTANCE, SessionTokenRelationMissingError, assertClientGrantAllowed, base64URLEncode, buildOAuth2BearerTokenResponse, buildOAuth2CodeChallenge, buildOAuth2TokenHash, deriveAmrAcr, generateOAuth2CodeVerifier, isSessionTokenRelationMissingError };
46
+ export { ClientCredentialsGrant, IdentityGrantType, OAuth2AccessPolicyEvaluator, OAuth2AccessTokenIssuer, OAuth2Authorization, OAuth2AuthorizationCodeIssuer, OAuth2AuthorizationCodeRequestValidator, OAuth2AuthorizationCodeRequestVerifier, OAuth2AuthorizationCodeVerifier, OAuth2AuthorizationStateManager, OAuth2AuthorizeGrant, OAuth2ClientAuthenticator, OAuth2EndSessionRequestValidator, OAuth2EndSessionService, OAuth2FederatedLoginRefusal, OAuth2FederatedLoginService, OAuth2MfaLoginService, OAuth2MfaTokenIssuer, OAuth2OpenIDClaimsBuilder, OAuth2OpenIDTokenIssuer, OAuth2RefreshTokenGrant, OAuth2RefreshTokenIssuer, OAuth2ScopeAttributesResolver, OAuth2TokenRevoker, OAuth2TokenSigner, OAuth2TokenVerifier, PasswordGrantType, SESSION_TOKEN_RELATION_MISSING_ERROR_INSTANCE, SessionTokenRelationMissingError, assertClientGrantAllowed, base64URLEncode, buildOAuth2BearerTokenResponse, buildOAuth2CodeChallenge, buildOAuth2TokenHash, deriveAmrAcr, generateOAuth2CodeVerifier, isSessionTokenRelationMissingError };
@@ -1 +1 @@
1
- {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../../../src/core/provisioning/entities/policy/validator.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACnE,OAAO,EAAE,SAAS,EAAiC,MAAM,SAAS,CAAC;AAInE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE3D,qBAAa,2BAA4B,SAAQ,SAAS,CAAC,wBAAwB,CAAC;IACjE,GAAG,CACd,KAAK,CAAC,EAAE,cAAc,CAAC,wBAAwB,CAAC,EAChD,OAAO,CAAC,EAAE,mBAAmB,CAAC,wBAAwB,CAAC,GACxD,OAAO,CAAC,wBAAwB,CAAC;IAuBpC,SAAS,CAAC,UAAU;CAmBvB"}
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../../../src/core/provisioning/entities/policy/validator.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACnE,OAAO,EAAE,SAAS,EAAgB,MAAM,SAAS,CAAC;AAKlD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE3D,qBAAa,2BAA4B,SAAQ,SAAS,CAAC,wBAAwB,CAAC;IACjE,GAAG,CACd,KAAK,CAAC,EAAE,cAAc,CAAC,wBAAwB,CAAC,EAChD,OAAO,CAAC,EAAE,mBAAmB,CAAC,wBAAwB,CAAC,GACxD,OAAO,CAAC,wBAAwB,CAAC;IAuBpC,SAAS,CAAC,UAAU;CAmBvB"}
@@ -4,9 +4,10 @@ import.meta.url;
4
4
  import { ProvisioningStrategyValidator } from "../../strategy/validator.mjs";
5
5
  import "../../strategy/index.mjs";
6
6
  import { createProvisioningEntitiesValidator } from "../utils.mjs";
7
+ import { defineIssueItem } from "../../../../node_modules/@ebec/core/dist/index.mjs";
7
8
  import { PolicyValidator } from "@authup/core-kit";
8
9
  import { BuiltInPolicyType } from "@authup/access";
9
- import { Container, ValidupError, defineIssueItem } from "validup";
10
+ import { Container, ValidupError } from "validup";
10
11
  import { createValidator } from "@validup/zod";
11
12
  import { z } from "zod";
12
13
  //#region src/core/provisioning/entities/policy/validator.ts
@@ -1 +1 @@
1
- {"version":3,"file":"validator.mjs","names":["BuiltInPolicyType","PolicyValidator","createValidator","Container","ValidupError","defineIssueItem","z","ProvisioningStrategyValidator","createProvisioningEntitiesValidator","PolicyProvisioningValidator","run","input","options","output","attributes","type","COMPOSITE","children","length","path","message","initialize","strategyValidator","mount","optional","attributesValidator","record","string","any","array","check"],"sources":["../../../../../src/core/provisioning/entities/policy/validator.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { BuiltInPolicyType } from '@authup/access';\nimport { PolicyValidator } from '@authup/core-kit';\nimport { createValidator } from '@validup/zod';\nimport type { ContainerInput, ContainerRunOptions } from 'validup';\nimport { Container, ValidupError, defineIssueItem } from 'validup';\nimport { z } from 'zod';\nimport { ProvisioningStrategyValidator } from '../../strategy/index.ts';\nimport { createProvisioningEntitiesValidator } from '../utils.ts';\nimport type { PolicyProvisioningEntity } from './types.ts';\n\nexport class PolicyProvisioningValidator extends Container<PolicyProvisioningEntity> {\n override async run(\n input?: ContainerInput<PolicyProvisioningEntity>,\n options?: ContainerRunOptions<PolicyProvisioningEntity>,\n ): Promise<PolicyProvisioningEntity> {\n const output = await super.run(input, options);\n\n // A composite policy with no children can never be satisfied and makes\n // any permission bound to it permanently un-grantable (#3304). Reject\n // it up front so provisioning fails at config load with a clear\n // message, rather than silently provisioning a \"stale\" permission that\n // only fails — with an opaque empty-issue error — once evaluated.\n if (\n output.attributes?.type === BuiltInPolicyType.COMPOSITE &&\n (!output.children || output.children.length === 0)\n ) {\n throw new ValidupError([\n defineIssueItem({\n path: ['children'],\n message: 'A composite policy must define at least one child policy.',\n }),\n ]);\n }\n\n return output;\n }\n\n protected initialize() {\n super.initialize();\n\n const strategyValidator = new ProvisioningStrategyValidator();\n this.mount('strategy', { optional: true }, strategyValidator);\n\n const attributesValidator = new PolicyValidator();\n this.mount('attributes', attributesValidator);\n\n this.mount('extraAttributes', { optional: true }, createValidator(\n z.record(z.string(), z.any()),\n ));\n\n this.mount('children', { optional: true }, createValidator(\n z\n .array(z.any())\n .check(createProvisioningEntitiesValidator(this)),\n ));\n }\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,IAAaS,8BAAb,cAAiDN,UAAAA;CAC7C,MAAeO,IACXC,OACAC,SACiC;EACjC,MAAMC,SAAS,MAAM,MAAMH,IAAIC,OAAOC,OAAAA;EAOtC,IACIC,OAAOC,YAAYC,SAASf,kBAAkBgB,cAC7C,CAACH,OAAOI,YAAYJ,OAAOI,SAASC,WAAW,IAEhD,MAAM,IAAId,aAAa,CACnBC,gBAAgB;GACZc,MAAM,CAAC,UAAW;GAClBC,SAAS;EACb,CAAA,CACH,CAAA;EAGL,OAAOP;CACX;CAEUQ,aAAa;EACnB,MAAMA,WAAAA;EAEN,MAAMC,oBAAoB,IAAIf,8BAAAA;EAC9B,KAAKgB,MAAM,YAAY,EAAEC,UAAU,KAAK,GAAGF,iBAAAA;EAE3C,MAAMG,sBAAsB,IAAIxB,gBAAAA;EAChC,KAAKsB,MAAM,cAAcE,mBAAAA;EAEzB,KAAKF,MAAM,mBAAmB,EAAEC,UAAU,KAAK,GAAGtB,gBAC9CI,EAAEoB,OAAOpB,EAAEqB,OAAM,GAAIrB,EAAEsB,IAAG,CAAA,CAAA,CAAA;EAG9B,KAAKL,MAAM,YAAY,EAAEC,UAAU,KAAK,GAAGtB,gBACvCI,EACKuB,MAAMvB,EAAEsB,IAAG,CAAA,CAAA,CACXE,MAAMtB,oCAAoC,IAAI,CAAA,CAAA,CAAA;CAE3D;AACJ"}
1
+ {"version":3,"file":"validator.mjs","names":["BuiltInPolicyType","PolicyValidator","createValidator","Container","ValidupError","defineIssueItem","z","ProvisioningStrategyValidator","createProvisioningEntitiesValidator","PolicyProvisioningValidator","run","input","options","output","attributes","type","COMPOSITE","children","length","path","message","initialize","strategyValidator","mount","optional","attributesValidator","record","string","any","array","check"],"sources":["../../../../../src/core/provisioning/entities/policy/validator.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { BuiltInPolicyType } from '@authup/access';\nimport { PolicyValidator } from '@authup/core-kit';\nimport { createValidator } from '@validup/zod';\nimport type { ContainerInput, ContainerRunOptions } from 'validup';\nimport { Container, ValidupError } from 'validup';\nimport { defineIssueItem } from '@ebec/core';\nimport { z } from 'zod';\nimport { ProvisioningStrategyValidator } from '../../strategy/index.ts';\nimport { createProvisioningEntitiesValidator } from '../utils.ts';\nimport type { PolicyProvisioningEntity } from './types.ts';\n\nexport class PolicyProvisioningValidator extends Container<PolicyProvisioningEntity> {\n override async run(\n input?: ContainerInput<PolicyProvisioningEntity>,\n options?: ContainerRunOptions<PolicyProvisioningEntity>,\n ): Promise<PolicyProvisioningEntity> {\n const output = await super.run(input, options);\n\n // A composite policy with no children can never be satisfied and makes\n // any permission bound to it permanently un-grantable (#3304). Reject\n // it up front so provisioning fails at config load with a clear\n // message, rather than silently provisioning a \"stale\" permission that\n // only fails — with an opaque empty-issue error — once evaluated.\n if (\n output.attributes?.type === BuiltInPolicyType.COMPOSITE &&\n (!output.children || output.children.length === 0)\n ) {\n throw new ValidupError([\n defineIssueItem({\n path: ['children'],\n message: 'A composite policy must define at least one child policy.',\n }),\n ]);\n }\n\n return output;\n }\n\n protected initialize() {\n super.initialize();\n\n const strategyValidator = new ProvisioningStrategyValidator();\n this.mount('strategy', { optional: true }, strategyValidator);\n\n const attributesValidator = new PolicyValidator();\n this.mount('attributes', attributesValidator);\n\n this.mount('extraAttributes', { optional: true }, createValidator(\n z.record(z.string(), z.any()),\n ));\n\n this.mount('children', { optional: true }, createValidator(\n z\n .array(z.any())\n .check(createProvisioningEntitiesValidator(this)),\n ));\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAkBA,IAAaS,8BAAb,cAAiDN,UAAAA;CAC7C,MAAeO,IACXC,OACAC,SACiC;EACjC,MAAMC,SAAS,MAAM,MAAMH,IAAIC,OAAOC,OAAAA;EAOtC,IACIC,OAAOC,YAAYC,SAASf,kBAAkBgB,cAC7C,CAACH,OAAOI,YAAYJ,OAAOI,SAASC,WAAW,IAEhD,MAAM,IAAId,aAAa,CACnBC,gBAAgB;GACZc,MAAM,CAAC,UAAW;GAClBC,SAAS;EACb,CAAA,CACH,CAAA;EAGL,OAAOP;CACX;CAEUQ,aAAa;EACnB,MAAMA,WAAAA;EAEN,MAAMC,oBAAoB,IAAIf,8BAAAA;EAC9B,KAAKgB,MAAM,YAAY,EAAEC,UAAU,KAAK,GAAGF,iBAAAA;EAE3C,MAAMG,sBAAsB,IAAIxB,gBAAAA;EAChC,KAAKsB,MAAM,cAAcE,mBAAAA;EAEzB,KAAKF,MAAM,mBAAmB,EAAEC,UAAU,KAAK,GAAGtB,gBAC9CI,EAAEoB,OAAOpB,EAAEqB,OAAM,GAAIrB,EAAEsB,IAAG,CAAA,CAAA,CAAA;EAG9B,KAAKL,MAAM,YAAY,EAAEC,UAAU,KAAK,GAAGtB,gBACvCI,EACKuB,MAAMvB,EAAEsB,IAAG,CAAA,CAAA,CACXE,MAAMtB,oCAAoC,IAAI,CAAA,CAAA,CAAA;CAE3D;AACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"wildcard-validator.d.ts","sourceRoot":"","sources":["../../../../../src/core/provisioning/entities/realm/wildcard-validator.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAS,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAiC,MAAM,SAAS,CAAC;AAInE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,qBAAa,kCAAmC,SAAQ,SAAS,CAAC,uBAAuB,CAAC;IACtF,SAAS,CAAC,UAAU;IA8BL,GAAG,CACd,KAAK,CAAC,EAAE,cAAc,CAAC,uBAAuB,CAAC,EAC/C,OAAO,CAAC,EAAE,mBAAmB,CAAC,uBAAuB,CAAC,GACvD,OAAO,CAAC,uBAAuB,CAAC;CAsBtC"}
1
+ {"version":3,"file":"wildcard-validator.d.ts","sourceRoot":"","sources":["../../../../../src/core/provisioning/entities/realm/wildcard-validator.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnE,OAAO,EAAE,SAAS,EAAgB,MAAM,SAAS,CAAC;AAKlD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,qBAAa,kCAAmC,SAAQ,SAAS,CAAC,uBAAuB,CAAC;IACtF,SAAS,CAAC,UAAU;IA8BL,GAAG,CACd,KAAK,CAAC,EAAE,cAAc,CAAC,uBAAuB,CAAC,EAC/C,OAAO,CAAC,EAAE,mBAAmB,CAAC,uBAAuB,CAAC,GACvD,OAAO,CAAC,uBAAuB,CAAC;CAsBtC"}
@@ -2,9 +2,10 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import "../../constants.mjs";
5
+ import { defineIssueItem } from "../../../../node_modules/@ebec/core/dist/index.mjs";
5
6
  import { RealmProvisioningRelationsValidator } from "./relations-validator.mjs";
6
7
  import { CLIENT_RESERVED_NAMES } from "@authup/core-kit";
7
- import { Container, ValidupError, defineIssueItem } from "validup";
8
+ import { Container, ValidupError } from "validup";
8
9
  import { createValidator } from "@validup/zod";
9
10
  import { z } from "zod";
10
11
  //#region src/core/provisioning/entities/realm/wildcard-validator.ts
@@ -1 +1 @@
1
- {"version":3,"file":"wildcard-validator.mjs","names":["CLIENT_RESERVED_NAMES","createValidator","Container","ValidupError","defineIssueItem","z","REALM_WILDCARD_NAME","RealmProvisioningRelationsValidator","RealmWildcardProvisioningValidator","initialize","mount","looseObject","name","literal","check","ctx","keys","Object","value","length","issues","push","code","input","message","optional","any","relationsValidator","run","options","output","clients","relations","forEach","client","index","attributes","includes","path","received"],"sources":["../../../../../src/core/provisioning/entities/realm/wildcard-validator.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { CLIENT_RESERVED_NAMES } from '@authup/core-kit';\nimport { createValidator } from '@validup/zod';\nimport type { ContainerInput, ContainerRunOptions, Issue } from 'validup';\nimport { Container, ValidupError, defineIssueItem } from 'validup';\nimport { z } from 'zod';\nimport { REALM_WILDCARD_NAME } from '../../constants.ts';\nimport { RealmProvisioningRelationsValidator } from './relations-validator.ts';\nimport type { RealmProvisioningEntity } from './types.ts';\n\n/**\n * Validates a WILDCARD realm entry (attributes.name === '*'). A wildcard\n * entry is a selector over realms, not a realm declaration, so it is\n * relations-only: `attributes` may carry nothing but the literal wildcard\n * name, and a realm-level `strategy` is rejected (`absent` would mean\n * \"delete every realm\"). Child strategies stay fully supported.\n *\n * Reserved client names (system / console clients) are rejected: a\n * template stamping one into every realm would fight the system client\n * MERGE on every boot.\n */\nexport class RealmWildcardProvisioningValidator extends Container<RealmProvisioningEntity> {\n protected initialize() {\n super.initialize();\n\n this.mount('attributes', createValidator(\n z.looseObject({ name: z.literal(REALM_WILDCARD_NAME) }).check((ctx) => {\n const keys = Object.keys(ctx.value);\n if (keys.length > 1) {\n ctx.issues.push({\n code: 'custom',\n input: ctx.value,\n message: 'A wildcard realm entry is relations-only and can not declare realm attributes.',\n });\n }\n }),\n ));\n\n this.mount('strategy', { optional: true }, createValidator(\n z.any().check((ctx) => {\n ctx.issues.push({\n code: 'custom',\n input: ctx.value,\n message: 'A wildcard realm entry can not declare a realm-level strategy.',\n });\n }),\n ));\n\n const relationsValidator = new RealmProvisioningRelationsValidator();\n this.mount('relations', { optional: true }, relationsValidator);\n }\n\n override async run(\n input?: ContainerInput<RealmProvisioningEntity>,\n options?: ContainerRunOptions<RealmProvisioningEntity>,\n ): Promise<RealmProvisioningEntity> {\n const output = await super.run(input, options);\n\n const issues : Issue[] = [];\n const clients = output.relations?.clients ?? [];\n clients.forEach((client, index) => {\n const name = client.attributes?.name;\n if (name && (CLIENT_RESERVED_NAMES as string[]).includes(name)) {\n issues.push(defineIssueItem({\n path: ['relations', 'clients', index, 'attributes', 'name'],\n message: `The client name \"${name}\" is reserved for system clients and can not be declared in a wildcard realm entry.`,\n received: name,\n }));\n }\n });\n\n if (issues.length > 0) {\n throw new ValidupError(issues);\n }\n\n return output;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;GA2BA,IAAaQ,qCAAb,cAAwDN,UAAAA;CAC1CO,aAAa;EACnB,MAAMA,WAAAA;EAEN,KAAKC,MAAM,cAAcT,gBACrBI,EAAEM,YAAY,EAAEC,MAAMP,EAAEQ,QAAAA,GAAQP,EAAqB,CAAA,CAAA,CAAGQ,OAAOC,QAAAA;GAE3D,IADaE,OAAOD,KAAKD,IAAIG,KACzBF,CAAAA,CAAKG,SAAS,GACdJ,IAAIK,OAAOC,KAAK;IACZC,MAAM;IACNC,OAAOR,IAAIG;IACXM,SAAS;GACb,CAAA;EAER,CAAA,CAAA,CAAA;EAGJ,KAAKd,MAAM,YAAY,EAAEe,UAAU,KAAK,GAAGxB,gBACvCI,EAAEqB,IAAG,CAAA,CAAGZ,OAAOC,QAAAA;GACXA,IAAIK,OAAOC,KAAK;IACZC,MAAM;IACNC,OAAOR,IAAIG;IACXM,SAAS;GACb,CAAA;EACJ,CAAA,CAAA,CAAA;EAGJ,MAAMG,qBAAqB,IAAIpB,oCAAAA;EAC/B,KAAKG,MAAM,aAAa,EAAEe,UAAU,KAAK,GAAGE,kBAAAA;CAChD;CAEA,MAAeC,IACXL,OACAM,SACgC;EAChC,MAAMC,SAAS,MAAM,MAAMF,IAAIL,OAAOM,OAAAA;EAEtC,MAAMT,SAAmB,CAAA;EAEzBW,CADgBD,OAAOE,WAAWD,WAAW,CAAA,EAAA,CACrCE,SAASC,QAAQC,UAAAA;GACrB,MAAMvB,OAAOsB,OAAOE,YAAYxB;GAChC,IAAIA,QAAQ,sBAAoCyB,SAASzB,IAAAA,GACrDQ,OAAOC,KAAKjB,gBAAgB;IACxBkC,MAAM;KAAC;KAAa;KAAWH;KAAO;KAAc;IAAO;IAC3DX,SAAS,oBAAoBZ,KAAK;IAClC2B,UAAU3B;GACd,CAAA,CAAA;EAER,CAAA;EAEA,IAAIQ,OAAOD,SAAS,GAChB,MAAM,IAAIhB,aAAaiB,MAAAA;EAG3B,OAAOU;CACX;AACJ"}
1
+ {"version":3,"file":"wildcard-validator.mjs","names":["CLIENT_RESERVED_NAMES","createValidator","Container","ValidupError","defineIssueItem","z","REALM_WILDCARD_NAME","RealmProvisioningRelationsValidator","RealmWildcardProvisioningValidator","initialize","mount","looseObject","name","literal","check","ctx","keys","Object","value","length","issues","push","code","input","message","optional","any","relationsValidator","run","options","output","clients","relations","forEach","client","index","attributes","includes","path","received"],"sources":["../../../../../src/core/provisioning/entities/realm/wildcard-validator.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { CLIENT_RESERVED_NAMES } from '@authup/core-kit';\nimport { createValidator } from '@validup/zod';\nimport type { ContainerInput, ContainerRunOptions } from 'validup';\nimport type { Issue } from '@ebec/core';\nimport { Container, ValidupError } from 'validup';\nimport { defineIssueItem } from '@ebec/core';\nimport { z } from 'zod';\nimport { REALM_WILDCARD_NAME } from '../../constants.ts';\nimport { RealmProvisioningRelationsValidator } from './relations-validator.ts';\nimport type { RealmProvisioningEntity } from './types.ts';\n\n/**\n * Validates a WILDCARD realm entry (attributes.name === '*'). A wildcard\n * entry is a selector over realms, not a realm declaration, so it is\n * relations-only: `attributes` may carry nothing but the literal wildcard\n * name, and a realm-level `strategy` is rejected (`absent` would mean\n * \"delete every realm\"). Child strategies stay fully supported.\n *\n * Reserved client names (system / console clients) are rejected: a\n * template stamping one into every realm would fight the system client\n * MERGE on every boot.\n */\nexport class RealmWildcardProvisioningValidator extends Container<RealmProvisioningEntity> {\n protected initialize() {\n super.initialize();\n\n this.mount('attributes', createValidator(\n z.looseObject({ name: z.literal(REALM_WILDCARD_NAME) }).check((ctx) => {\n const keys = Object.keys(ctx.value);\n if (keys.length > 1) {\n ctx.issues.push({\n code: 'custom',\n input: ctx.value,\n message: 'A wildcard realm entry is relations-only and can not declare realm attributes.',\n });\n }\n }),\n ));\n\n this.mount('strategy', { optional: true }, createValidator(\n z.any().check((ctx) => {\n ctx.issues.push({\n code: 'custom',\n input: ctx.value,\n message: 'A wildcard realm entry can not declare a realm-level strategy.',\n });\n }),\n ));\n\n const relationsValidator = new RealmProvisioningRelationsValidator();\n this.mount('relations', { optional: true }, relationsValidator);\n }\n\n override async run(\n input?: ContainerInput<RealmProvisioningEntity>,\n options?: ContainerRunOptions<RealmProvisioningEntity>,\n ): Promise<RealmProvisioningEntity> {\n const output = await super.run(input, options);\n\n const issues : Issue[] = [];\n const clients = output.relations?.clients ?? [];\n clients.forEach((client, index) => {\n const name = client.attributes?.name;\n if (name && (CLIENT_RESERVED_NAMES as string[]).includes(name)) {\n issues.push(defineIssueItem({\n path: ['relations', 'clients', index, 'attributes', 'name'],\n message: `The client name \"${name}\" is reserved for system clients and can not be declared in a wildcard realm entry.`,\n received: name,\n }));\n }\n });\n\n if (issues.length > 0) {\n throw new ValidupError(issues);\n }\n\n return output;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;GA6BA,IAAaQ,qCAAb,cAAwDN,UAAAA;CAC1CO,aAAa;EACnB,MAAMA,WAAAA;EAEN,KAAKC,MAAM,cAAcT,gBACrBI,EAAEM,YAAY,EAAEC,MAAMP,EAAEQ,QAAAA,GAAQP,EAAqB,CAAA,CAAA,CAAGQ,OAAOC,QAAAA;GAE3D,IADaE,OAAOD,KAAKD,IAAIG,KACzBF,CAAAA,CAAKG,SAAS,GACdJ,IAAIK,OAAOC,KAAK;IACZC,MAAM;IACNC,OAAOR,IAAIG;IACXM,SAAS;GACb,CAAA;EAER,CAAA,CAAA,CAAA;EAGJ,KAAKd,MAAM,YAAY,EAAEe,UAAU,KAAK,GAAGxB,gBACvCI,EAAEqB,IAAG,CAAA,CAAGZ,OAAOC,QAAAA;GACXA,IAAIK,OAAOC,KAAK;IACZC,MAAM;IACNC,OAAOR,IAAIG;IACXM,SAAS;GACb,CAAA;EACJ,CAAA,CAAA,CAAA;EAGJ,MAAMG,qBAAqB,IAAIpB,oCAAAA;EAC/B,KAAKG,MAAM,aAAa,EAAEe,UAAU,KAAK,GAAGE,kBAAAA;CAChD;CAEA,MAAeC,IACXL,OACAM,SACgC;EAChC,MAAMC,SAAS,MAAM,MAAMF,IAAIL,OAAOM,OAAAA;EAEtC,MAAMT,SAAmB,CAAA;EAEzBW,CADgBD,OAAOE,WAAWD,WAAW,CAAA,EAAA,CACrCE,SAASC,QAAQC,UAAAA;GACrB,MAAMvB,OAAOsB,OAAOE,YAAYxB;GAChC,IAAIA,QAAQ,sBAAoCyB,SAASzB,IAAAA,GACrDQ,OAAOC,KAAKjB,gBAAgB;IACxBkC,MAAM;KAAC;KAAa;KAAWH;KAAO;KAAc;IAAO;IAC3DX,SAAS,oBAAoBZ,KAAK;IAClC2B,UAAU3B;GACd,CAAA,CAAA;EAER,CAAA;EAEA,IAAIQ,OAAOD,SAAS,GAChB,MAAM,IAAIhB,aAAaiB,MAAAA;EAG3B,OAAOU;CACX;AACJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"evaluator.mjs","names":["and","or","BuiltInPolicyType","IdentityPolicyEvaluator","PermissionBindingPolicyValidator","PolicyEngine","PolicyIssueCode","RealmMatchPolicyEvaluator","RealmScope","aggregatePermissionPolicyBindings","definePolicyIssueItem","maybeInvertPolicyOutcome","normalizeRealmScope","PermissionBindingPolicyEvaluator","validator","identityEvaluator","realmMatchEvaluator","identityPermissionProvider","requires","PERMISSION_BINDING","accessData","ctx","data","has","isValidated","get","set","setValidated","evaluate","value","policy","run","identity","success","issues","code","DATA_MISSING","message","path","binding","identityBindings","getFor","then","bindings","filter","item","permission","name","realmId","clientId","length","invert","aggr","pendingIssues","conditions","pending","lowerable","grant","grants","reachCondition","realmOutcome","scope","realmScope","NONE","ANY","condition","push","engine","evaluators","outcome","withConditions","result"],"sources":["../../../../src/core/security/policy/evaluator.ts"],"sourcesContent":["/*\n * Copyright (c) 2024-2024.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { ICondition } from '@rapiq/core';\nimport { and, or } from '@rapiq/core';\nimport type { Issue } from 'validup';\nimport type {\n IPolicyEvaluator,\n PermissionPolicyBindingAggregated,\n PolicyEvaluationContext,\n PolicyEvaluationResult,\n} from '@authup/access';\nimport {\n BuiltInPolicyType,\n IdentityPolicyEvaluator,\n PermissionBindingPolicyValidator,\n PolicyEngine,\n PolicyIssueCode,\n RealmMatchPolicyEvaluator,\n RealmScope,\n aggregatePermissionPolicyBindings,\n definePolicyIssueItem,\n maybeInvertPolicyOutcome,\n normalizeRealmScope,\n} from '@authup/access';\nimport type { IIdentityPermissionProvider } from '../../identity/permission/types.ts';\n\nexport class PermissionBindingPolicyEvaluator implements IPolicyEvaluator {\n protected validator : PermissionBindingPolicyValidator;\n\n protected identityEvaluator: IdentityPolicyEvaluator;\n\n protected realmMatchEvaluator : RealmMatchPolicyEvaluator;\n\n protected identityPermissionProvider: IIdentityPermissionProvider;\n\n constructor(identityPermissionProvider: IIdentityPermissionProvider) {\n this.validator = new PermissionBindingPolicyValidator();\n this.identityEvaluator = new IdentityPolicyEvaluator();\n this.realmMatchEvaluator = new RealmMatchPolicyEvaluator();\n this.identityPermissionProvider = identityPermissionProvider;\n }\n\n requires() : string[] {\n // IDENTITY is deliberately NOT declared: a missing identity must stay a\n // settled DATA_MISSING deny (fail-closed) so a scope-restricted or anonymous\n // bearer still fails the pre-gate through system.default — pending would be\n // permitted there.\n return [BuiltInPolicyType.PERMISSION_BINDING];\n }\n\n async accessData(ctx: PolicyEvaluationContext) : Promise<PermissionPolicyBindingAggregated | null> {\n if (!ctx.data.has(BuiltInPolicyType.PERMISSION_BINDING)) {\n return null;\n }\n\n if (ctx.data.isValidated(BuiltInPolicyType.PERMISSION_BINDING)) {\n return ctx.data.get(BuiltInPolicyType.PERMISSION_BINDING);\n }\n\n const data = ctx.data.get<PermissionPolicyBindingAggregated>(BuiltInPolicyType.PERMISSION_BINDING);\n\n ctx.data.set(BuiltInPolicyType.PERMISSION_BINDING, data);\n ctx.data.setValidated(BuiltInPolicyType.PERMISSION_BINDING);\n\n return data;\n }\n\n async evaluate(value: Record<string, any>, ctx: PolicyEvaluationContext): Promise<PolicyEvaluationResult> {\n const policy = await this.validator.run(value);\n\n const identity = await this.identityEvaluator.accessData(ctx);\n if (!identity) {\n return {\n success: false,\n issues: [\n definePolicyIssueItem({\n code: PolicyIssueCode.DATA_MISSING,\n message: 'The data property identity is missing',\n path: ctx.path,\n }),\n ],\n };\n }\n const binding = await this.accessData(ctx);\n if (!binding) {\n return {\n success: false,\n issues: [\n definePolicyIssueItem({\n code: PolicyIssueCode.DATA_MISSING,\n message: 'The data property permission is missing',\n path: ctx.path,\n }),\n ],\n };\n }\n\n const identityBindings = await this.identityPermissionProvider.getFor(identity)\n .then((bindings) => bindings.filter((item) => {\n if (item.permission.name !== binding.permission.name) {\n return false;\n }\n\n return (binding.permission.realmId ?? null) === (item.permission.realmId ?? null) &&\n (binding.permission.clientId ?? null) === (item.permission.clientId ?? null);\n }));\n\n if (identityBindings.length === 0) {\n return { success: maybeInvertPolicyOutcome(false, policy.invert) };\n }\n\n const [aggr] = aggregatePermissionPolicyBindings(identityBindings);\n if (!aggr) {\n return { success: maybeInvertPolicyOutcome(false, policy.invert) };\n }\n\n // identityBindings is filtered to a single (name, realm_id, client_id) key, so the\n // aggregate yields exactly one entry whose `grants` are the actor's per-grant\n // (realm_scope, policy) disjunction terms; access is the DISJUNCTION over them:\n // ∃ grant . realmScopeMatches(grant.realmScope, resource) ∧ (grant.policy passes)\n // Pairing each grant's realm reach with its OWN policy is what fixes both the mixed\n // policy-free + policy-bound UNDER-grant (a policy-free `own` grant must not mask a\n // policy-bound `any` grant's wider reach) and the symmetric OVER-grant (an `own`\n // grant's passing policy must not ride an `any` grant's wider reach).\n const issues : Issue[] = [];\n const pendingIssues : Issue[] = [];\n const conditions : ICondition[] = [];\n let pending = false;\n let lowerable = true;\n for (const grant of aggr.grants) {\n // Realm reach (coarse, actor-relative) — a SEPARATE factor from the grant's\n // policy, ANDed with it. The realm-match evaluator runs in SCOPE MODE: it reads the\n // resource realm from ctx.data[REALM_MATCH] (fallback ATTRIBUTES.realmId) and\n // neutral-passes when absent (preEvaluate / gate checks / realm-less resources) —\n // key-PRESENCE is the discriminator. Invoked DIRECTLY (not via PolicyEngine) so\n // the reach factor can never be skipped by a caller's include/exclude filters or\n // deferred by the engine's data-availability gate.\n //\n // Under `withConditions` (query-build) with no resource realm present, the\n // reach PENDS with its condition form over the row's realm column instead of\n // neutral-passing — `any` stays unrestricted and `none` reaches nothing, so\n // neither pollutes the composed WHERE with constant terms.\n let reachCondition : ICondition | null = null;\n const realmOutcome = await this.realmMatchEvaluator.evaluate(\n { scope: grant.realmScope },\n ctx,\n );\n if (realmOutcome.pending) {\n const scope = normalizeRealmScope(grant.realmScope);\n if (scope === RealmScope.NONE) {\n continue;\n }\n\n if (scope !== RealmScope.ANY) {\n if (!realmOutcome.condition) {\n pending = true;\n lowerable = false;\n continue;\n }\n\n reachCondition = realmOutcome.condition;\n }\n } else if (!realmOutcome.success) {\n continue;\n }\n\n if (!grant.policy) {\n if (!reachCondition) {\n // Reach matches and this grant carries no further restriction.\n return { success: maybeInvertPolicyOutcome(true, policy.invert) };\n }\n\n // Reach is the grant's only restriction — a pure condition term.\n conditions.push(reachCondition);\n pending = true;\n continue;\n }\n\n // Missing evaluators is a misconfiguration: let the engine fail CLOSED with a\n // surfaced POLICY_EVALUATOR_NOT_FOUND issue (new PolicyEngine(undefined) defaults\n // to an empty registry, so grant.policy resolves to no evaluator) rather than\n // swallowing the grant with a silent issue-less deny.\n const engine = new PolicyEngine(ctx.evaluators);\n const outcome = await engine.evaluate(grant.policy, {\n ...ctx,\n path: [\n ...(ctx.path || []),\n ],\n });\n\n if (outcome.success) {\n if (!reachCondition) {\n return { success: maybeInvertPolicyOutcome(true, policy.invert) };\n }\n\n conditions.push(reachCondition);\n pending = true;\n continue;\n }\n\n // A PENDING junction policy (required data key absent — e.g. an ATTRIBUTES\n // policy at the pre-gate) leaves this grant term UNKNOWN, not failed: the\n // grant could still pass once the data arrives, so it must not settle the\n // disjunction to false.\n if (outcome.pending) {\n pending = true;\n\n if (ctx.withConditions && outcome.condition) {\n conditions.push(\n reachCondition ?\n and(reachCondition, outcome.condition) :\n outcome.condition,\n );\n } else {\n lowerable = false;\n }\n\n if (outcome.issues) {\n pendingIssues.push(...outcome.issues);\n }\n\n continue;\n }\n\n if (outcome.issues) {\n issues.push(...outcome.issues);\n }\n }\n\n // No grant term settled true. If some term is still pending, the disjunction is\n // pending — deliberately uninverted (unknown stays unknown under negation). The\n // condition form composes the grant terms as a disjunction, all-or-nothing: a\n // single non-expressible grant term makes the whole binding non-expressible\n // (pushing a partial OR would wrongly exclude rows).\n if (pending) {\n const result : PolicyEvaluationResult = {\n success: false,\n pending: true,\n issues: pendingIssues,\n };\n\n if (ctx.withConditions && lowerable && conditions.length > 0) {\n result.condition = conditions.length === 1 ?\n conditions[0]! :\n or(...conditions);\n }\n\n return result;\n }\n\n // No grant term satisfied (reach ∧ policies). Apply `invert` ONCE to the aggregated\n // disjunction outcome — never per term, which would corrupt the quantifier.\n const success = maybeInvertPolicyOutcome(false, policy.invert);\n return {\n success,\n issues: success ? [] : issues,\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"evaluator.mjs","names":["and","or","BuiltInPolicyType","IdentityPolicyEvaluator","PermissionBindingPolicyValidator","PolicyEngine","PolicyIssueCode","RealmMatchPolicyEvaluator","RealmScope","aggregatePermissionPolicyBindings","definePolicyIssueItem","maybeInvertPolicyOutcome","normalizeRealmScope","PermissionBindingPolicyEvaluator","validator","identityEvaluator","realmMatchEvaluator","identityPermissionProvider","requires","PERMISSION_BINDING","accessData","ctx","data","has","isValidated","get","set","setValidated","evaluate","value","policy","run","identity","success","issues","code","DATA_MISSING","message","path","binding","identityBindings","getFor","then","bindings","filter","item","permission","name","realmId","clientId","length","invert","aggr","pendingIssues","conditions","pending","lowerable","grant","grants","reachCondition","realmOutcome","scope","realmScope","NONE","ANY","condition","push","engine","evaluators","outcome","withConditions","result"],"sources":["../../../../src/core/security/policy/evaluator.ts"],"sourcesContent":["/*\n * Copyright (c) 2024-2024.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { ICondition } from '@rapiq/core';\nimport { and, or } from '@rapiq/core';\nimport type { Issue } from '@ebec/core';\nimport type {\n IPolicyEvaluator,\n PermissionPolicyBindingAggregated,\n PolicyEvaluationContext,\n PolicyEvaluationResult,\n} from '@authup/access';\nimport {\n BuiltInPolicyType,\n IdentityPolicyEvaluator,\n PermissionBindingPolicyValidator,\n PolicyEngine,\n PolicyIssueCode,\n RealmMatchPolicyEvaluator,\n RealmScope,\n aggregatePermissionPolicyBindings,\n definePolicyIssueItem,\n maybeInvertPolicyOutcome,\n normalizeRealmScope,\n} from '@authup/access';\nimport type { IIdentityPermissionProvider } from '../../identity/permission/types.ts';\n\nexport class PermissionBindingPolicyEvaluator implements IPolicyEvaluator {\n protected validator : PermissionBindingPolicyValidator;\n\n protected identityEvaluator: IdentityPolicyEvaluator;\n\n protected realmMatchEvaluator : RealmMatchPolicyEvaluator;\n\n protected identityPermissionProvider: IIdentityPermissionProvider;\n\n constructor(identityPermissionProvider: IIdentityPermissionProvider) {\n this.validator = new PermissionBindingPolicyValidator();\n this.identityEvaluator = new IdentityPolicyEvaluator();\n this.realmMatchEvaluator = new RealmMatchPolicyEvaluator();\n this.identityPermissionProvider = identityPermissionProvider;\n }\n\n requires() : string[] {\n // IDENTITY is deliberately NOT declared: a missing identity must stay a\n // settled DATA_MISSING deny (fail-closed) so a scope-restricted or anonymous\n // bearer still fails the pre-gate through system.default — pending would be\n // permitted there.\n return [BuiltInPolicyType.PERMISSION_BINDING];\n }\n\n async accessData(ctx: PolicyEvaluationContext) : Promise<PermissionPolicyBindingAggregated | null> {\n if (!ctx.data.has(BuiltInPolicyType.PERMISSION_BINDING)) {\n return null;\n }\n\n if (ctx.data.isValidated(BuiltInPolicyType.PERMISSION_BINDING)) {\n return ctx.data.get(BuiltInPolicyType.PERMISSION_BINDING);\n }\n\n const data = ctx.data.get<PermissionPolicyBindingAggregated>(BuiltInPolicyType.PERMISSION_BINDING);\n\n ctx.data.set(BuiltInPolicyType.PERMISSION_BINDING, data);\n ctx.data.setValidated(BuiltInPolicyType.PERMISSION_BINDING);\n\n return data;\n }\n\n async evaluate(value: Record<string, any>, ctx: PolicyEvaluationContext): Promise<PolicyEvaluationResult> {\n const policy = await this.validator.run(value);\n\n const identity = await this.identityEvaluator.accessData(ctx);\n if (!identity) {\n return {\n success: false,\n issues: [\n definePolicyIssueItem({\n code: PolicyIssueCode.DATA_MISSING,\n message: 'The data property identity is missing',\n path: ctx.path,\n }),\n ],\n };\n }\n const binding = await this.accessData(ctx);\n if (!binding) {\n return {\n success: false,\n issues: [\n definePolicyIssueItem({\n code: PolicyIssueCode.DATA_MISSING,\n message: 'The data property permission is missing',\n path: ctx.path,\n }),\n ],\n };\n }\n\n const identityBindings = await this.identityPermissionProvider.getFor(identity)\n .then((bindings) => bindings.filter((item) => {\n if (item.permission.name !== binding.permission.name) {\n return false;\n }\n\n return (binding.permission.realmId ?? null) === (item.permission.realmId ?? null) &&\n (binding.permission.clientId ?? null) === (item.permission.clientId ?? null);\n }));\n\n if (identityBindings.length === 0) {\n return { success: maybeInvertPolicyOutcome(false, policy.invert) };\n }\n\n const [aggr] = aggregatePermissionPolicyBindings(identityBindings);\n if (!aggr) {\n return { success: maybeInvertPolicyOutcome(false, policy.invert) };\n }\n\n // identityBindings is filtered to a single (name, realm_id, client_id) key, so the\n // aggregate yields exactly one entry whose `grants` are the actor's per-grant\n // (realm_scope, policy) disjunction terms; access is the DISJUNCTION over them:\n // ∃ grant . realmScopeMatches(grant.realmScope, resource) ∧ (grant.policy passes)\n // Pairing each grant's realm reach with its OWN policy is what fixes both the mixed\n // policy-free + policy-bound UNDER-grant (a policy-free `own` grant must not mask a\n // policy-bound `any` grant's wider reach) and the symmetric OVER-grant (an `own`\n // grant's passing policy must not ride an `any` grant's wider reach).\n const issues : Issue[] = [];\n const pendingIssues : Issue[] = [];\n const conditions : ICondition[] = [];\n let pending = false;\n let lowerable = true;\n for (const grant of aggr.grants) {\n // Realm reach (coarse, actor-relative) — a SEPARATE factor from the grant's\n // policy, ANDed with it. The realm-match evaluator runs in SCOPE MODE: it reads the\n // resource realm from ctx.data[REALM_MATCH] (fallback ATTRIBUTES.realmId) and\n // neutral-passes when absent (preEvaluate / gate checks / realm-less resources) —\n // key-PRESENCE is the discriminator. Invoked DIRECTLY (not via PolicyEngine) so\n // the reach factor can never be skipped by a caller's include/exclude filters or\n // deferred by the engine's data-availability gate.\n //\n // Under `withConditions` (query-build) with no resource realm present, the\n // reach PENDS with its condition form over the row's realm column instead of\n // neutral-passing — `any` stays unrestricted and `none` reaches nothing, so\n // neither pollutes the composed WHERE with constant terms.\n let reachCondition : ICondition | null = null;\n const realmOutcome = await this.realmMatchEvaluator.evaluate(\n { scope: grant.realmScope },\n ctx,\n );\n if (realmOutcome.pending) {\n const scope = normalizeRealmScope(grant.realmScope);\n if (scope === RealmScope.NONE) {\n continue;\n }\n\n if (scope !== RealmScope.ANY) {\n if (!realmOutcome.condition) {\n pending = true;\n lowerable = false;\n continue;\n }\n\n reachCondition = realmOutcome.condition;\n }\n } else if (!realmOutcome.success) {\n continue;\n }\n\n if (!grant.policy) {\n if (!reachCondition) {\n // Reach matches and this grant carries no further restriction.\n return { success: maybeInvertPolicyOutcome(true, policy.invert) };\n }\n\n // Reach is the grant's only restriction — a pure condition term.\n conditions.push(reachCondition);\n pending = true;\n continue;\n }\n\n // Missing evaluators is a misconfiguration: let the engine fail CLOSED with a\n // surfaced POLICY_EVALUATOR_NOT_FOUND issue (new PolicyEngine(undefined) defaults\n // to an empty registry, so grant.policy resolves to no evaluator) rather than\n // swallowing the grant with a silent issue-less deny.\n const engine = new PolicyEngine(ctx.evaluators);\n const outcome = await engine.evaluate(grant.policy, {\n ...ctx,\n path: [\n ...(ctx.path || []),\n ],\n });\n\n if (outcome.success) {\n if (!reachCondition) {\n return { success: maybeInvertPolicyOutcome(true, policy.invert) };\n }\n\n conditions.push(reachCondition);\n pending = true;\n continue;\n }\n\n // A PENDING junction policy (required data key absent — e.g. an ATTRIBUTES\n // policy at the pre-gate) leaves this grant term UNKNOWN, not failed: the\n // grant could still pass once the data arrives, so it must not settle the\n // disjunction to false.\n if (outcome.pending) {\n pending = true;\n\n if (ctx.withConditions && outcome.condition) {\n conditions.push(\n reachCondition ?\n and(reachCondition, outcome.condition) :\n outcome.condition,\n );\n } else {\n lowerable = false;\n }\n\n if (outcome.issues) {\n pendingIssues.push(...outcome.issues);\n }\n\n continue;\n }\n\n if (outcome.issues) {\n issues.push(...outcome.issues);\n }\n }\n\n // No grant term settled true. If some term is still pending, the disjunction is\n // pending — deliberately uninverted (unknown stays unknown under negation). The\n // condition form composes the grant terms as a disjunction, all-or-nothing: a\n // single non-expressible grant term makes the whole binding non-expressible\n // (pushing a partial OR would wrongly exclude rows).\n if (pending) {\n const result : PolicyEvaluationResult = {\n success: false,\n pending: true,\n issues: pendingIssues,\n };\n\n if (ctx.withConditions && lowerable && conditions.length > 0) {\n result.condition = conditions.length === 1 ?\n conditions[0]! :\n or(...conditions);\n }\n\n return result;\n }\n\n // No grant term satisfied (reach ∧ policies). Apply `invert` ONCE to the aggregated\n // disjunction outcome — never per term, which would corrupt the quantifier.\n const success = maybeInvertPolicyOutcome(false, policy.invert);\n return {\n success,\n issues: success ? [] : issues,\n };\n }\n}\n"],"mappings":"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"}