@jskit-ai/auth-web 0.1.4
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 +290 -0
- package/package.json +29 -0
- package/src/client/composables/useDefaultLoginView.js +935 -0
- package/src/client/composables/useDefaultSignOutView.js +113 -0
- package/src/client/index.js +19 -0
- package/src/client/lib/returnToPath.js +20 -0
- package/src/client/lib/surfaceLinkTarget.js +19 -0
- package/src/client/providers/AuthWebClientProvider.js +72 -0
- package/src/client/runtime/authGuardRuntime.js +499 -0
- package/src/client/runtime/authHttpClient.js +19 -0
- package/src/client/runtime/inject.js +43 -0
- package/src/client/runtime/tokens.js +7 -0
- package/src/client/runtime/useLoginView.js +7 -0
- package/src/client/runtime/useSignOut.js +121 -0
- package/src/client/views/AuthProfileMenuLinkItem.vue +83 -0
- package/src/client/views/AuthProfileWidget.vue +100 -0
- package/src/client/views/DefaultLoginView.vue +291 -0
- package/src/client/views/DefaultSignOutView.vue +58 -0
- package/src/server/constants/authActionIds.js +15 -0
- package/src/server/controllers/AuthController.js +183 -0
- package/src/server/providers/AuthRouteServiceProvider.js +31 -0
- package/src/server/providers/AuthWebServiceProvider.js +23 -0
- package/src/server/routes/authRoutes.js +244 -0
- package/src/server/services/AuthWebService.js +126 -0
- package/templates/src/pages/auth/login.vue +17 -0
- package/templates/src/pages/auth/signout.vue +17 -0
- package/templates/src/runtime/authGuardRuntime.js +7 -0
- package/templates/src/runtime/authHttpClient.js +1 -0
- package/templates/src/runtime/useSignOut.js +1 -0
- package/templates/src/views/auth/LoginView.vue +7 -0
- package/templates/src/views/auth/SignOutView.vue +7 -0
- package/test/authGuardRuntime.test.js +361 -0
- package/test/clientBoot.test.js +16 -0
- package/test/clientSurface.test.js +89 -0
- package/test/index.test.js +21 -0
- package/test/logoutFallback.test.js +50 -0
- package/test/providerRuntime.test.js +100 -0
- package/test/returnToPath.test.js +72 -0
- package/test/surfaceLinkTarget.test.js +80 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { AUTH_ACTION_IDS } from "../constants/authActionIds.js";
|
|
2
|
+
import { AuthWebService } from "../services/AuthWebService.js";
|
|
3
|
+
|
|
4
|
+
class AuthController {
|
|
5
|
+
constructor({ service } = {}) {
|
|
6
|
+
if (!service) {
|
|
7
|
+
throw new Error("AuthController requires AuthWebService instance.");
|
|
8
|
+
}
|
|
9
|
+
this.service = service;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
getOAuthProviderCatalogPayload() {
|
|
13
|
+
return this.service.getOAuthProviderCatalogPayload();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async register(request, reply) {
|
|
17
|
+
const payload = request.body || {};
|
|
18
|
+
const result = await this.service.register(request, payload);
|
|
19
|
+
|
|
20
|
+
this.service.writeSessionCookies(reply, result.session);
|
|
21
|
+
|
|
22
|
+
if (result.requiresEmailConfirmation) {
|
|
23
|
+
reply.code(201).send({
|
|
24
|
+
ok: true,
|
|
25
|
+
requiresEmailConfirmation: true,
|
|
26
|
+
message: "Check your email to confirm the account before logging in."
|
|
27
|
+
});
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
reply.code(201).send({
|
|
32
|
+
ok: true,
|
|
33
|
+
username: result.profile.displayName,
|
|
34
|
+
requiresEmailConfirmation: false
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async login(request, reply) {
|
|
39
|
+
const payload = request.body || {};
|
|
40
|
+
const result = await this.service.login(request, payload);
|
|
41
|
+
|
|
42
|
+
this.service.writeSessionCookies(reply, result.session);
|
|
43
|
+
|
|
44
|
+
reply.code(200).send({
|
|
45
|
+
ok: true,
|
|
46
|
+
username: result.profile.displayName
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async requestOtpLogin(request, reply) {
|
|
51
|
+
const payload = request.body || {};
|
|
52
|
+
const result = await this.service.requestOtpLogin(request, payload);
|
|
53
|
+
reply.code(200).send(result);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async verifyOtpLogin(request, reply) {
|
|
57
|
+
const payload = request.body || {};
|
|
58
|
+
const result = await this.service.verifyOtpLogin(request, payload);
|
|
59
|
+
|
|
60
|
+
this.service.writeSessionCookies(reply, result.session);
|
|
61
|
+
reply.code(200).send({
|
|
62
|
+
ok: true,
|
|
63
|
+
username: result.profile.displayName,
|
|
64
|
+
email: result.profile.email
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async oauthStart(request, reply) {
|
|
69
|
+
const provider = request.params?.provider;
|
|
70
|
+
const returnTo = request.query?.returnTo;
|
|
71
|
+
const result = await this.service.oauthStart(request, { provider, returnTo });
|
|
72
|
+
reply.redirect(result.url);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async oauthComplete(request, reply) {
|
|
76
|
+
const payload = request.body || {};
|
|
77
|
+
const result = await this.service.oauthComplete(request, payload);
|
|
78
|
+
this.service.writeSessionCookies(reply, result.session);
|
|
79
|
+
|
|
80
|
+
reply.code(200).send({
|
|
81
|
+
ok: true,
|
|
82
|
+
provider: result.provider,
|
|
83
|
+
username: result.profile.displayName,
|
|
84
|
+
email: result.profile.email
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async logout(request, reply) {
|
|
89
|
+
let clearSession = true;
|
|
90
|
+
try {
|
|
91
|
+
const result = await this.service.logout(request);
|
|
92
|
+
clearSession = result?.clearSession !== false;
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (request?.log && typeof request.log.warn === "function") {
|
|
95
|
+
request.log.warn({ err: error }, "Auth logout fallback: clearing local session cookies.");
|
|
96
|
+
}
|
|
97
|
+
clearSession = true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (clearSession) {
|
|
101
|
+
this.service.clearSessionCookies(reply);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
reply.code(200).send({
|
|
105
|
+
ok: true
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async session(request, reply) {
|
|
110
|
+
const csrfToken = await reply.generateCsrf();
|
|
111
|
+
const oauthCatalogPayload = this.getOAuthProviderCatalogPayload();
|
|
112
|
+
const authResult = await this.service.session(request);
|
|
113
|
+
|
|
114
|
+
if (authResult.clearSession) {
|
|
115
|
+
this.service.clearSessionCookies(reply);
|
|
116
|
+
}
|
|
117
|
+
if (authResult.session) {
|
|
118
|
+
this.service.writeSessionCookies(reply, authResult.session);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (authResult.transientFailure) {
|
|
122
|
+
reply.code(503).send({
|
|
123
|
+
error: "Authentication service temporarily unavailable. Please retry.",
|
|
124
|
+
csrfToken,
|
|
125
|
+
...oauthCatalogPayload
|
|
126
|
+
});
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!authResult.authenticated) {
|
|
131
|
+
reply.code(200).send({
|
|
132
|
+
authenticated: false,
|
|
133
|
+
csrfToken,
|
|
134
|
+
...oauthCatalogPayload
|
|
135
|
+
});
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
reply.code(200).send({
|
|
140
|
+
authenticated: true,
|
|
141
|
+
username: authResult.profile.displayName,
|
|
142
|
+
csrfToken,
|
|
143
|
+
...oauthCatalogPayload
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async requestPasswordReset(request, reply) {
|
|
148
|
+
const payload = request.body || {};
|
|
149
|
+
const result = await this.service.requestPasswordReset(request, payload);
|
|
150
|
+
reply.code(200).send(result);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async completePasswordRecovery(request, reply) {
|
|
154
|
+
const payload = request.body || {};
|
|
155
|
+
const result = await this.service.completePasswordRecovery(request, payload);
|
|
156
|
+
this.service.writeSessionCookies(reply, result.session);
|
|
157
|
+
reply.code(200).send({
|
|
158
|
+
ok: true
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async resetPassword(request, reply) {
|
|
163
|
+
const payload = request.body || {};
|
|
164
|
+
await this.service.resetPassword(request, payload);
|
|
165
|
+
this.service.clearSessionCookies(reply);
|
|
166
|
+
reply.code(200).send({
|
|
167
|
+
ok: true,
|
|
168
|
+
message: "Password updated. Sign in with your new password."
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function createController({ service, authService } = {}) {
|
|
174
|
+
if (!service) {
|
|
175
|
+
if (!authService) {
|
|
176
|
+
throw new Error("createController requires either service or authService.");
|
|
177
|
+
}
|
|
178
|
+
service = new AuthWebService({ authService });
|
|
179
|
+
}
|
|
180
|
+
return new AuthController({ service });
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export { AuthController, AUTH_ACTION_IDS, createController };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { KERNEL_TOKENS } from "@jskit-ai/kernel/shared/support/tokens";
|
|
2
|
+
import { AuthController } from "../controllers/AuthController.js";
|
|
3
|
+
import { buildRoutes } from "../routes/authRoutes.js";
|
|
4
|
+
|
|
5
|
+
class AuthRouteServiceProvider {
|
|
6
|
+
static id = "auth.routes";
|
|
7
|
+
|
|
8
|
+
static dependsOn = ["auth.web"];
|
|
9
|
+
|
|
10
|
+
register(app) {
|
|
11
|
+
if (!app || typeof app.has !== "function") {
|
|
12
|
+
throw new Error("AuthRouteServiceProvider requires application has().");
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
boot(app) {
|
|
17
|
+
if (!app || typeof app.make !== "function") {
|
|
18
|
+
throw new Error("AuthRouteServiceProvider requires application make().");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const router = app.make(KERNEL_TOKENS.HttpRouter);
|
|
22
|
+
const authWebService = app.make("auth.web.service");
|
|
23
|
+
const controller = new AuthController({ service: authWebService });
|
|
24
|
+
const routes = buildRoutes(controller);
|
|
25
|
+
for (const route of routes) {
|
|
26
|
+
router.register(route.method, route.path, route, route.handler);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { AuthRouteServiceProvider };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { AuthWebService } from "../services/AuthWebService.js";
|
|
2
|
+
|
|
3
|
+
class AuthWebServiceProvider {
|
|
4
|
+
static id = "auth.web";
|
|
5
|
+
|
|
6
|
+
static dependsOn = ["auth.provider"];
|
|
7
|
+
|
|
8
|
+
register(app) {
|
|
9
|
+
if (!app || typeof app.singleton !== "function" || typeof app.has !== "function") {
|
|
10
|
+
throw new Error("AuthWebServiceProvider requires application singleton()/has().");
|
|
11
|
+
}
|
|
12
|
+
if (!app.has("actionExecutor")) {
|
|
13
|
+
throw new Error("AuthWebServiceProvider requires actionExecutor binding.");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
app.singleton("auth.web.service", (scope) => {
|
|
17
|
+
const authService = scope.make("authService");
|
|
18
|
+
return new AuthWebService({ authService });
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { AuthWebServiceProvider };
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { withStandardErrorResponses } from "@jskit-ai/http-runtime/shared/validators/errorResponses";
|
|
2
|
+
import { authRegisterCommand } from "@jskit-ai/auth-core/shared/commands/authRegisterCommand";
|
|
3
|
+
import { authLoginPasswordCommand } from "@jskit-ai/auth-core/shared/commands/authLoginPasswordCommand";
|
|
4
|
+
import { authLoginOtpRequestCommand } from "@jskit-ai/auth-core/shared/commands/authLoginOtpRequestCommand";
|
|
5
|
+
import { authLoginOtpVerifyCommand } from "@jskit-ai/auth-core/shared/commands/authLoginOtpVerifyCommand";
|
|
6
|
+
import { authLoginOAuthStartCommand } from "@jskit-ai/auth-core/shared/commands/authLoginOAuthStartCommand";
|
|
7
|
+
import { authLoginOAuthCompleteCommand } from "@jskit-ai/auth-core/shared/commands/authLoginOAuthCompleteCommand";
|
|
8
|
+
import { authPasswordResetRequestCommand } from "@jskit-ai/auth-core/shared/commands/authPasswordResetRequestCommand";
|
|
9
|
+
import { authPasswordRecoveryCompleteCommand } from "@jskit-ai/auth-core/shared/commands/authPasswordRecoveryCompleteCommand";
|
|
10
|
+
import { authPasswordResetCommand } from "@jskit-ai/auth-core/shared/commands/authPasswordResetCommand";
|
|
11
|
+
import { authLogoutCommand } from "@jskit-ai/auth-core/shared/commands/authLogoutCommand";
|
|
12
|
+
import { authSessionReadCommand } from "@jskit-ai/auth-core/shared/commands/authSessionReadCommand";
|
|
13
|
+
import { AUTH_PATHS } from "@jskit-ai/auth-core/shared/authPaths";
|
|
14
|
+
|
|
15
|
+
function buildRoutes(controller) {
|
|
16
|
+
if (!controller) {
|
|
17
|
+
throw new Error("Auth routes require a controller instance.");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const handler = (methodName) => controller[methodName].bind(controller);
|
|
21
|
+
|
|
22
|
+
return [
|
|
23
|
+
{
|
|
24
|
+
path: AUTH_PATHS.REGISTER,
|
|
25
|
+
method: "POST",
|
|
26
|
+
auth: "public",
|
|
27
|
+
meta: {
|
|
28
|
+
tags: ["auth"],
|
|
29
|
+
summary: "Register a new user"
|
|
30
|
+
},
|
|
31
|
+
bodyValidator: authRegisterCommand.operation.bodyValidator,
|
|
32
|
+
responseValidators: withStandardErrorResponses(
|
|
33
|
+
{
|
|
34
|
+
201: authRegisterCommand.operation.responseValidator
|
|
35
|
+
},
|
|
36
|
+
{ includeValidation400: true }
|
|
37
|
+
),
|
|
38
|
+
rateLimit: {
|
|
39
|
+
max: 10,
|
|
40
|
+
timeWindow: "1 minute"
|
|
41
|
+
},
|
|
42
|
+
handler: handler("register")
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
path: AUTH_PATHS.LOGIN,
|
|
46
|
+
method: "POST",
|
|
47
|
+
auth: "public",
|
|
48
|
+
meta: {
|
|
49
|
+
tags: ["auth"],
|
|
50
|
+
summary: "Log in with configured credentials"
|
|
51
|
+
},
|
|
52
|
+
bodyValidator: authLoginPasswordCommand.operation.bodyValidator,
|
|
53
|
+
responseValidators: withStandardErrorResponses(
|
|
54
|
+
{
|
|
55
|
+
200: authLoginPasswordCommand.operation.responseValidator
|
|
56
|
+
},
|
|
57
|
+
{ includeValidation400: true }
|
|
58
|
+
),
|
|
59
|
+
rateLimit: {
|
|
60
|
+
max: 10,
|
|
61
|
+
timeWindow: "1 minute"
|
|
62
|
+
},
|
|
63
|
+
handler: handler("login")
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
path: AUTH_PATHS.LOGIN_OTP_REQUEST,
|
|
67
|
+
method: "POST",
|
|
68
|
+
auth: "public",
|
|
69
|
+
meta: {
|
|
70
|
+
tags: ["auth"],
|
|
71
|
+
summary: "Request one-time email login code"
|
|
72
|
+
},
|
|
73
|
+
bodyValidator: authLoginOtpRequestCommand.operation.bodyValidator,
|
|
74
|
+
responseValidators: withStandardErrorResponses(
|
|
75
|
+
{
|
|
76
|
+
200: authLoginOtpRequestCommand.operation.responseValidator
|
|
77
|
+
},
|
|
78
|
+
{ includeValidation400: true }
|
|
79
|
+
),
|
|
80
|
+
rateLimit: {
|
|
81
|
+
max: 10,
|
|
82
|
+
timeWindow: "1 minute"
|
|
83
|
+
},
|
|
84
|
+
handler: handler("requestOtpLogin")
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
path: AUTH_PATHS.LOGIN_OTP_VERIFY,
|
|
88
|
+
method: "POST",
|
|
89
|
+
auth: "public",
|
|
90
|
+
meta: {
|
|
91
|
+
tags: ["auth"],
|
|
92
|
+
summary: "Verify one-time email login code and create session"
|
|
93
|
+
},
|
|
94
|
+
bodyValidator: authLoginOtpVerifyCommand.operation.bodyValidator,
|
|
95
|
+
responseValidators: withStandardErrorResponses(
|
|
96
|
+
{
|
|
97
|
+
200: authLoginOtpVerifyCommand.operation.responseValidator
|
|
98
|
+
},
|
|
99
|
+
{ includeValidation400: true }
|
|
100
|
+
),
|
|
101
|
+
rateLimit: {
|
|
102
|
+
max: 10,
|
|
103
|
+
timeWindow: "1 minute"
|
|
104
|
+
},
|
|
105
|
+
handler: handler("verifyOtpLogin")
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
path: AUTH_PATHS.OAUTH_START_TEMPLATE,
|
|
109
|
+
method: "GET",
|
|
110
|
+
auth: "public",
|
|
111
|
+
csrfProtection: false,
|
|
112
|
+
meta: {
|
|
113
|
+
tags: ["auth"],
|
|
114
|
+
summary: "Start OAuth login with configured provider"
|
|
115
|
+
},
|
|
116
|
+
paramsValidator: authLoginOAuthStartCommand.operation.paramsValidator,
|
|
117
|
+
queryValidator: authLoginOAuthStartCommand.operation.queryValidator,
|
|
118
|
+
responseValidators: withStandardErrorResponses(
|
|
119
|
+
{
|
|
120
|
+
302: authLoginOAuthStartCommand.operation.responseValidator
|
|
121
|
+
},
|
|
122
|
+
{ includeValidation400: true }
|
|
123
|
+
),
|
|
124
|
+
rateLimit: {
|
|
125
|
+
max: 20,
|
|
126
|
+
timeWindow: "1 minute"
|
|
127
|
+
},
|
|
128
|
+
handler: handler("oauthStart")
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
path: AUTH_PATHS.OAUTH_COMPLETE,
|
|
132
|
+
method: "POST",
|
|
133
|
+
auth: "public",
|
|
134
|
+
meta: {
|
|
135
|
+
tags: ["auth"],
|
|
136
|
+
summary: "Complete OAuth code exchange and set session cookies"
|
|
137
|
+
},
|
|
138
|
+
bodyValidator: authLoginOAuthCompleteCommand.operation.bodyValidator,
|
|
139
|
+
responseValidators: withStandardErrorResponses(
|
|
140
|
+
{
|
|
141
|
+
200: authLoginOAuthCompleteCommand.operation.responseValidator
|
|
142
|
+
},
|
|
143
|
+
{ includeValidation400: true }
|
|
144
|
+
),
|
|
145
|
+
rateLimit: {
|
|
146
|
+
max: 20,
|
|
147
|
+
timeWindow: "1 minute"
|
|
148
|
+
},
|
|
149
|
+
handler: handler("oauthComplete")
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
path: AUTH_PATHS.PASSWORD_FORGOT,
|
|
153
|
+
method: "POST",
|
|
154
|
+
auth: "public",
|
|
155
|
+
meta: {
|
|
156
|
+
tags: ["auth"],
|
|
157
|
+
summary: "Request a password reset email"
|
|
158
|
+
},
|
|
159
|
+
bodyValidator: authPasswordResetRequestCommand.operation.bodyValidator,
|
|
160
|
+
responseValidators: withStandardErrorResponses(
|
|
161
|
+
{
|
|
162
|
+
200: authPasswordResetRequestCommand.operation.responseValidator
|
|
163
|
+
},
|
|
164
|
+
{ includeValidation400: true }
|
|
165
|
+
),
|
|
166
|
+
rateLimit: {
|
|
167
|
+
max: 5,
|
|
168
|
+
timeWindow: "1 minute"
|
|
169
|
+
},
|
|
170
|
+
handler: handler("requestPasswordReset")
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
path: AUTH_PATHS.PASSWORD_RECOVERY,
|
|
174
|
+
method: "POST",
|
|
175
|
+
auth: "public",
|
|
176
|
+
meta: {
|
|
177
|
+
tags: ["auth"],
|
|
178
|
+
summary: "Complete password recovery link exchange"
|
|
179
|
+
},
|
|
180
|
+
bodyValidator: authPasswordRecoveryCompleteCommand.operation.bodyValidator,
|
|
181
|
+
responseValidators: withStandardErrorResponses(
|
|
182
|
+
{
|
|
183
|
+
200: authPasswordRecoveryCompleteCommand.operation.responseValidator
|
|
184
|
+
},
|
|
185
|
+
{ includeValidation400: true }
|
|
186
|
+
),
|
|
187
|
+
rateLimit: {
|
|
188
|
+
max: 20,
|
|
189
|
+
timeWindow: "1 minute"
|
|
190
|
+
},
|
|
191
|
+
handler: handler("completePasswordRecovery")
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
path: AUTH_PATHS.PASSWORD_RESET,
|
|
195
|
+
method: "POST",
|
|
196
|
+
auth: "required",
|
|
197
|
+
meta: {
|
|
198
|
+
tags: ["auth"],
|
|
199
|
+
summary: "Set a new password for authenticated recovery session"
|
|
200
|
+
},
|
|
201
|
+
bodyValidator: authPasswordResetCommand.operation.bodyValidator,
|
|
202
|
+
responseValidators: withStandardErrorResponses(
|
|
203
|
+
{
|
|
204
|
+
200: authPasswordResetCommand.operation.responseValidator
|
|
205
|
+
},
|
|
206
|
+
{ includeValidation400: true }
|
|
207
|
+
),
|
|
208
|
+
rateLimit: {
|
|
209
|
+
max: 20,
|
|
210
|
+
timeWindow: "1 minute"
|
|
211
|
+
},
|
|
212
|
+
handler: handler("resetPassword")
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
path: AUTH_PATHS.LOGOUT,
|
|
216
|
+
method: "POST",
|
|
217
|
+
auth: "public",
|
|
218
|
+
meta: {
|
|
219
|
+
tags: ["auth"],
|
|
220
|
+
summary: "Log out and clear session cookies"
|
|
221
|
+
},
|
|
222
|
+
responseValidators: withStandardErrorResponses({
|
|
223
|
+
200: authLogoutCommand.operation.responseValidator
|
|
224
|
+
}),
|
|
225
|
+
handler: handler("logout")
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
path: AUTH_PATHS.SESSION,
|
|
229
|
+
method: "GET",
|
|
230
|
+
auth: "public",
|
|
231
|
+
meta: {
|
|
232
|
+
tags: ["auth"],
|
|
233
|
+
summary: "Get current session status and CSRF token"
|
|
234
|
+
},
|
|
235
|
+
responseValidators: withStandardErrorResponses({
|
|
236
|
+
200: authSessionReadCommand.operation.responseValidator,
|
|
237
|
+
503: authSessionReadCommand.operation.unavailableResponseValidator
|
|
238
|
+
}),
|
|
239
|
+
handler: handler("session")
|
|
240
|
+
}
|
|
241
|
+
];
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export { buildRoutes };
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { AUTH_ACTION_IDS } from "../constants/authActionIds.js";
|
|
2
|
+
|
|
3
|
+
class AuthWebService {
|
|
4
|
+
constructor({ authService } = {}) {
|
|
5
|
+
if (!authService) {
|
|
6
|
+
throw new Error("authService is required.");
|
|
7
|
+
}
|
|
8
|
+
this.authService = authService;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static get actionIds() {
|
|
12
|
+
return AUTH_ACTION_IDS;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async register(request, payload) {
|
|
16
|
+
return request.executeAction({
|
|
17
|
+
actionId: AUTH_ACTION_IDS.REGISTER,
|
|
18
|
+
input: payload
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async login(request, payload) {
|
|
23
|
+
return request.executeAction({
|
|
24
|
+
actionId: AUTH_ACTION_IDS.LOGIN_PASSWORD,
|
|
25
|
+
input: payload
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async requestOtpLogin(request, payload) {
|
|
30
|
+
return request.executeAction({
|
|
31
|
+
actionId: AUTH_ACTION_IDS.LOGIN_OTP_REQUEST,
|
|
32
|
+
input: payload
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async verifyOtpLogin(request, payload) {
|
|
37
|
+
return request.executeAction({
|
|
38
|
+
actionId: AUTH_ACTION_IDS.LOGIN_OTP_VERIFY,
|
|
39
|
+
input: payload
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async oauthStart(request, input) {
|
|
44
|
+
return request.executeAction({
|
|
45
|
+
actionId: AUTH_ACTION_IDS.LOGIN_OAUTH_START,
|
|
46
|
+
input
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async oauthComplete(request, payload) {
|
|
51
|
+
return request.executeAction({
|
|
52
|
+
actionId: AUTH_ACTION_IDS.LOGIN_OAUTH_COMPLETE,
|
|
53
|
+
input: payload
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async logout(request) {
|
|
58
|
+
return request.executeAction({
|
|
59
|
+
actionId: AUTH_ACTION_IDS.LOGOUT
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async session(request) {
|
|
64
|
+
return request.executeAction({
|
|
65
|
+
actionId: AUTH_ACTION_IDS.SESSION_READ
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async requestPasswordReset(request, payload) {
|
|
70
|
+
return request.executeAction({
|
|
71
|
+
actionId: AUTH_ACTION_IDS.PASSWORD_RESET_REQUEST,
|
|
72
|
+
input: payload
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async completePasswordRecovery(request, payload) {
|
|
77
|
+
return request.executeAction({
|
|
78
|
+
actionId: AUTH_ACTION_IDS.PASSWORD_RECOVERY_COMPLETE,
|
|
79
|
+
input: payload
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async resetPassword(request, payload) {
|
|
84
|
+
return request.executeAction({
|
|
85
|
+
actionId: AUTH_ACTION_IDS.PASSWORD_RESET,
|
|
86
|
+
input: payload
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
writeSessionCookies(reply, session) {
|
|
91
|
+
if (session && reply) {
|
|
92
|
+
this.authService.writeSessionCookies(reply, session);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
clearSessionCookies(reply) {
|
|
97
|
+
if (reply) {
|
|
98
|
+
this.authService.clearSessionCookies(reply);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
getOAuthProviderCatalogPayload() {
|
|
103
|
+
const catalog =
|
|
104
|
+
typeof this.authService.getOAuthProviderCatalog === "function"
|
|
105
|
+
? this.authService.getOAuthProviderCatalog()
|
|
106
|
+
: null;
|
|
107
|
+
const providers = Array.isArray(catalog?.providers)
|
|
108
|
+
? catalog.providers
|
|
109
|
+
.map((provider) => ({
|
|
110
|
+
id: String(provider?.id || "").trim().toLowerCase(),
|
|
111
|
+
label: String(provider?.label || "").trim()
|
|
112
|
+
}))
|
|
113
|
+
.filter((provider) => provider.id && provider.label)
|
|
114
|
+
: [];
|
|
115
|
+
const defaultProvider = String(catalog?.defaultProvider || "").trim().toLowerCase();
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
oauthProviders: providers,
|
|
119
|
+
oauthDefaultProvider: providers.some((provider) => provider.id === defaultProvider)
|
|
120
|
+
? defaultProvider
|
|
121
|
+
: null
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export { AuthWebService };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<route lang="json">
|
|
2
|
+
{
|
|
3
|
+
"meta": {
|
|
4
|
+
"guard": {
|
|
5
|
+
"policy": "public"
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
</route>
|
|
10
|
+
|
|
11
|
+
<script setup>
|
|
12
|
+
import DefaultLoginView from "@jskit-ai/auth-web/client/views/DefaultLoginView";
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<DefaultLoginView />
|
|
17
|
+
</template>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<route lang="json">
|
|
2
|
+
{
|
|
3
|
+
"meta": {
|
|
4
|
+
"guard": {
|
|
5
|
+
"policy": "public"
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
</route>
|
|
10
|
+
|
|
11
|
+
<script setup>
|
|
12
|
+
import DefaultSignOutView from "@jskit-ai/auth-web/client/views/DefaultSignOutView";
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<DefaultSignOutView />
|
|
17
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { authHttpRequest, clearAuthCsrfTokenCache } from "@jskit-ai/auth-web/client/runtime/authHttpClient";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useSignOut, createSignOutAction, performSignOutRequest } from "@jskit-ai/auth-web/client/runtime/useSignOut";
|