@jskit-ai/auth-web 0.1.56 → 0.1.57
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 +5 -6
- package/package.json +9 -6
- package/src/client/composables/loginView/useLoginViewActions.js +9 -9
- package/src/client/composables/loginView/useLoginViewValidation.js +1 -1
- package/src/client/providers/bootAuthClientProvider.js +11 -2
- package/src/server/providers/AuthWebServiceProvider.js +30 -2
- package/src/server/routes/authRoutes.js +39 -39
- package/src/server/services/AuthWebService.js +33 -10
- package/test/clientSurface.test.js +3 -3
- package/test/index.test.js +4 -4
- package/test/provider.test.js +34 -1
- package/test/providerRuntime.test.js +53 -4
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-web",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.57",
|
|
5
5
|
"kind": "runtime",
|
|
6
6
|
"description": "Auth web module: Fastify auth routes plus web login/sign-out scaffolds.",
|
|
7
7
|
"dependsOn": [
|
|
@@ -219,11 +219,10 @@ export default Object.freeze({
|
|
|
219
219
|
"runtime": {
|
|
220
220
|
"@tanstack/vue-query": "5.92.12",
|
|
221
221
|
"@mdi/js": "^7.4.47",
|
|
222
|
-
"@
|
|
223
|
-
"@jskit-ai/
|
|
224
|
-
"@jskit-ai/
|
|
225
|
-
"@jskit-ai/
|
|
226
|
-
"@jskit-ai/shell-web": "0.1.54",
|
|
222
|
+
"@jskit-ai/auth-core": "0.1.55",
|
|
223
|
+
"@jskit-ai/http-runtime": "0.1.55",
|
|
224
|
+
"@jskit-ai/kernel": "0.1.56",
|
|
225
|
+
"@jskit-ai/shell-web": "0.1.55",
|
|
227
226
|
"vuetify": "^4.0.0"
|
|
228
227
|
},
|
|
229
228
|
"dev": {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/auth-web",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.57",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -18,13 +18,16 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@tanstack/vue-query": "^5.90.5",
|
|
21
|
-
"@jskit-ai/auth-core": "0.1.
|
|
21
|
+
"@jskit-ai/auth-core": "0.1.55",
|
|
22
22
|
"@mdi/js": "^7.4.47",
|
|
23
|
-
"@
|
|
24
|
-
"@jskit-ai/
|
|
25
|
-
"@jskit-ai/shell-web": "0.1.54",
|
|
23
|
+
"@jskit-ai/kernel": "0.1.56",
|
|
24
|
+
"@jskit-ai/shell-web": "0.1.55",
|
|
26
25
|
"pinia": "^3.0.4",
|
|
27
26
|
"vuetify": "^4.0.0",
|
|
28
|
-
"@jskit-ai/http-runtime": "0.1.
|
|
27
|
+
"@jskit-ai/http-runtime": "0.1.55"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"vue": "^3.5.13",
|
|
31
|
+
"vue-router": "^5.0.4"
|
|
29
32
|
}
|
|
30
33
|
}
|
|
@@ -150,7 +150,7 @@ export function useLoginViewActions({
|
|
|
150
150
|
email: normalizedEmail,
|
|
151
151
|
password: String(state.password.value || "")
|
|
152
152
|
};
|
|
153
|
-
ensureCommandSectionValid(authRegisterCommand, "
|
|
153
|
+
ensureCommandSectionValid(authRegisterCommand, "body", registerPayload, "Unable to register.");
|
|
154
154
|
|
|
155
155
|
const registerResult = await request(AUTH_PATHS.REGISTER, {
|
|
156
156
|
method: "POST",
|
|
@@ -178,7 +178,7 @@ export function useLoginViewActions({
|
|
|
178
178
|
const forgotPayload = { email: normalizedEmail };
|
|
179
179
|
ensureCommandSectionValid(
|
|
180
180
|
authPasswordResetRequestCommand,
|
|
181
|
-
"
|
|
181
|
+
"body",
|
|
182
182
|
forgotPayload,
|
|
183
183
|
"Unable to request password reset."
|
|
184
184
|
);
|
|
@@ -196,7 +196,7 @@ export function useLoginViewActions({
|
|
|
196
196
|
token: String(state.otpCode.value || "").trim(),
|
|
197
197
|
type: "email"
|
|
198
198
|
};
|
|
199
|
-
ensureCommandSectionValid(authLoginOtpVerifyCommand, "
|
|
199
|
+
ensureCommandSectionValid(authLoginOtpVerifyCommand, "body", otpPayload, "Unable to verify one-time code.");
|
|
200
200
|
|
|
201
201
|
const otpResult = await request(AUTH_PATHS.LOGIN_OTP_VERIFY, {
|
|
202
202
|
method: "POST",
|
|
@@ -215,7 +215,7 @@ export function useLoginViewActions({
|
|
|
215
215
|
email: normalizedEmail,
|
|
216
216
|
password: String(state.password.value || "")
|
|
217
217
|
};
|
|
218
|
-
ensureCommandSectionValid(authLoginPasswordCommand, "
|
|
218
|
+
ensureCommandSectionValid(authLoginPasswordCommand, "body", loginPayload, "Unable to sign in.");
|
|
219
219
|
|
|
220
220
|
const loginResult = await request(AUTH_PATHS.LOGIN, {
|
|
221
221
|
method: "POST",
|
|
@@ -284,7 +284,7 @@ export function useLoginViewActions({
|
|
|
284
284
|
});
|
|
285
285
|
ensureCommandSectionValid(
|
|
286
286
|
authLoginOAuthCompleteCommand,
|
|
287
|
-
"
|
|
287
|
+
"body",
|
|
288
288
|
payload,
|
|
289
289
|
"Invalid OAuth callback payload."
|
|
290
290
|
);
|
|
@@ -358,7 +358,7 @@ export function useLoginViewActions({
|
|
|
358
358
|
};
|
|
359
359
|
ensureCommandSectionValid(
|
|
360
360
|
authLoginOtpRequestCommand,
|
|
361
|
-
"
|
|
361
|
+
"body",
|
|
362
362
|
otpRequestPayload,
|
|
363
363
|
"Unable to request one-time code."
|
|
364
364
|
);
|
|
@@ -389,7 +389,7 @@ export function useLoginViewActions({
|
|
|
389
389
|
};
|
|
390
390
|
ensureCommandSectionValid(
|
|
391
391
|
authRegisterConfirmationResendCommand,
|
|
392
|
-
"
|
|
392
|
+
"body",
|
|
393
393
|
resendPayload,
|
|
394
394
|
"Unable to resend confirmation email."
|
|
395
395
|
);
|
|
@@ -441,13 +441,13 @@ export function useLoginViewActions({
|
|
|
441
441
|
try {
|
|
442
442
|
ensureCommandSectionValid(
|
|
443
443
|
authLoginOAuthStartCommand,
|
|
444
|
-
"
|
|
444
|
+
"params",
|
|
445
445
|
paramsPayload,
|
|
446
446
|
"OAuth provider id is invalid."
|
|
447
447
|
);
|
|
448
448
|
ensureCommandSectionValid(
|
|
449
449
|
authLoginOAuthStartCommand,
|
|
450
|
-
"
|
|
450
|
+
"query",
|
|
451
451
|
queryPayload,
|
|
452
452
|
"OAuth return path is invalid."
|
|
453
453
|
);
|
|
@@ -2,8 +2,8 @@ import { AUTH_GUARD_RUNTIME_INJECTION_KEY } from "../runtime/inject.js";
|
|
|
2
2
|
import { useAuthStore } from "../stores/useAuthStore.js";
|
|
3
3
|
|
|
4
4
|
async function bootAuthClientProvider(app) {
|
|
5
|
-
if (!app || typeof app.make !== "function") {
|
|
6
|
-
throw new Error("AuthWebClientProvider requires application make().");
|
|
5
|
+
if (!app || typeof app.make !== "function" || typeof app.has !== "function") {
|
|
6
|
+
throw new Error("AuthWebClientProvider requires application make()/has().");
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
const authGuardRuntime = app.make("runtime.auth-guard.client");
|
|
@@ -15,6 +15,15 @@ async function bootAuthClientProvider(app) {
|
|
|
15
15
|
authStore.attachRuntime(authGuardRuntime);
|
|
16
16
|
await authStore.initialize();
|
|
17
17
|
|
|
18
|
+
if (app.has("runtime.web-bootstrap.client")) {
|
|
19
|
+
const bootstrapRuntime = app.make("runtime.web-bootstrap.client");
|
|
20
|
+
if (bootstrapRuntime && typeof bootstrapRuntime.refresh === "function") {
|
|
21
|
+
authStore.subscribe(() => {
|
|
22
|
+
void bootstrapRuntime.refresh("auth.state");
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
18
27
|
if (!app.has("jskit.client.vue.app")) {
|
|
19
28
|
return;
|
|
20
29
|
}
|
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
import { AuthWebService } from "../services/AuthWebService.js";
|
|
2
2
|
|
|
3
|
+
function parseBoolean(value, fallback = false) {
|
|
4
|
+
const raw = String(value || "").trim().toLowerCase();
|
|
5
|
+
if (!raw) {
|
|
6
|
+
return fallback;
|
|
7
|
+
}
|
|
8
|
+
if (["1", "true", "yes", "on"].includes(raw)) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
if (["0", "false", "no", "off"].includes(raw)) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return fallback;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function resolveDevAuthBootstrapEnabled(scope) {
|
|
18
|
+
const env =
|
|
19
|
+
scope && typeof scope.has === "function" && scope.has("jskit.env")
|
|
20
|
+
? scope.make("jskit.env")
|
|
21
|
+
: {};
|
|
22
|
+
|
|
23
|
+
return parseBoolean(env?.AUTH_DEV_BYPASS_ENABLED, false) &&
|
|
24
|
+
String(env?.NODE_ENV || "development").trim().toLowerCase() !== "production";
|
|
25
|
+
}
|
|
26
|
+
|
|
3
27
|
class AuthWebServiceProvider {
|
|
4
28
|
static id = "auth.web";
|
|
5
29
|
|
|
@@ -14,8 +38,12 @@ class AuthWebServiceProvider {
|
|
|
14
38
|
}
|
|
15
39
|
|
|
16
40
|
app.singleton("auth.web.service", (scope) => {
|
|
17
|
-
|
|
18
|
-
|
|
41
|
+
return new AuthWebService({
|
|
42
|
+
getAuthService() {
|
|
43
|
+
return scope.make("authService");
|
|
44
|
+
},
|
|
45
|
+
devAuthBootstrapEnabled: resolveDevAuthBootstrapEnabled(scope)
|
|
46
|
+
});
|
|
19
47
|
});
|
|
20
48
|
}
|
|
21
49
|
}
|
|
@@ -32,10 +32,10 @@ function buildRoutes(controller, { includeDevLoginAs = false } = {}) {
|
|
|
32
32
|
tags: ["auth"],
|
|
33
33
|
summary: "Register a new user"
|
|
34
34
|
},
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
body: authRegisterCommand.operation.body,
|
|
36
|
+
responses: withStandardErrorResponses(
|
|
37
37
|
{
|
|
38
|
-
201: authRegisterCommand.operation.
|
|
38
|
+
201: authRegisterCommand.operation.response
|
|
39
39
|
},
|
|
40
40
|
{ includeValidation400: true }
|
|
41
41
|
),
|
|
@@ -53,10 +53,10 @@ function buildRoutes(controller, { includeDevLoginAs = false } = {}) {
|
|
|
53
53
|
tags: ["auth"],
|
|
54
54
|
summary: "Resend sign-up email confirmation"
|
|
55
55
|
},
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
body: authRegisterConfirmationResendCommand.operation.body,
|
|
57
|
+
responses: withStandardErrorResponses(
|
|
58
58
|
{
|
|
59
|
-
200: authRegisterConfirmationResendCommand.operation.
|
|
59
|
+
200: authRegisterConfirmationResendCommand.operation.response
|
|
60
60
|
},
|
|
61
61
|
{ includeValidation400: true }
|
|
62
62
|
),
|
|
@@ -74,10 +74,10 @@ function buildRoutes(controller, { includeDevLoginAs = false } = {}) {
|
|
|
74
74
|
tags: ["auth"],
|
|
75
75
|
summary: "Log in with configured credentials"
|
|
76
76
|
},
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
body: authLoginPasswordCommand.operation.body,
|
|
78
|
+
responses: withStandardErrorResponses(
|
|
79
79
|
{
|
|
80
|
-
200: authLoginPasswordCommand.operation.
|
|
80
|
+
200: authLoginPasswordCommand.operation.response
|
|
81
81
|
},
|
|
82
82
|
{ includeValidation400: true }
|
|
83
83
|
),
|
|
@@ -95,10 +95,10 @@ function buildRoutes(controller, { includeDevLoginAs = false } = {}) {
|
|
|
95
95
|
tags: ["auth"],
|
|
96
96
|
summary: "Request one-time email login code"
|
|
97
97
|
},
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
body: authLoginOtpRequestCommand.operation.body,
|
|
99
|
+
responses: withStandardErrorResponses(
|
|
100
100
|
{
|
|
101
|
-
200: authLoginOtpRequestCommand.operation.
|
|
101
|
+
200: authLoginOtpRequestCommand.operation.response
|
|
102
102
|
},
|
|
103
103
|
{ includeValidation400: true }
|
|
104
104
|
),
|
|
@@ -116,10 +116,10 @@ function buildRoutes(controller, { includeDevLoginAs = false } = {}) {
|
|
|
116
116
|
tags: ["auth"],
|
|
117
117
|
summary: "Verify one-time email login code and create session"
|
|
118
118
|
},
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
body: authLoginOtpVerifyCommand.operation.body,
|
|
120
|
+
responses: withStandardErrorResponses(
|
|
121
121
|
{
|
|
122
|
-
200: authLoginOtpVerifyCommand.operation.
|
|
122
|
+
200: authLoginOtpVerifyCommand.operation.response
|
|
123
123
|
},
|
|
124
124
|
{ includeValidation400: true }
|
|
125
125
|
),
|
|
@@ -138,11 +138,11 @@ function buildRoutes(controller, { includeDevLoginAs = false } = {}) {
|
|
|
138
138
|
tags: ["auth"],
|
|
139
139
|
summary: "Start OAuth login with configured provider"
|
|
140
140
|
},
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
params: authLoginOAuthStartCommand.operation.params,
|
|
142
|
+
query: authLoginOAuthStartCommand.operation.query,
|
|
143
|
+
responses: withStandardErrorResponses(
|
|
144
144
|
{
|
|
145
|
-
302: authLoginOAuthStartCommand.operation.
|
|
145
|
+
302: authLoginOAuthStartCommand.operation.response
|
|
146
146
|
},
|
|
147
147
|
{ includeValidation400: true }
|
|
148
148
|
),
|
|
@@ -160,10 +160,10 @@ function buildRoutes(controller, { includeDevLoginAs = false } = {}) {
|
|
|
160
160
|
tags: ["auth"],
|
|
161
161
|
summary: "Complete OAuth code exchange and set session cookies"
|
|
162
162
|
},
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
body: authLoginOAuthCompleteCommand.operation.body,
|
|
164
|
+
responses: withStandardErrorResponses(
|
|
165
165
|
{
|
|
166
|
-
200: authLoginOAuthCompleteCommand.operation.
|
|
166
|
+
200: authLoginOAuthCompleteCommand.operation.response
|
|
167
167
|
},
|
|
168
168
|
{ includeValidation400: true }
|
|
169
169
|
),
|
|
@@ -181,10 +181,10 @@ function buildRoutes(controller, { includeDevLoginAs = false } = {}) {
|
|
|
181
181
|
tags: ["auth"],
|
|
182
182
|
summary: "Dev-only: create a local session for an existing user"
|
|
183
183
|
},
|
|
184
|
-
|
|
185
|
-
|
|
184
|
+
body: authDevLoginAsCommand.operation.body,
|
|
185
|
+
responses: withStandardErrorResponses(
|
|
186
186
|
{
|
|
187
|
-
200: authDevLoginAsCommand.operation.
|
|
187
|
+
200: authDevLoginAsCommand.operation.response
|
|
188
188
|
},
|
|
189
189
|
{ includeValidation400: true }
|
|
190
190
|
),
|
|
@@ -202,10 +202,10 @@ function buildRoutes(controller, { includeDevLoginAs = false } = {}) {
|
|
|
202
202
|
tags: ["auth"],
|
|
203
203
|
summary: "Request a password reset email"
|
|
204
204
|
},
|
|
205
|
-
|
|
206
|
-
|
|
205
|
+
body: authPasswordResetRequestCommand.operation.body,
|
|
206
|
+
responses: withStandardErrorResponses(
|
|
207
207
|
{
|
|
208
|
-
200: authPasswordResetRequestCommand.operation.
|
|
208
|
+
200: authPasswordResetRequestCommand.operation.response
|
|
209
209
|
},
|
|
210
210
|
{ includeValidation400: true }
|
|
211
211
|
),
|
|
@@ -223,10 +223,10 @@ function buildRoutes(controller, { includeDevLoginAs = false } = {}) {
|
|
|
223
223
|
tags: ["auth"],
|
|
224
224
|
summary: "Complete password recovery link exchange"
|
|
225
225
|
},
|
|
226
|
-
|
|
227
|
-
|
|
226
|
+
body: authPasswordRecoveryCompleteCommand.operation.body,
|
|
227
|
+
responses: withStandardErrorResponses(
|
|
228
228
|
{
|
|
229
|
-
200: authPasswordRecoveryCompleteCommand.operation.
|
|
229
|
+
200: authPasswordRecoveryCompleteCommand.operation.response
|
|
230
230
|
},
|
|
231
231
|
{ includeValidation400: true }
|
|
232
232
|
),
|
|
@@ -244,10 +244,10 @@ function buildRoutes(controller, { includeDevLoginAs = false } = {}) {
|
|
|
244
244
|
tags: ["auth"],
|
|
245
245
|
summary: "Set a new password for authenticated recovery session"
|
|
246
246
|
},
|
|
247
|
-
|
|
248
|
-
|
|
247
|
+
body: authPasswordResetCommand.operation.body,
|
|
248
|
+
responses: withStandardErrorResponses(
|
|
249
249
|
{
|
|
250
|
-
200: authPasswordResetCommand.operation.
|
|
250
|
+
200: authPasswordResetCommand.operation.response
|
|
251
251
|
},
|
|
252
252
|
{ includeValidation400: true }
|
|
253
253
|
),
|
|
@@ -265,8 +265,8 @@ function buildRoutes(controller, { includeDevLoginAs = false } = {}) {
|
|
|
265
265
|
tags: ["auth"],
|
|
266
266
|
summary: "Log out and clear session cookies"
|
|
267
267
|
},
|
|
268
|
-
|
|
269
|
-
200: authLogoutCommand.operation.
|
|
268
|
+
responses: withStandardErrorResponses({
|
|
269
|
+
200: authLogoutCommand.operation.response
|
|
270
270
|
}),
|
|
271
271
|
handler: handler("logout")
|
|
272
272
|
},
|
|
@@ -278,9 +278,9 @@ function buildRoutes(controller, { includeDevLoginAs = false } = {}) {
|
|
|
278
278
|
tags: ["auth"],
|
|
279
279
|
summary: "Get current session status and CSRF token"
|
|
280
280
|
},
|
|
281
|
-
|
|
282
|
-
200: authSessionReadCommand.operation.
|
|
283
|
-
503: authSessionReadCommand.operation.
|
|
281
|
+
responses: withStandardErrorResponses({
|
|
282
|
+
200: authSessionReadCommand.operation.response,
|
|
283
|
+
503: authSessionReadCommand.operation.unavailableResponse
|
|
284
284
|
}),
|
|
285
285
|
handler: handler("session")
|
|
286
286
|
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { AUTH_ACTION_IDS } from "../constants/authActionIds.js";
|
|
2
2
|
|
|
3
3
|
class AuthWebService {
|
|
4
|
-
constructor({ authService } = {}) {
|
|
5
|
-
if (!authService) {
|
|
6
|
-
throw new Error("authService is required.");
|
|
4
|
+
constructor({ authService, getAuthService, devAuthBootstrapEnabled } = {}) {
|
|
5
|
+
if (!authService && typeof getAuthService !== "function") {
|
|
6
|
+
throw new Error("authService or getAuthService is required.");
|
|
7
7
|
}
|
|
8
|
-
this.authService = authService;
|
|
8
|
+
this.authService = authService || null;
|
|
9
|
+
this.getAuthService = typeof getAuthService === "function" ? getAuthService : null;
|
|
10
|
+
this.devAuthBootstrapEnabled =
|
|
11
|
+
typeof devAuthBootstrapEnabled === "boolean" ? devAuthBootstrapEnabled : null;
|
|
9
12
|
}
|
|
10
13
|
|
|
11
14
|
static get actionIds() {
|
|
@@ -68,9 +71,28 @@ class AuthWebService {
|
|
|
68
71
|
});
|
|
69
72
|
}
|
|
70
73
|
|
|
74
|
+
resolveAuthService() {
|
|
75
|
+
if (this.authService) {
|
|
76
|
+
return this.authService;
|
|
77
|
+
}
|
|
78
|
+
if (typeof this.getAuthService !== "function") {
|
|
79
|
+
throw new Error("authService is required.");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.authService = this.getAuthService();
|
|
83
|
+
if (!this.authService) {
|
|
84
|
+
throw new Error("authService is required.");
|
|
85
|
+
}
|
|
86
|
+
return this.authService;
|
|
87
|
+
}
|
|
88
|
+
|
|
71
89
|
isDevLoginAsAvailable() {
|
|
72
|
-
|
|
73
|
-
|
|
90
|
+
if (this.devAuthBootstrapEnabled != null) {
|
|
91
|
+
return this.devAuthBootstrapEnabled === true;
|
|
92
|
+
}
|
|
93
|
+
const authService = this.resolveAuthService();
|
|
94
|
+
return typeof authService?.isDevAuthBootstrapEnabled === "function"
|
|
95
|
+
? authService.isDevAuthBootstrapEnabled()
|
|
74
96
|
: false;
|
|
75
97
|
}
|
|
76
98
|
|
|
@@ -109,20 +131,21 @@ class AuthWebService {
|
|
|
109
131
|
|
|
110
132
|
writeSessionCookies(reply, session) {
|
|
111
133
|
if (session && reply) {
|
|
112
|
-
this.
|
|
134
|
+
this.resolveAuthService().writeSessionCookies(reply, session);
|
|
113
135
|
}
|
|
114
136
|
}
|
|
115
137
|
|
|
116
138
|
clearSessionCookies(reply) {
|
|
117
139
|
if (reply) {
|
|
118
|
-
this.
|
|
140
|
+
this.resolveAuthService().clearSessionCookies(reply);
|
|
119
141
|
}
|
|
120
142
|
}
|
|
121
143
|
|
|
122
144
|
getOAuthProviderCatalogPayload() {
|
|
145
|
+
const authService = this.resolveAuthService();
|
|
123
146
|
const catalog =
|
|
124
|
-
typeof
|
|
125
|
-
?
|
|
147
|
+
typeof authService.getOAuthProviderCatalog === "function"
|
|
148
|
+
? authService.getOAuthProviderCatalog()
|
|
126
149
|
: null;
|
|
127
150
|
const providers = Array.isArray(catalog?.providers)
|
|
128
151
|
? catalog.providers
|
|
@@ -37,15 +37,15 @@ test("auth-web exports runtime signout helpers directly", () => {
|
|
|
37
37
|
assert.equal(typeof fromRuntimePerformSignOutRequest, "function");
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
test("auth-web removes
|
|
41
|
-
const
|
|
40
|
+
test("auth-web removes copied client wrapper modules", () => {
|
|
41
|
+
const removedFiles = [
|
|
42
42
|
"../src/client/composables/authHttpClient.js",
|
|
43
43
|
"../src/client/composables/authGuardRuntime.js",
|
|
44
44
|
"../src/client/composables/useSignOut.js",
|
|
45
45
|
"../src/client/api/AuthHttpClient.js"
|
|
46
46
|
];
|
|
47
47
|
|
|
48
|
-
for (const relativePath of
|
|
48
|
+
for (const relativePath of removedFiles) {
|
|
49
49
|
const absolutePath = fileURLToPath(new URL(relativePath, import.meta.url));
|
|
50
50
|
assert.equal(existsSync(absolutePath), false, `${relativePath} must not exist.`);
|
|
51
51
|
}
|
package/test/index.test.js
CHANGED
|
@@ -10,12 +10,12 @@ import { authLoginPasswordCommand } from "@jskit-ai/auth-core/shared/commands/au
|
|
|
10
10
|
test("auth fastify adapter exports controller/routes backed by shared command validators", () => {
|
|
11
11
|
assert.equal(typeof AuthController, "function");
|
|
12
12
|
assert.equal(typeof buildRoutes, "function");
|
|
13
|
-
assert.ok(authLoginPasswordCommand.operation.
|
|
13
|
+
assert.ok(authLoginPasswordCommand.operation.body.schema);
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
-
test("auth-web
|
|
16
|
+
test("auth-web does not contain src/server/schema", () => {
|
|
17
17
|
const testFilePath = fileURLToPath(import.meta.url);
|
|
18
18
|
const packageRoot = path.resolve(path.dirname(testFilePath), "..");
|
|
19
|
-
const
|
|
20
|
-
assert.equal(existsSync(
|
|
19
|
+
const serverSchemaDirPath = path.join(packageRoot, "src", "server", "schema");
|
|
20
|
+
assert.equal(existsSync(serverSchemaDirPath), false, "src/server/schema must not exist.");
|
|
21
21
|
});
|
package/test/provider.test.js
CHANGED
|
@@ -56,7 +56,7 @@ function createAuthRuntimeStub(initialState = {}) {
|
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
function createAppDouble({ authGuardRuntime } = {}) {
|
|
59
|
+
function createAppDouble({ authGuardRuntime, bootstrapRuntime = null } = {}) {
|
|
60
60
|
const singletons = new Map();
|
|
61
61
|
const singletonInstances = new Map();
|
|
62
62
|
const provided = [];
|
|
@@ -88,6 +88,9 @@ function createAppDouble({ authGuardRuntime } = {}) {
|
|
|
88
88
|
if (token === "runtime.auth-guard.client") {
|
|
89
89
|
return true;
|
|
90
90
|
}
|
|
91
|
+
if (token === "runtime.web-bootstrap.client") {
|
|
92
|
+
return Boolean(bootstrapRuntime);
|
|
93
|
+
}
|
|
91
94
|
return singletons.has(token) || singletonInstances.has(token);
|
|
92
95
|
},
|
|
93
96
|
make(token) {
|
|
@@ -124,6 +127,9 @@ function createAppDouble({ authGuardRuntime } = {}) {
|
|
|
124
127
|
if (token === "runtime.auth-guard.client") {
|
|
125
128
|
return authGuardRuntime;
|
|
126
129
|
}
|
|
130
|
+
if (token === "runtime.web-bootstrap.client") {
|
|
131
|
+
return bootstrapRuntime;
|
|
132
|
+
}
|
|
127
133
|
if (singletonInstances.has(token)) {
|
|
128
134
|
return singletonInstances.get(token);
|
|
129
135
|
}
|
|
@@ -163,3 +169,30 @@ test("auth web client boot binds explicit Pinia store state and raw runtime inje
|
|
|
163
169
|
const providedByKey = new Map(app.provided.map((entry) => [entry.key, entry.value]));
|
|
164
170
|
assert.equal(providedByKey.get(AUTH_GUARD_RUNTIME_INJECTION_KEY), authGuardRuntime);
|
|
165
171
|
});
|
|
172
|
+
|
|
173
|
+
test("auth web client boot refreshes shared bootstrap runtime on auth changes", async () => {
|
|
174
|
+
const authGuardRuntime = createAuthRuntimeStub({
|
|
175
|
+
authenticated: false,
|
|
176
|
+
username: ""
|
|
177
|
+
});
|
|
178
|
+
const refreshCalls = [];
|
|
179
|
+
const app = createAppDouble({
|
|
180
|
+
authGuardRuntime,
|
|
181
|
+
bootstrapRuntime: {
|
|
182
|
+
async refresh(reason) {
|
|
183
|
+
refreshCalls.push(String(reason || ""));
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
await bootAuthClientProvider(app);
|
|
190
|
+
assert.deepEqual(refreshCalls, []);
|
|
191
|
+
|
|
192
|
+
authGuardRuntime.push({
|
|
193
|
+
authenticated: true,
|
|
194
|
+
username: "ada"
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
assert.deepEqual(refreshCalls, ["auth.state"]);
|
|
198
|
+
});
|
|
@@ -123,15 +123,12 @@ test("auth route provider registers routes and executes login/logout handlers",
|
|
|
123
123
|
assert.equal(events.some((entry) => entry.type === "clearSession"), true);
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
-
test("auth route provider registers dev login route only when auth
|
|
126
|
+
test("auth route provider registers dev login route only when dev auth bypass is enabled in env", async () => {
|
|
127
127
|
const fastify = createFastifyStub();
|
|
128
128
|
const app = createApplication();
|
|
129
129
|
const httpRuntime = createHttpRuntime({ app, fastify });
|
|
130
130
|
|
|
131
131
|
const authService = {
|
|
132
|
-
isDevAuthBootstrapEnabled() {
|
|
133
|
-
return true;
|
|
134
|
-
},
|
|
135
132
|
writeSessionCookies() {},
|
|
136
133
|
clearSessionCookies() {},
|
|
137
134
|
getOAuthProviderCatalog() {
|
|
@@ -139,6 +136,10 @@ test("auth route provider registers dev login route only when auth service enabl
|
|
|
139
136
|
}
|
|
140
137
|
};
|
|
141
138
|
|
|
139
|
+
app.instance("jskit.env", {
|
|
140
|
+
NODE_ENV: "development",
|
|
141
|
+
AUTH_DEV_BYPASS_ENABLED: "true"
|
|
142
|
+
});
|
|
142
143
|
app.instance("authService", authService);
|
|
143
144
|
app.instance("actionExecutor", {
|
|
144
145
|
async execute({ actionId }) {
|
|
@@ -169,3 +170,51 @@ test("auth route provider registers dev login route only when auth service enabl
|
|
|
169
170
|
assert.equal(devLoginReply.payload.userId, "7");
|
|
170
171
|
assert.equal(devLoginReply.payload.username, "Dev Ada");
|
|
171
172
|
});
|
|
173
|
+
|
|
174
|
+
test("auth route provider does not resolve authService during boot", async () => {
|
|
175
|
+
const fastify = createFastifyStub();
|
|
176
|
+
const app = createApplication();
|
|
177
|
+
const httpRuntime = createHttpRuntime({ app, fastify });
|
|
178
|
+
let authServiceResolutions = 0;
|
|
179
|
+
|
|
180
|
+
app.singleton("authService", () => {
|
|
181
|
+
authServiceResolutions += 1;
|
|
182
|
+
return {
|
|
183
|
+
writeSessionCookies() {},
|
|
184
|
+
clearSessionCookies() {},
|
|
185
|
+
getOAuthProviderCatalog() {
|
|
186
|
+
return { providers: [], defaultProvider: "" };
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
app.instance("actionExecutor", {
|
|
192
|
+
async execute({ actionId }) {
|
|
193
|
+
if (actionId === "auth.login.password") {
|
|
194
|
+
return {
|
|
195
|
+
session: { access_token: "a", refresh_token: "r" },
|
|
196
|
+
profile: { displayName: "Ada" }
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return {};
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
class MockAuthProvider {
|
|
204
|
+
static id = "auth.provider";
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
await app.start({ providers: [MockAuthProvider, AuthWebServiceProvider, AuthRouteServiceProvider] });
|
|
208
|
+
assert.equal(authServiceResolutions, 0);
|
|
209
|
+
|
|
210
|
+
const registration = httpRuntime.registerRoutes();
|
|
211
|
+
assert.equal(registration.routeCount > 0, true);
|
|
212
|
+
assert.equal(authServiceResolutions, 0);
|
|
213
|
+
|
|
214
|
+
const loginRoute = fastify.routes.find((route) => route.method === "POST" && route.url === "/api/login");
|
|
215
|
+
assert.ok(loginRoute);
|
|
216
|
+
const loginReply = createReplyStub();
|
|
217
|
+
await loginRoute.handler({ body: { email: "ada@example.com", password: "pass" } }, loginReply);
|
|
218
|
+
assert.equal(loginReply.statusCode, 200);
|
|
219
|
+
assert.equal(authServiceResolutions, 1);
|
|
220
|
+
});
|