@jskit-ai/auth-core 0.1.99 → 0.1.101
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.descriptor.mjs +9 -4
- package/package.json +9 -2
- package/src/server/actions/auth.contributor.js +263 -0
- package/src/server/authActor.js +96 -0
- package/src/server/lib/index.js +12 -0
- package/src/server/providers/AuthActionsServiceProvider.js +67 -0
- package/src/server/providers/FastifyAuthPolicyServiceProvider.js +7 -6
- package/src/server/services/authSessionEventsService.js +30 -0
- package/src/server/unsupportedOperation.js +28 -0
- package/src/shared/authCapabilities.js +184 -0
- package/src/shared/authSecurityStatus.js +111 -0
- package/src/shared/commands/authCommandValidators.js +115 -0
- package/src/shared/index.js +12 -0
- package/test/authActions.test.js +174 -0
- package/test/authContract.test.js +171 -0
- package/test/commandValidators.test.js +5 -0
- package/test/providerRuntime.test.js +43 -0
package/package.descriptor.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default Object.freeze({
|
|
2
2
|
"packageVersion": 1,
|
|
3
3
|
"packageId": "@jskit-ai/auth-core",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.101",
|
|
5
5
|
"kind": "runtime",
|
|
6
6
|
"dependsOn": [
|
|
7
7
|
"@jskit-ai/value-app-config-shared"
|
|
@@ -21,6 +21,10 @@ export default Object.freeze({
|
|
|
21
21
|
"entrypoint": "src/server/providers/AccessCoreServiceProvider.js",
|
|
22
22
|
"export": "AccessCoreServiceProvider"
|
|
23
23
|
},
|
|
24
|
+
{
|
|
25
|
+
"entrypoint": "src/server/providers/AuthActionsServiceProvider.js",
|
|
26
|
+
"export": "AuthActionsServiceProvider"
|
|
27
|
+
},
|
|
24
28
|
{
|
|
25
29
|
"entrypoint": "src/server/providers/FastifyAuthPolicyServiceProvider.js",
|
|
26
30
|
"export": "FastifyAuthPolicyServiceProvider"
|
|
@@ -49,7 +53,7 @@ export default Object.freeze({
|
|
|
49
53
|
},
|
|
50
54
|
{
|
|
51
55
|
"subpath": "./server",
|
|
52
|
-
"summary": "Exports server auth access and Fastify auth policy providers plus server auth utility modules."
|
|
56
|
+
"summary": "Exports server auth access, shared auth action, and Fastify auth policy providers plus server auth utility modules."
|
|
53
57
|
},
|
|
54
58
|
{
|
|
55
59
|
"subpath": "./shared",
|
|
@@ -58,7 +62,8 @@ export default Object.freeze({
|
|
|
58
62
|
],
|
|
59
63
|
"containerTokens": {
|
|
60
64
|
"server": [
|
|
61
|
-
"auth.access"
|
|
65
|
+
"auth.access",
|
|
66
|
+
"auth.session.events.service"
|
|
62
67
|
],
|
|
63
68
|
"client": [
|
|
64
69
|
"auth.access.client"
|
|
@@ -69,7 +74,7 @@ export default Object.freeze({
|
|
|
69
74
|
"mutations": {
|
|
70
75
|
"dependencies": {
|
|
71
76
|
"runtime": {
|
|
72
|
-
"@jskit-ai/kernel": "0.1.
|
|
77
|
+
"@jskit-ai/kernel": "0.1.103",
|
|
73
78
|
"@fastify/cookie": "^11.0.2",
|
|
74
79
|
"@fastify/csrf-protection": "^7.1.0",
|
|
75
80
|
"@fastify/rate-limit": "^10.3.0"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/auth-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.101",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -8,8 +8,13 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
"./server/providers/AccessCoreServiceProvider": "./src/server/providers/AccessCoreServiceProvider.js",
|
|
10
10
|
"./server/providers/FastifyAuthPolicyServiceProvider": "./src/server/providers/FastifyAuthPolicyServiceProvider.js",
|
|
11
|
+
"./server/providers/AuthActionsServiceProvider": "./src/server/providers/AuthActionsServiceProvider.js",
|
|
12
|
+
"./server/actions/auth.contributor": "./src/server/actions/auth.contributor.js",
|
|
13
|
+
"./server/services/authSessionEventsService": "./src/server/services/authSessionEventsService.js",
|
|
11
14
|
"./server/authPolicyContextResolverRegistry": "./src/server/authPolicyContextResolverRegistry.js",
|
|
12
15
|
"./server/authServiceDecoratorRegistry": "./src/server/authServiceDecoratorRegistry.js",
|
|
16
|
+
"./server/authActor": "./src/server/authActor.js",
|
|
17
|
+
"./server/unsupportedOperation": "./src/server/unsupportedOperation.js",
|
|
13
18
|
"./server/utils": "./src/server/utils.js",
|
|
14
19
|
"./server/validators": "./src/server/validators.js",
|
|
15
20
|
"./server/inviteTokens": "./src/server/inviteTokens.js",
|
|
@@ -26,6 +31,8 @@
|
|
|
26
31
|
"./shared/authDenied": "./src/shared/authDenied.js",
|
|
27
32
|
"./shared/authPaths": "./src/shared/authPaths.js",
|
|
28
33
|
"./shared/authConstraints": "./src/shared/authConstraints.js",
|
|
34
|
+
"./shared/authCapabilities": "./src/shared/authCapabilities.js",
|
|
35
|
+
"./shared/authSecurityStatus": "./src/shared/authSecurityStatus.js",
|
|
29
36
|
"./shared/authMethods": "./src/shared/authMethods.js",
|
|
30
37
|
"./shared/oauthProviders": "./src/shared/oauthProviders.js",
|
|
31
38
|
"./shared/oauthCallbackParams": "./src/shared/oauthCallbackParams.js",
|
|
@@ -47,7 +54,7 @@
|
|
|
47
54
|
"./shared/commands/authSessionReadCommand": "./src/shared/commands/authSessionReadCommand.js"
|
|
48
55
|
},
|
|
49
56
|
"dependencies": {
|
|
50
|
-
"@jskit-ai/kernel": "0.1.
|
|
57
|
+
"@jskit-ai/kernel": "0.1.103",
|
|
51
58
|
"@fastify/cookie": "^11.0.2",
|
|
52
59
|
"@fastify/csrf-protection": "^7.1.0",
|
|
53
60
|
"@fastify/rate-limit": "^10.3.0",
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { createSchema } from "json-rest-schema";
|
|
2
|
+
import {
|
|
3
|
+
emptyInputValidator
|
|
4
|
+
} from "@jskit-ai/kernel/shared/actions/actionContributorHelpers";
|
|
5
|
+
import {
|
|
6
|
+
composeSchemaDefinitions
|
|
7
|
+
} from "@jskit-ai/kernel/shared/validators";
|
|
8
|
+
import { deepFreeze } from "@jskit-ai/kernel/shared/support/deepFreeze";
|
|
9
|
+
import {
|
|
10
|
+
authRegisterCommand,
|
|
11
|
+
authRegisterConfirmationResendCommand,
|
|
12
|
+
authLoginPasswordCommand,
|
|
13
|
+
authLoginOtpRequestCommand,
|
|
14
|
+
authLoginOtpVerifyCommand,
|
|
15
|
+
authLoginOAuthStartCommand,
|
|
16
|
+
authLoginOAuthCompleteCommand,
|
|
17
|
+
authPasswordResetRequestCommand,
|
|
18
|
+
authPasswordRecoveryCompleteCommand,
|
|
19
|
+
authPasswordResetCommand
|
|
20
|
+
} from "../../shared/commands/index.js";
|
|
21
|
+
|
|
22
|
+
const authLoginOAuthStartInput = composeSchemaDefinitions([
|
|
23
|
+
authLoginOAuthStartCommand.operation.params,
|
|
24
|
+
authLoginOAuthStartCommand.operation.query
|
|
25
|
+
], {
|
|
26
|
+
mode: "patch",
|
|
27
|
+
context: "authContributor.authLoginOAuthStartInput"
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const authLogoutOutput = deepFreeze({
|
|
31
|
+
schema: createSchema({
|
|
32
|
+
ok: { type: "boolean", required: true },
|
|
33
|
+
clearSession: { type: "boolean", required: true }
|
|
34
|
+
}),
|
|
35
|
+
mode: "replace"
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
function requireRequestContext(context, actionId) {
|
|
39
|
+
const request = context?.requestMeta?.request || null;
|
|
40
|
+
if (request) {
|
|
41
|
+
return request;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
throw new Error(`${actionId} requires request context.`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const baseAuthActions = Object.freeze([
|
|
48
|
+
{
|
|
49
|
+
id: "auth.register",
|
|
50
|
+
version: 1,
|
|
51
|
+
kind: "command",
|
|
52
|
+
channels: ["api", "internal"],
|
|
53
|
+
surfacesFrom: "enabled",
|
|
54
|
+
input: authRegisterCommand.operation.body,
|
|
55
|
+
idempotency: "none",
|
|
56
|
+
audit: {
|
|
57
|
+
actionName: "auth.register"
|
|
58
|
+
},
|
|
59
|
+
observability: {},
|
|
60
|
+
async execute(input, _context, deps) {
|
|
61
|
+
return deps.authService.register(input);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: "auth.register.confirmation.resend",
|
|
66
|
+
version: 1,
|
|
67
|
+
kind: "command",
|
|
68
|
+
channels: ["api", "internal"],
|
|
69
|
+
surfacesFrom: "enabled",
|
|
70
|
+
input: authRegisterConfirmationResendCommand.operation.body,
|
|
71
|
+
idempotency: "none",
|
|
72
|
+
audit: {
|
|
73
|
+
actionName: "auth.register.confirmation.resend"
|
|
74
|
+
},
|
|
75
|
+
observability: {},
|
|
76
|
+
async execute(input, _context, deps) {
|
|
77
|
+
return deps.authService.resendRegisterConfirmation(input);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: "auth.login.password",
|
|
82
|
+
version: 1,
|
|
83
|
+
kind: "command",
|
|
84
|
+
channels: ["api", "internal"],
|
|
85
|
+
surfacesFrom: "enabled",
|
|
86
|
+
input: authLoginPasswordCommand.operation.body,
|
|
87
|
+
idempotency: "none",
|
|
88
|
+
audit: {
|
|
89
|
+
actionName: "auth.login.password"
|
|
90
|
+
},
|
|
91
|
+
observability: {},
|
|
92
|
+
async execute(input, _context, deps) {
|
|
93
|
+
return deps.authService.login(input);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: "auth.login.otp.request",
|
|
98
|
+
version: 1,
|
|
99
|
+
kind: "command",
|
|
100
|
+
channels: ["api", "internal"],
|
|
101
|
+
surfacesFrom: "enabled",
|
|
102
|
+
input: authLoginOtpRequestCommand.operation.body,
|
|
103
|
+
idempotency: "none",
|
|
104
|
+
audit: {
|
|
105
|
+
actionName: "auth.login.otp.request"
|
|
106
|
+
},
|
|
107
|
+
observability: {},
|
|
108
|
+
async execute(input, _context, deps) {
|
|
109
|
+
return deps.authService.requestOtpLogin(input);
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: "auth.login.otp.verify",
|
|
114
|
+
version: 1,
|
|
115
|
+
kind: "command",
|
|
116
|
+
channels: ["api", "internal"],
|
|
117
|
+
surfacesFrom: "enabled",
|
|
118
|
+
input: authLoginOtpVerifyCommand.operation.body,
|
|
119
|
+
idempotency: "none",
|
|
120
|
+
audit: {
|
|
121
|
+
actionName: "auth.login.otp.verify"
|
|
122
|
+
},
|
|
123
|
+
observability: {},
|
|
124
|
+
async execute(input, _context, deps) {
|
|
125
|
+
return deps.authService.verifyOtpLogin(input);
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: "auth.login.oauth.start",
|
|
130
|
+
version: 1,
|
|
131
|
+
kind: "command",
|
|
132
|
+
channels: ["api", "internal"],
|
|
133
|
+
surfacesFrom: "enabled",
|
|
134
|
+
input: authLoginOAuthStartInput,
|
|
135
|
+
idempotency: "none",
|
|
136
|
+
audit: {
|
|
137
|
+
actionName: "auth.login.oauth.start"
|
|
138
|
+
},
|
|
139
|
+
observability: {},
|
|
140
|
+
async execute(input, _context, deps) {
|
|
141
|
+
return deps.authService.oauthStart(input);
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: "auth.login.oauth.complete",
|
|
146
|
+
version: 1,
|
|
147
|
+
kind: "command",
|
|
148
|
+
channels: ["api", "internal"],
|
|
149
|
+
surfacesFrom: "enabled",
|
|
150
|
+
input: authLoginOAuthCompleteCommand.operation.body,
|
|
151
|
+
idempotency: "none",
|
|
152
|
+
audit: {
|
|
153
|
+
actionName: "auth.login.oauth.complete"
|
|
154
|
+
},
|
|
155
|
+
observability: {},
|
|
156
|
+
async execute(input, _context, deps) {
|
|
157
|
+
return deps.authService.oauthComplete(input);
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: "auth.password.reset.request",
|
|
162
|
+
version: 1,
|
|
163
|
+
kind: "command",
|
|
164
|
+
channels: ["api", "internal"],
|
|
165
|
+
surfacesFrom: "enabled",
|
|
166
|
+
input: authPasswordResetRequestCommand.operation.body,
|
|
167
|
+
idempotency: "none",
|
|
168
|
+
audit: {
|
|
169
|
+
actionName: "auth.password.reset.request"
|
|
170
|
+
},
|
|
171
|
+
observability: {},
|
|
172
|
+
async execute(input, _context, deps) {
|
|
173
|
+
return deps.authService.requestPasswordReset(input);
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
id: "auth.password.recovery.complete",
|
|
178
|
+
version: 1,
|
|
179
|
+
kind: "command",
|
|
180
|
+
channels: ["api", "internal"],
|
|
181
|
+
surfacesFrom: "enabled",
|
|
182
|
+
input: authPasswordRecoveryCompleteCommand.operation.body,
|
|
183
|
+
idempotency: "none",
|
|
184
|
+
audit: {
|
|
185
|
+
actionName: "auth.password.recovery.complete"
|
|
186
|
+
},
|
|
187
|
+
observability: {},
|
|
188
|
+
async execute(input, _context, deps) {
|
|
189
|
+
return deps.authService.completePasswordRecovery(input);
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
id: "auth.password.reset",
|
|
194
|
+
version: 1,
|
|
195
|
+
kind: "command",
|
|
196
|
+
channels: ["api", "internal"],
|
|
197
|
+
surfacesFrom: "enabled",
|
|
198
|
+
input: authPasswordResetCommand.operation.body,
|
|
199
|
+
idempotency: "none",
|
|
200
|
+
audit: {
|
|
201
|
+
actionName: "auth.password.reset"
|
|
202
|
+
},
|
|
203
|
+
observability: {},
|
|
204
|
+
async execute(input, context, deps) {
|
|
205
|
+
return deps.authService.resetPassword(requireRequestContext(context, "auth.password.reset"), input);
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
id: "auth.logout",
|
|
210
|
+
version: 1,
|
|
211
|
+
kind: "command",
|
|
212
|
+
channels: ["api", "automation", "internal"],
|
|
213
|
+
surfacesFrom: "enabled",
|
|
214
|
+
input: emptyInputValidator,
|
|
215
|
+
output: authLogoutOutput,
|
|
216
|
+
idempotency: "none",
|
|
217
|
+
audit: {
|
|
218
|
+
actionName: "auth.logout"
|
|
219
|
+
},
|
|
220
|
+
observability: {},
|
|
221
|
+
async execute(_input, context, deps) {
|
|
222
|
+
let logoutResult = {
|
|
223
|
+
ok: true,
|
|
224
|
+
clearSession: true
|
|
225
|
+
};
|
|
226
|
+
if (deps.authService && typeof deps.authService.logout === "function") {
|
|
227
|
+
logoutResult = await deps.authService.logout(requireRequestContext(context, "auth.logout"));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (deps.authSessionEventsService && typeof deps.authSessionEventsService.notifySessionChanged === "function") {
|
|
231
|
+
await deps.authSessionEventsService.notifySessionChanged({
|
|
232
|
+
context
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
ok: logoutResult?.ok !== false,
|
|
237
|
+
clearSession: logoutResult?.clearSession !== false
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
id: "auth.session.read",
|
|
243
|
+
version: 1,
|
|
244
|
+
kind: "query",
|
|
245
|
+
channels: ["api", "internal"],
|
|
246
|
+
surfacesFrom: "enabled",
|
|
247
|
+
input: emptyInputValidator,
|
|
248
|
+
idempotency: "none",
|
|
249
|
+
audit: {
|
|
250
|
+
actionName: "auth.session.read"
|
|
251
|
+
},
|
|
252
|
+
observability: {},
|
|
253
|
+
async execute(_input, context, deps) {
|
|
254
|
+
return deps.authService.authenticateRequest(requireRequestContext(context, "auth.session.read"));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
]);
|
|
258
|
+
|
|
259
|
+
function buildAuthActions() {
|
|
260
|
+
return baseAuthActions;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export { baseAuthActions, buildAuthActions, requireRequestContext };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { normalizeOpaqueId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
|
+
import { normalizeAuthProviderId } from "../shared/authCapabilities.js";
|
|
3
|
+
import { normalizeEmail } from "./utils.js";
|
|
4
|
+
|
|
5
|
+
function normalizeDisplayName(value, email = "") {
|
|
6
|
+
const displayName = String(value || "").trim();
|
|
7
|
+
if (displayName) {
|
|
8
|
+
return displayName;
|
|
9
|
+
}
|
|
10
|
+
const localPart = String(email || "").split("@")[0]?.trim();
|
|
11
|
+
return localPart || "User";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function normalizeProviderUserId(value) {
|
|
15
|
+
return String(value || "").trim();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function createAuthIdentityId(provider, providerUserId) {
|
|
19
|
+
if (!provider || !providerUserId) {
|
|
20
|
+
return "";
|
|
21
|
+
}
|
|
22
|
+
return `${provider}:${providerUserId}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function normalizeAuthActor(value = {}, options = {}) {
|
|
26
|
+
const source = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
27
|
+
const provider = normalizeAuthProviderId(source.provider || source.authProvider || options.provider, {
|
|
28
|
+
fallback: normalizeAuthProviderId(options.provider, { fallback: "unknown" })
|
|
29
|
+
});
|
|
30
|
+
const providerUserId = normalizeProviderUserId(
|
|
31
|
+
source.providerUserId ||
|
|
32
|
+
source.authProviderUserSid ||
|
|
33
|
+
source.sub ||
|
|
34
|
+
source.userId ||
|
|
35
|
+
source.id
|
|
36
|
+
);
|
|
37
|
+
const email = normalizeEmail(source.email || "");
|
|
38
|
+
const displayName = normalizeDisplayName(source.displayName || source.username || source.name, email);
|
|
39
|
+
const appUserId = normalizeOpaqueId(source.appUserId || source.profileId || source.userProfileId, {
|
|
40
|
+
fallback: null
|
|
41
|
+
});
|
|
42
|
+
const authIdentityId = String(source.authIdentityId || createAuthIdentityId(provider, providerUserId)).trim();
|
|
43
|
+
|
|
44
|
+
if (!providerUserId || !email) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return Object.freeze({
|
|
49
|
+
authIdentityId,
|
|
50
|
+
provider,
|
|
51
|
+
providerUserId,
|
|
52
|
+
email,
|
|
53
|
+
displayName,
|
|
54
|
+
appUserId,
|
|
55
|
+
profileSource: String(source.profileSource || options.profileSource || "auth-provider").trim() || "auth-provider"
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function buildLegacyProfileFromActor(actorLike) {
|
|
60
|
+
const actor = normalizeAuthActor(actorLike);
|
|
61
|
+
if (!actor) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const id = actor.appUserId || actor.providerUserId || actor.authIdentityId;
|
|
65
|
+
return Object.freeze({
|
|
66
|
+
id,
|
|
67
|
+
email: actor.email,
|
|
68
|
+
displayName: actor.displayName,
|
|
69
|
+
authProvider: actor.provider,
|
|
70
|
+
authProviderUserSid: actor.providerUserId
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function normalizeAuthResult(value = {}, options = {}) {
|
|
75
|
+
const source = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
76
|
+
const actor = normalizeAuthActor(source.actor, options) || normalizeAuthActor(source.profile, {
|
|
77
|
+
...options,
|
|
78
|
+
profileSource: "users"
|
|
79
|
+
});
|
|
80
|
+
const profile = source.profile && typeof source.profile === "object"
|
|
81
|
+
? source.profile
|
|
82
|
+
: buildLegacyProfileFromActor(actor);
|
|
83
|
+
|
|
84
|
+
return Object.freeze({
|
|
85
|
+
...source,
|
|
86
|
+
...(actor ? { actor } : {}),
|
|
87
|
+
...(profile ? { profile } : {})
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export {
|
|
92
|
+
createAuthIdentityId,
|
|
93
|
+
normalizeAuthActor,
|
|
94
|
+
buildLegacyProfileFromActor,
|
|
95
|
+
normalizeAuthResult
|
|
96
|
+
};
|
package/src/server/lib/index.js
CHANGED
|
@@ -1,2 +1,14 @@
|
|
|
1
1
|
export { authPolicyPlugin } from "./plugin.js";
|
|
2
2
|
export { withAuthPolicy, mergeAuthPolicy } from "./routeMeta.js";
|
|
3
|
+
export {
|
|
4
|
+
createAuthIdentityId,
|
|
5
|
+
normalizeAuthActor,
|
|
6
|
+
buildLegacyProfileFromActor,
|
|
7
|
+
normalizeAuthResult
|
|
8
|
+
} from "../authActor.js";
|
|
9
|
+
export {
|
|
10
|
+
AUTH_OPERATION_UNSUPPORTED_CODE,
|
|
11
|
+
createUnsupportedAuthOperationError,
|
|
12
|
+
throwUnsupportedAuthOperation,
|
|
13
|
+
isUnsupportedAuthOperationError
|
|
14
|
+
} from "../unsupportedOperation.js";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
|
|
2
|
+
import { buildAuthActions } from "../actions/auth.contributor.js";
|
|
3
|
+
import { createAuthSessionEventsService } from "../services/authSessionEventsService.js";
|
|
4
|
+
|
|
5
|
+
class AuthActionsServiceProvider {
|
|
6
|
+
static id = "auth.actions";
|
|
7
|
+
|
|
8
|
+
static dependsOn = ["runtime.actions"];
|
|
9
|
+
|
|
10
|
+
register(app) {
|
|
11
|
+
if (
|
|
12
|
+
!app ||
|
|
13
|
+
typeof app.actions !== "function" ||
|
|
14
|
+
typeof app.has !== "function" ||
|
|
15
|
+
typeof app.service !== "function"
|
|
16
|
+
) {
|
|
17
|
+
throw new Error("AuthActionsServiceProvider requires application actions()/has()/service().");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!app.has("auth.session.events.service")) {
|
|
21
|
+
app.service(
|
|
22
|
+
"auth.session.events.service",
|
|
23
|
+
() => createAuthSessionEventsService(),
|
|
24
|
+
{
|
|
25
|
+
events: {
|
|
26
|
+
notifySessionChanged: [
|
|
27
|
+
{
|
|
28
|
+
type: "entity.changed",
|
|
29
|
+
source: "auth",
|
|
30
|
+
entity: "session",
|
|
31
|
+
operation: "updated",
|
|
32
|
+
entityId: ({ result }) => result?.id,
|
|
33
|
+
realtime: {
|
|
34
|
+
event: "auth.session.changed",
|
|
35
|
+
audience: "actor_user"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
type: "entity.changed",
|
|
40
|
+
source: "users",
|
|
41
|
+
entity: "bootstrap",
|
|
42
|
+
operation: "updated",
|
|
43
|
+
entityId: ({ result }) => result?.id,
|
|
44
|
+
realtime: {
|
|
45
|
+
event: "users.bootstrap.changed",
|
|
46
|
+
audience: "actor_user"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
app.actions(
|
|
56
|
+
withActionDefaults(buildAuthActions(), {
|
|
57
|
+
domain: "auth",
|
|
58
|
+
dependencies: {
|
|
59
|
+
authService: "authService",
|
|
60
|
+
authSessionEventsService: "auth.session.events.service"
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { AuthActionsServiceProvider };
|
|
@@ -38,8 +38,6 @@ function defaultHasPermission({ permission, permissions = [] } = {}) {
|
|
|
38
38
|
class FastifyAuthPolicyServiceProvider {
|
|
39
39
|
static id = "auth.policy.fastify";
|
|
40
40
|
|
|
41
|
-
static dependsOn = ["auth.provider"];
|
|
42
|
-
|
|
43
41
|
register(app) {
|
|
44
42
|
if (!app || typeof app.has !== "function") {
|
|
45
43
|
throw new Error("FastifyAuthPolicyServiceProvider requires application has().");
|
|
@@ -70,15 +68,18 @@ class FastifyAuthPolicyServiceProvider {
|
|
|
70
68
|
if (!app || typeof app.make !== "function" || typeof app.has !== "function") {
|
|
71
69
|
throw new Error("FastifyAuthPolicyServiceProvider requires application make()/has().");
|
|
72
70
|
}
|
|
73
|
-
if (!app.has("authService")) {
|
|
74
|
-
throw new Error("FastifyAuthPolicyServiceProvider requires authService binding.");
|
|
75
|
-
}
|
|
76
|
-
|
|
77
71
|
const env = app.has("jskit.env") ? app.make("jskit.env") : {};
|
|
78
72
|
const fastify = app.make("jskit.fastify");
|
|
79
73
|
|
|
80
74
|
const pluginDeps = {
|
|
81
75
|
resolveActor: async (request) => {
|
|
76
|
+
if (!app.has("authService")) {
|
|
77
|
+
return {
|
|
78
|
+
authenticated: false,
|
|
79
|
+
actor: null,
|
|
80
|
+
transientFailure: false
|
|
81
|
+
};
|
|
82
|
+
}
|
|
82
83
|
const authService = app.make("authService");
|
|
83
84
|
if (authService && typeof authService.authenticateRequest === "function") {
|
|
84
85
|
return authService.authenticateRequest(request);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { normalizeOpaqueId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
|
+
|
|
3
|
+
function resolveActorId(context = {}) {
|
|
4
|
+
return normalizeOpaqueId(
|
|
5
|
+
context?.actor?.id ||
|
|
6
|
+
context?.actor?.appUserId ||
|
|
7
|
+
context?.actor?.providerUserId ||
|
|
8
|
+
context?.profile?.id,
|
|
9
|
+
{ fallback: null }
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function createAuthSessionEventsService() {
|
|
14
|
+
async function notifySessionChanged(options = {}) {
|
|
15
|
+
const actorId = resolveActorId(options?.context || {});
|
|
16
|
+
if (!actorId) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
id: actorId
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return Object.freeze({
|
|
26
|
+
notifySessionChanged
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { createAuthSessionEventsService };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AppError } from "@jskit-ai/kernel/server/runtime/errors";
|
|
2
|
+
|
|
3
|
+
const AUTH_OPERATION_UNSUPPORTED_CODE = "AUTH_OPERATION_UNSUPPORTED";
|
|
4
|
+
|
|
5
|
+
function createUnsupportedAuthOperationError(operation, message = "") {
|
|
6
|
+
const operationName = String(operation || "auth operation").trim() || "auth operation";
|
|
7
|
+
return new AppError(501, message || `Auth operation "${operationName}" is not supported by the active provider.`, {
|
|
8
|
+
code: AUTH_OPERATION_UNSUPPORTED_CODE,
|
|
9
|
+
details: {
|
|
10
|
+
operation: operationName
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function throwUnsupportedAuthOperation(operation, message = "") {
|
|
16
|
+
throw createUnsupportedAuthOperationError(operation, message);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isUnsupportedAuthOperationError(error) {
|
|
20
|
+
return String(error?.code || "") === AUTH_OPERATION_UNSUPPORTED_CODE;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
AUTH_OPERATION_UNSUPPORTED_CODE,
|
|
25
|
+
createUnsupportedAuthOperationError,
|
|
26
|
+
throwUnsupportedAuthOperation,
|
|
27
|
+
isUnsupportedAuthOperationError
|
|
28
|
+
};
|