@jskit-ai/auth-core 0.1.157 → 0.1.159
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/package.json +30 -45
- package/src/client/index.js +0 -1
- package/src/client/providers/AccessCoreClientProvider.js +11 -12
- package/src/server/actions/auth.contributor.js +40 -37
- package/src/server/authExtensions.js +155 -0
- package/src/server/authPolicyContextResolverRegistry.js +9 -120
- package/src/server/authServiceDecoratorRegistry.js +9 -64
- package/src/server/deferredAuthService.js +32 -0
- package/src/server/lib/actionContextContributor.js +2 -1
- package/src/server/providers/{AccessCoreServiceProvider.js → AuthAccessProvider.js} +11 -14
- package/src/server/providers/AuthExtensionsProvider.js +14 -0
- package/src/server/providers/AuthFeature.js +15 -0
- package/src/server/providers/AuthPolicyProvider.js +79 -0
- package/test/actionContextContributor.test.js +14 -6
- package/test/authActions.test.js +60 -237
- package/test/authPolicyContextResolverRegistry.test.js +9 -67
- package/test/authServiceDecoratorRegistry.test.js +21 -47
- package/test/providerRuntime.test.js +90 -141
- package/src/client/providers/FastifyAuthPolicyClientProvider.js +0 -13
- package/src/server/providers/AuthActionsServiceProvider.js +0 -76
- package/src/server/providers/FastifyAuthPolicyServiceProvider.js +0 -108
- package/src/server/services/authSessionEventsService.js +0 -29
package/package.json
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/auth-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.159",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
7
7
|
},
|
|
8
8
|
"exports": {
|
|
9
|
-
"./server/providers/
|
|
10
|
-
"./server/providers/
|
|
11
|
-
"./server/providers/
|
|
9
|
+
"./server/providers/AuthAccessProvider": "./src/server/providers/AuthAccessProvider.js",
|
|
10
|
+
"./server/providers/AuthExtensionsProvider": "./src/server/providers/AuthExtensionsProvider.js",
|
|
11
|
+
"./server/providers/AuthFeature": "./src/server/providers/AuthFeature.js",
|
|
12
|
+
"./server/providers/AuthPolicyProvider": "./src/server/providers/AuthPolicyProvider.js",
|
|
12
13
|
"./server/actions/auth.contributor": "./src/server/actions/auth.contributor.js",
|
|
13
|
-
"./server/
|
|
14
|
+
"./server/authExtensions": "./src/server/authExtensions.js",
|
|
14
15
|
"./server/authPolicyContextResolverRegistry": "./src/server/authPolicyContextResolverRegistry.js",
|
|
15
16
|
"./server/authServiceDecoratorRegistry": "./src/server/authServiceDecoratorRegistry.js",
|
|
16
17
|
"./server/authActor": "./src/server/authActor.js",
|
|
17
18
|
"./server/booleanFlag": "./src/server/booleanFlag.js",
|
|
18
19
|
"./server/devAuth": "./src/server/devAuth.js",
|
|
20
|
+
"./server/deferredAuthService": "./src/server/deferredAuthService.js",
|
|
19
21
|
"./server/unsupportedOperation": "./src/server/unsupportedOperation.js",
|
|
20
22
|
"./server/utils": "./src/server/utils.js",
|
|
21
23
|
"./server/validators": "./src/server/validators.js",
|
|
@@ -59,7 +61,7 @@
|
|
|
59
61
|
"@fastify/cookie": "^11.0.2",
|
|
60
62
|
"@fastify/csrf-protection": "^7.1.0",
|
|
61
63
|
"@fastify/rate-limit": "^10.3.0",
|
|
62
|
-
"@jskit-ai/kernel": "0.1.
|
|
64
|
+
"@jskit-ai/kernel": "0.1.161",
|
|
63
65
|
"json-rest-schema": "^1.0.17"
|
|
64
66
|
},
|
|
65
67
|
"jskit": {
|
|
@@ -67,25 +69,35 @@
|
|
|
67
69
|
"capabilities": {
|
|
68
70
|
"provides": [
|
|
69
71
|
"auth.access",
|
|
72
|
+
"auth.extensions",
|
|
70
73
|
"auth.policy"
|
|
71
74
|
],
|
|
72
|
-
"requires": [
|
|
75
|
+
"requires": [
|
|
76
|
+
"runtime.actions",
|
|
77
|
+
"runtime.env",
|
|
78
|
+
"runtime.fastify",
|
|
79
|
+
"runtime.http"
|
|
80
|
+
]
|
|
73
81
|
},
|
|
74
82
|
"runtime": {
|
|
75
83
|
"server": {
|
|
76
|
-
"providerEntrypoint": "src/server/providers/
|
|
84
|
+
"providerEntrypoint": "src/server/providers/AuthAccessProvider.js",
|
|
77
85
|
"providers": [
|
|
78
86
|
{
|
|
79
|
-
"entrypoint": "src/server/providers/
|
|
80
|
-
"export": "
|
|
87
|
+
"entrypoint": "src/server/providers/AuthAccessProvider.js",
|
|
88
|
+
"export": "AuthAccessProvider"
|
|
81
89
|
},
|
|
82
90
|
{
|
|
83
|
-
"entrypoint": "src/server/providers/
|
|
84
|
-
"export": "
|
|
91
|
+
"entrypoint": "src/server/providers/AuthExtensionsProvider.js",
|
|
92
|
+
"export": "AuthExtensionsProvider"
|
|
85
93
|
},
|
|
86
94
|
{
|
|
87
|
-
"entrypoint": "src/server/providers/
|
|
88
|
-
"export": "
|
|
95
|
+
"entrypoint": "src/server/providers/AuthFeature.js",
|
|
96
|
+
"export": "AuthFeature"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"entrypoint": "src/server/providers/AuthPolicyProvider.js",
|
|
100
|
+
"export": "AuthPolicyProvider"
|
|
89
101
|
}
|
|
90
102
|
]
|
|
91
103
|
},
|
|
@@ -94,10 +106,6 @@
|
|
|
94
106
|
{
|
|
95
107
|
"entrypoint": "src/client/providers/AccessCoreClientProvider.js",
|
|
96
108
|
"export": "AccessCoreClientProvider"
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
"entrypoint": "src/client/providers/FastifyAuthPolicyClientProvider.js",
|
|
100
|
-
"export": "FastifyAuthPolicyClientProvider"
|
|
101
109
|
}
|
|
102
110
|
]
|
|
103
111
|
}
|
|
@@ -107,7 +115,7 @@
|
|
|
107
115
|
"surfaces": [
|
|
108
116
|
{
|
|
109
117
|
"subpath": "./client",
|
|
110
|
-
"summary": "Exports client auth access
|
|
118
|
+
"summary": "Exports the client auth access API and its capability provider."
|
|
111
119
|
},
|
|
112
120
|
{
|
|
113
121
|
"subpath": "./server",
|
|
@@ -118,33 +126,10 @@
|
|
|
118
126
|
"summary": "Exports shared auth client helpers (createApi and runAuthSignOutFlow), with structured subpaths at ./shared/authApi and ./shared/signOutFlow."
|
|
119
127
|
}
|
|
120
128
|
],
|
|
121
|
-
"
|
|
122
|
-
"
|
|
123
|
-
|
|
124
|
-
"auth.session.events.service"
|
|
125
|
-
],
|
|
126
|
-
"client": [
|
|
127
|
-
"auth.access.client"
|
|
128
|
-
]
|
|
129
|
-
}
|
|
129
|
+
"clientCapabilities": [
|
|
130
|
+
"auth.access.client"
|
|
131
|
+
]
|
|
130
132
|
}
|
|
131
|
-
},
|
|
132
|
-
"mutations": {
|
|
133
|
-
"dependencies": {
|
|
134
|
-
"runtime": {
|
|
135
|
-
"@jskit-ai/kernel": "0.1.159",
|
|
136
|
-
"@fastify/cookie": "^11.0.2",
|
|
137
|
-
"@fastify/csrf-protection": "^7.1.0",
|
|
138
|
-
"@fastify/rate-limit": "^10.3.0"
|
|
139
|
-
},
|
|
140
|
-
"dev": {}
|
|
141
|
-
},
|
|
142
|
-
"packageJson": {
|
|
143
|
-
"scripts": {}
|
|
144
|
-
},
|
|
145
|
-
"procfile": {},
|
|
146
|
-
"files": [],
|
|
147
|
-
"text": []
|
|
148
133
|
}
|
|
149
134
|
}
|
|
150
135
|
}
|
package/src/client/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { defineProvider } from "@jskit-ai/kernel/shared/capabilities";
|
|
1
2
|
import { createApi as createAuthApi } from "../authApi.js";
|
|
2
3
|
import { runAuthSignOutFlow } from "../signOutFlow.js";
|
|
3
4
|
|
|
@@ -6,18 +7,16 @@ const CLIENT_API = Object.freeze({
|
|
|
6
7
|
runAuthSignOutFlow
|
|
7
8
|
});
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
const AccessCoreClientProvider = defineProvider({
|
|
11
|
+
id: "auth.access.client",
|
|
12
|
+
provides: {
|
|
13
|
+
auth: "auth.access.client"
|
|
14
|
+
},
|
|
15
|
+
setup() {
|
|
16
|
+
return {
|
|
17
|
+
auth: CLIENT_API
|
|
18
|
+
};
|
|
18
19
|
}
|
|
19
|
-
|
|
20
|
-
boot() {}
|
|
21
|
-
}
|
|
20
|
+
});
|
|
22
21
|
|
|
23
22
|
export { AccessCoreClientProvider };
|
|
@@ -45,7 +45,7 @@ function requireRequestContext(context, actionId) {
|
|
|
45
45
|
throw new Error(`${actionId} requires request context.`);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
const
|
|
48
|
+
const authActionSpecifications = Object.freeze([
|
|
49
49
|
{
|
|
50
50
|
id: "auth.register",
|
|
51
51
|
version: 1,
|
|
@@ -58,8 +58,8 @@ const baseAuthActions = Object.freeze([
|
|
|
58
58
|
actionName: "auth.register"
|
|
59
59
|
},
|
|
60
60
|
observability: {},
|
|
61
|
-
async
|
|
62
|
-
return
|
|
61
|
+
async run(authService, input) {
|
|
62
|
+
return authService.register(input);
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
65
|
{
|
|
@@ -74,8 +74,8 @@ const baseAuthActions = Object.freeze([
|
|
|
74
74
|
actionName: "auth.register.confirmation.resend"
|
|
75
75
|
},
|
|
76
76
|
observability: {},
|
|
77
|
-
async
|
|
78
|
-
return
|
|
77
|
+
async run(authService, input) {
|
|
78
|
+
return authService.resendRegisterConfirmation(input);
|
|
79
79
|
}
|
|
80
80
|
},
|
|
81
81
|
{
|
|
@@ -90,8 +90,8 @@ const baseAuthActions = Object.freeze([
|
|
|
90
90
|
actionName: "auth.login.password"
|
|
91
91
|
},
|
|
92
92
|
observability: {},
|
|
93
|
-
async
|
|
94
|
-
return
|
|
93
|
+
async run(authService, input) {
|
|
94
|
+
return authService.login(input);
|
|
95
95
|
}
|
|
96
96
|
},
|
|
97
97
|
{
|
|
@@ -106,8 +106,8 @@ const baseAuthActions = Object.freeze([
|
|
|
106
106
|
actionName: "auth.login.otp.request"
|
|
107
107
|
},
|
|
108
108
|
observability: {},
|
|
109
|
-
async
|
|
110
|
-
return
|
|
109
|
+
async run(authService, input) {
|
|
110
|
+
return authService.requestOtpLogin(input);
|
|
111
111
|
}
|
|
112
112
|
},
|
|
113
113
|
{
|
|
@@ -122,8 +122,8 @@ const baseAuthActions = Object.freeze([
|
|
|
122
122
|
actionName: "auth.login.otp.verify"
|
|
123
123
|
},
|
|
124
124
|
observability: {},
|
|
125
|
-
async
|
|
126
|
-
return
|
|
125
|
+
async run(authService, input) {
|
|
126
|
+
return authService.verifyOtpLogin(input);
|
|
127
127
|
}
|
|
128
128
|
},
|
|
129
129
|
{
|
|
@@ -138,8 +138,8 @@ const baseAuthActions = Object.freeze([
|
|
|
138
138
|
actionName: "auth.login.oauth.start"
|
|
139
139
|
},
|
|
140
140
|
observability: {},
|
|
141
|
-
async
|
|
142
|
-
return
|
|
141
|
+
async run(authService, input) {
|
|
142
|
+
return authService.oauthStart(input);
|
|
143
143
|
}
|
|
144
144
|
},
|
|
145
145
|
{
|
|
@@ -154,8 +154,8 @@ const baseAuthActions = Object.freeze([
|
|
|
154
154
|
actionName: "auth.login.oauth.complete"
|
|
155
155
|
},
|
|
156
156
|
observability: {},
|
|
157
|
-
async
|
|
158
|
-
return
|
|
157
|
+
async run(authService, input) {
|
|
158
|
+
return authService.oauthComplete(input);
|
|
159
159
|
}
|
|
160
160
|
},
|
|
161
161
|
{
|
|
@@ -170,8 +170,8 @@ const baseAuthActions = Object.freeze([
|
|
|
170
170
|
actionName: "auth.dev.loginAs"
|
|
171
171
|
},
|
|
172
172
|
observability: {},
|
|
173
|
-
async
|
|
174
|
-
return
|
|
173
|
+
async run(authService, input, context) {
|
|
174
|
+
return authService.devLoginAs(
|
|
175
175
|
requireRequestContext(context, "auth.dev.loginAs"),
|
|
176
176
|
input
|
|
177
177
|
);
|
|
@@ -189,8 +189,8 @@ const baseAuthActions = Object.freeze([
|
|
|
189
189
|
actionName: "auth.password.reset.request"
|
|
190
190
|
},
|
|
191
191
|
observability: {},
|
|
192
|
-
async
|
|
193
|
-
return
|
|
192
|
+
async run(authService, input) {
|
|
193
|
+
return authService.requestPasswordReset(input);
|
|
194
194
|
}
|
|
195
195
|
},
|
|
196
196
|
{
|
|
@@ -205,8 +205,8 @@ const baseAuthActions = Object.freeze([
|
|
|
205
205
|
actionName: "auth.password.recovery.complete"
|
|
206
206
|
},
|
|
207
207
|
observability: {},
|
|
208
|
-
async
|
|
209
|
-
return
|
|
208
|
+
async run(authService, input) {
|
|
209
|
+
return authService.completePasswordRecovery(input);
|
|
210
210
|
}
|
|
211
211
|
},
|
|
212
212
|
{
|
|
@@ -221,8 +221,8 @@ const baseAuthActions = Object.freeze([
|
|
|
221
221
|
actionName: "auth.password.reset"
|
|
222
222
|
},
|
|
223
223
|
observability: {},
|
|
224
|
-
async
|
|
225
|
-
return
|
|
224
|
+
async run(authService, input, context) {
|
|
225
|
+
return authService.resetPassword(requireRequestContext(context, "auth.password.reset"), input);
|
|
226
226
|
}
|
|
227
227
|
},
|
|
228
228
|
{
|
|
@@ -238,19 +238,13 @@ const baseAuthActions = Object.freeze([
|
|
|
238
238
|
actionName: "auth.logout"
|
|
239
239
|
},
|
|
240
240
|
observability: {},
|
|
241
|
-
async
|
|
241
|
+
async run(authService, _input, context) {
|
|
242
242
|
let logoutResult = {
|
|
243
243
|
ok: true,
|
|
244
244
|
clearSession: true
|
|
245
245
|
};
|
|
246
|
-
if (
|
|
247
|
-
logoutResult = await
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (deps.authSessionEventsService && typeof deps.authSessionEventsService.notifySessionChanged === "function") {
|
|
251
|
-
await deps.authSessionEventsService.notifySessionChanged({
|
|
252
|
-
context
|
|
253
|
-
});
|
|
246
|
+
if (typeof authService.logout === "function") {
|
|
247
|
+
logoutResult = await authService.logout(requireRequestContext(context, "auth.logout"));
|
|
254
248
|
}
|
|
255
249
|
return {
|
|
256
250
|
ok: logoutResult?.ok !== false,
|
|
@@ -270,14 +264,23 @@ const baseAuthActions = Object.freeze([
|
|
|
270
264
|
actionName: "auth.session.read"
|
|
271
265
|
},
|
|
272
266
|
observability: {},
|
|
273
|
-
async
|
|
274
|
-
return
|
|
267
|
+
async run(authService, _input, context) {
|
|
268
|
+
return authService.authenticateRequest(requireRequestContext(context, "auth.session.read"));
|
|
275
269
|
}
|
|
276
270
|
}
|
|
277
271
|
]);
|
|
278
272
|
|
|
279
|
-
function buildAuthActions() {
|
|
280
|
-
|
|
273
|
+
function buildAuthActions({ authService } = {}) {
|
|
274
|
+
if (!authService || typeof authService !== "object") {
|
|
275
|
+
throw new TypeError("buildAuthActions requires authService.");
|
|
276
|
+
}
|
|
277
|
+
return Object.freeze(authActionSpecifications.map(({ run, surfacesFrom: _surfaceSource, ...definition }) => Object.freeze({
|
|
278
|
+
...definition,
|
|
279
|
+
surfaces: ["*"],
|
|
280
|
+
execute(input, context) {
|
|
281
|
+
return run(authService, input, context);
|
|
282
|
+
}
|
|
283
|
+
})));
|
|
281
284
|
}
|
|
282
285
|
|
|
283
|
-
export {
|
|
286
|
+
export { authActionSpecifications, buildAuthActions, requireRequestContext };
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { normalizePermissionList } from "@jskit-ai/kernel/shared/support/permissions";
|
|
2
|
+
|
|
3
|
+
function normalizeOrderedExtension(value, { idField, method, label } = {}) {
|
|
4
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || typeof value[method] !== "function") {
|
|
5
|
+
throw new TypeError(`${label} requires ${method}().`);
|
|
6
|
+
}
|
|
7
|
+
const id = String(value[idField] || "").trim();
|
|
8
|
+
if (!id) {
|
|
9
|
+
throw new TypeError(`${label} requires ${idField}.`);
|
|
10
|
+
}
|
|
11
|
+
return Object.freeze({
|
|
12
|
+
...value,
|
|
13
|
+
[idField]: id,
|
|
14
|
+
order: Number.isFinite(value.order) ? Number(value.order) : 0,
|
|
15
|
+
[method]: value[method]
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function ordered(values) {
|
|
20
|
+
return values
|
|
21
|
+
.map((value, index) => ({ value, index }))
|
|
22
|
+
.sort((left, right) => left.value.order - right.value.order || left.index - right.index)
|
|
23
|
+
.map(({ value }) => value);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function mergePolicyContexts(contexts = []) {
|
|
27
|
+
const merged = {};
|
|
28
|
+
const permissions = new Set();
|
|
29
|
+
for (const context of contexts) {
|
|
30
|
+
if (!context || typeof context !== "object" || Array.isArray(context)) continue;
|
|
31
|
+
for (const [key, value] of Object.entries(context)) {
|
|
32
|
+
if (key === "permissions") {
|
|
33
|
+
for (const permission of normalizePermissionList(value)) permissions.add(permission);
|
|
34
|
+
} else if (value !== undefined) {
|
|
35
|
+
merged[key] = value;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (permissions.size > 0) merged.permissions = Object.freeze([...permissions]);
|
|
40
|
+
return Object.keys(merged).length > 0 ? Object.freeze(merged) : null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function createAuthExtensions() {
|
|
44
|
+
const serviceDecorators = [];
|
|
45
|
+
const policyContextResolvers = [];
|
|
46
|
+
const profileProjectors = [];
|
|
47
|
+
const invitationContextResolvers = [];
|
|
48
|
+
const ids = new Set();
|
|
49
|
+
let sealed = false;
|
|
50
|
+
|
|
51
|
+
function register(collection, extension, options) {
|
|
52
|
+
if (sealed) throw new Error("Auth extension registration is closed after authentication starts.");
|
|
53
|
+
const normalized = normalizeOrderedExtension(extension, options);
|
|
54
|
+
const qualifiedId = `${options.label}:${normalized[options.idField]}`;
|
|
55
|
+
if (ids.has(qualifiedId)) throw new Error(`${options.label} "${normalized[options.idField]}" is duplicated.`);
|
|
56
|
+
ids.add(qualifiedId);
|
|
57
|
+
collection.push(normalized);
|
|
58
|
+
return api;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function registerServiceDecorator(extension) {
|
|
62
|
+
return register(serviceDecorators, extension, {
|
|
63
|
+
idField: "decoratorId",
|
|
64
|
+
method: "decorateAuthService",
|
|
65
|
+
label: "Auth service decorator"
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function registerPolicyContextResolver(extension) {
|
|
70
|
+
return register(policyContextResolvers, extension, {
|
|
71
|
+
idField: "resolverId",
|
|
72
|
+
method: "resolveAuthPolicyContext",
|
|
73
|
+
label: "Auth policy context resolver"
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function registerProfileProjector(extension) {
|
|
78
|
+
return register(profileProjectors, extension, {
|
|
79
|
+
idField: "projectorId",
|
|
80
|
+
method: "syncIdentityProfile",
|
|
81
|
+
label: "Auth profile projector"
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function registerInvitationContextResolver(extension) {
|
|
86
|
+
return register(invitationContextResolvers, extension, {
|
|
87
|
+
idField: "resolverId",
|
|
88
|
+
method: "resolveInvitationContext",
|
|
89
|
+
label: "Auth invitation context resolver"
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function resolveSingle(collection, label) {
|
|
94
|
+
sealed = true;
|
|
95
|
+
const entries = ordered(collection);
|
|
96
|
+
if (entries.length > 1) {
|
|
97
|
+
throw new Error(`${label} is ambiguous: ${entries.map((entry) => entry.projectorId || entry.resolverId).join(", ")}.`);
|
|
98
|
+
}
|
|
99
|
+
return entries[0] || null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function resolveProfileProjector() {
|
|
103
|
+
return resolveSingle(profileProjectors, "Auth profile projector");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function resolveInvitationContextResolver() {
|
|
107
|
+
return resolveSingle(invitationContextResolvers, "Auth invitation context resolver");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function decorateService(authService) {
|
|
111
|
+
sealed = true;
|
|
112
|
+
let decorated = authService;
|
|
113
|
+
for (const decorator of ordered(serviceDecorators)) {
|
|
114
|
+
decorated = decorator.decorateAuthService(decorated);
|
|
115
|
+
if (!decorated || typeof decorated !== "object") {
|
|
116
|
+
throw new Error(`Auth service decorator "${decorator.decoratorId}" must return an auth service object.`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return decorated;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function resolvePolicyContext(input = {}) {
|
|
123
|
+
sealed = true;
|
|
124
|
+
const contexts = [];
|
|
125
|
+
for (const resolver of ordered(policyContextResolvers)) {
|
|
126
|
+
contexts.push(await resolver.resolveAuthPolicyContext(input));
|
|
127
|
+
}
|
|
128
|
+
return mergePolicyContexts(contexts);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function diagnostics() {
|
|
132
|
+
return Object.freeze({
|
|
133
|
+
sealed,
|
|
134
|
+
serviceDecoratorIds: Object.freeze(ordered(serviceDecorators).map((entry) => entry.decoratorId)),
|
|
135
|
+
policyContextResolverIds: Object.freeze(ordered(policyContextResolvers).map((entry) => entry.resolverId)),
|
|
136
|
+
profileProjectorIds: Object.freeze(ordered(profileProjectors).map((entry) => entry.projectorId)),
|
|
137
|
+
invitationContextResolverIds: Object.freeze(ordered(invitationContextResolvers).map((entry) => entry.resolverId))
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const api = Object.freeze({
|
|
142
|
+
registerServiceDecorator,
|
|
143
|
+
registerPolicyContextResolver,
|
|
144
|
+
registerProfileProjector,
|
|
145
|
+
registerInvitationContextResolver,
|
|
146
|
+
decorateService,
|
|
147
|
+
resolvePolicyContext,
|
|
148
|
+
resolveProfileProjector,
|
|
149
|
+
resolveInvitationContextResolver,
|
|
150
|
+
diagnostics
|
|
151
|
+
});
|
|
152
|
+
return api;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export { createAuthExtensions, mergePolicyContexts };
|
|
@@ -1,130 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { registerTaggedSingleton, resolveTaggedEntries } from "@jskit-ai/kernel/server/registries";
|
|
1
|
+
import { mergePolicyContexts } from "./authExtensions.js";
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
if (typeof entry === "function") {
|
|
8
|
-
return Object.freeze({
|
|
9
|
-
resolverId: String(entry.name || "anonymous"),
|
|
10
|
-
order: 0,
|
|
11
|
-
resolveAuthPolicyContext: entry
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if (!entry || typeof entry !== "object" || typeof entry.resolveAuthPolicyContext !== "function") {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const resolverId = String(entry.resolverId || "anonymous");
|
|
20
|
-
const order = Number.isFinite(entry.order) ? Number(entry.order) : 0;
|
|
21
|
-
|
|
22
|
-
return Object.freeze({
|
|
23
|
-
...entry,
|
|
24
|
-
resolverId,
|
|
25
|
-
order,
|
|
26
|
-
resolveAuthPolicyContext: entry.resolveAuthPolicyContext
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function registerAuthPolicyContextResolver(app, token, factory) {
|
|
31
|
-
registerTaggedSingleton(app, token, factory, AUTH_POLICY_CONTEXT_RESOLVER_TAG, {
|
|
32
|
-
context: "registerAuthPolicyContextResolver"
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function resolveAuthPolicyContextResolvers(scope) {
|
|
37
|
-
return resolveTaggedEntries(scope, AUTH_POLICY_CONTEXT_RESOLVER_TAG)
|
|
38
|
-
.map((entry, index) => ({
|
|
39
|
-
resolver: normalizeAuthPolicyContextResolver(entry),
|
|
40
|
-
index
|
|
41
|
-
}))
|
|
42
|
-
.filter((entry) => Boolean(entry.resolver))
|
|
43
|
-
.sort((left, right) => {
|
|
44
|
-
if (left.resolver.order !== right.resolver.order) {
|
|
45
|
-
return left.resolver.order - right.resolver.order;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return left.index - right.index;
|
|
49
|
-
})
|
|
50
|
-
.map((entry) => entry.resolver);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function resolveComposedAuthPolicyContextResolver(scope) {
|
|
54
|
-
return composeAuthPolicyContextResolvers(resolveAuthPolicyContextResolvers(scope));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function mergeAuthPolicyContexts(contexts = []) {
|
|
58
|
-
const merged = {};
|
|
59
|
-
let hasValues = false;
|
|
60
|
-
const permissions = new Set();
|
|
61
|
-
|
|
62
|
-
for (const context of Array.isArray(contexts) ? contexts : [contexts]) {
|
|
63
|
-
if (!context || typeof context !== "object") {
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
for (const [key, value] of Object.entries(context)) {
|
|
68
|
-
if (key === "permissions") {
|
|
69
|
-
for (const permission of normalizePermissionList(value)) {
|
|
70
|
-
permissions.add(permission);
|
|
71
|
-
}
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (value === undefined) {
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
merged[key] = value;
|
|
80
|
-
hasValues = true;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (permissions.size > 0) {
|
|
85
|
-
merged.permissions = Object.freeze([...permissions]);
|
|
86
|
-
hasValues = true;
|
|
3
|
+
function registerAuthPolicyContextResolver(extensions, resolver) {
|
|
4
|
+
if (!extensions || typeof extensions.registerPolicyContextResolver !== "function") {
|
|
5
|
+
throw new TypeError("registerAuthPolicyContextResolver requires auth.extensions.");
|
|
87
6
|
}
|
|
88
|
-
|
|
89
|
-
return hasValues ? Object.freeze(merged) : null;
|
|
7
|
+
return extensions.registerPolicyContextResolver(resolver);
|
|
90
8
|
}
|
|
91
9
|
|
|
92
|
-
function
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
resolver: normalizeAuthPolicyContextResolver(entry),
|
|
96
|
-
index
|
|
97
|
-
}))
|
|
98
|
-
.filter((entry) => Boolean(entry.resolver))
|
|
99
|
-
.sort((left, right) => {
|
|
100
|
-
if (left.resolver.order !== right.resolver.order) {
|
|
101
|
-
return left.resolver.order - right.resolver.order;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return left.index - right.index;
|
|
105
|
-
})
|
|
106
|
-
.map((entry) => entry.resolver);
|
|
107
|
-
|
|
108
|
-
if (normalizedResolvers.length < 1) {
|
|
109
|
-
return null;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return async function resolveComposedAuthPolicyContext(input = {}) {
|
|
113
|
-
const contexts = [];
|
|
114
|
-
|
|
115
|
-
for (const resolver of normalizedResolvers) {
|
|
116
|
-
contexts.push(await resolver.resolveAuthPolicyContext(input));
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return mergeAuthPolicyContexts(contexts);
|
|
120
|
-
};
|
|
10
|
+
function resolveComposedAuthPolicyContextResolver(extensions) {
|
|
11
|
+
if (!extensions || typeof extensions.resolvePolicyContext !== "function") return null;
|
|
12
|
+
return (input = {}) => extensions.resolvePolicyContext(input);
|
|
121
13
|
}
|
|
122
14
|
|
|
123
15
|
export {
|
|
124
|
-
AUTH_POLICY_CONTEXT_RESOLVER_TAG,
|
|
125
16
|
registerAuthPolicyContextResolver,
|
|
126
|
-
resolveAuthPolicyContextResolvers,
|
|
127
17
|
resolveComposedAuthPolicyContextResolver,
|
|
128
|
-
|
|
129
|
-
composeAuthPolicyContextResolvers
|
|
18
|
+
mergePolicyContexts
|
|
130
19
|
};
|