@adonisjs/auth 10.0.0-next.2 → 10.0.0-next.3
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/build/auth_manager-BMz_OJXq.js +129 -0
- package/build/debug-Ckko95-M.js +3 -0
- package/build/errors-BQxhZmkE.js +128 -0
- package/build/index.js +59 -100
- package/build/modules/access_tokens_guard/main.js +647 -1309
- package/build/modules/access_tokens_guard/types.js +1 -0
- package/build/modules/basic_auth_guard/main.js +106 -236
- package/build/modules/basic_auth_guard/types.js +1 -0
- package/build/modules/session_guard/main.js +377 -966
- package/build/modules/session_guard/types.js +1 -0
- package/build/providers/auth_provider.js +16 -40
- package/build/services/auth.js +3 -8
- package/build/src/debug.d.ts +1 -1
- package/build/src/middleware/initialize_auth_middleware.js +6 -24
- package/build/src/mixins/lucid.js +43 -95
- package/build/src/plugins/japa/api_client.js +43 -56
- package/build/src/plugins/japa/browser_client.js +45 -64
- package/build/src/types.js +1 -0
- package/build/symbols-Ct253Khf.js +8 -0
- package/package.json +40 -37
- package/build/chunk-2VRS2VHB.js +0 -7
- package/build/chunk-MSPAYMZE.js +0 -351
- package/build/chunk-S5G5RTJX.js +0 -236
- package/build/chunk-UXA4FHST.js +0 -19
|
@@ -1,978 +1,389 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
} from "../../chunk-S5G5RTJX.js";
|
|
4
|
-
import "../../chunk-UXA4FHST.js";
|
|
5
|
-
|
|
6
|
-
// modules/session_guard/guard.ts
|
|
7
|
-
import { Secret } from "@adonisjs/core/helpers";
|
|
1
|
+
import { n as E_UNAUTHORIZED_ACCESS } from "../../errors-BQxhZmkE.js";
|
|
2
|
+
import "../../symbols-Ct253Khf.js";
|
|
8
3
|
import { RuntimeException } from "@adonisjs/core/exceptions";
|
|
4
|
+
import { inspect } from "node:util";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import string from "@adonisjs/core/helpers/string";
|
|
7
|
+
import { Secret, base64, safeEqual } from "@adonisjs/core/helpers";
|
|
9
8
|
var SessionGuard = class {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
#createSessionForUser(userId) {
|
|
184
|
-
const session = this.#getSession();
|
|
185
|
-
session.put(this.sessionKeyName, userId);
|
|
186
|
-
session.regenerate();
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Creates the remember me cookie
|
|
190
|
-
*/
|
|
191
|
-
#createRememberMeCookie(value) {
|
|
192
|
-
this.#ctx.response.encryptedCookie(this.rememberMeKeyName, value.release(), {
|
|
193
|
-
maxAge: this.#options.rememberMeTokensAge,
|
|
194
|
-
httpOnly: true
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Authenticates the user using its id read from the session
|
|
199
|
-
* store.
|
|
200
|
-
*
|
|
201
|
-
* - We check the user exists in the db
|
|
202
|
-
* - If not, throw exception.
|
|
203
|
-
* - Otherwise, update local state to mark the user as logged-in
|
|
204
|
-
*/
|
|
205
|
-
async #authenticateViaId(userId, sessionId) {
|
|
206
|
-
const providerUser = await this.#userProvider.findById(userId);
|
|
207
|
-
if (!providerUser) {
|
|
208
|
-
throw this.#authenticationFailed(sessionId);
|
|
209
|
-
}
|
|
210
|
-
this.#authenticationSucceeded(sessionId, providerUser.getOriginal());
|
|
211
|
-
return this.user;
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* Authenticates user from the remember me cookie. Creates a fresh
|
|
215
|
-
* session for them and recycles the remember me token as well.
|
|
216
|
-
*/
|
|
217
|
-
async #authenticateViaRememberCookie(rememberMeCookie, sessionId) {
|
|
218
|
-
const userProvider = this.#userProvider;
|
|
219
|
-
const token = await userProvider.verifyRememberToken(new Secret(rememberMeCookie));
|
|
220
|
-
if (!token) {
|
|
221
|
-
throw this.#authenticationFailed(sessionId);
|
|
222
|
-
}
|
|
223
|
-
const providerUser = await userProvider.findById(token.tokenableId);
|
|
224
|
-
if (!providerUser) {
|
|
225
|
-
throw this.#authenticationFailed(sessionId);
|
|
226
|
-
}
|
|
227
|
-
const recycledToken = await userProvider.recycleRememberToken(
|
|
228
|
-
providerUser.getOriginal(),
|
|
229
|
-
token.identifier,
|
|
230
|
-
this.#options.rememberMeTokensAge
|
|
231
|
-
);
|
|
232
|
-
this.#createRememberMeCookie(recycledToken.value);
|
|
233
|
-
this.#createSessionForUser(providerUser.getId());
|
|
234
|
-
this.#authenticationSucceeded(sessionId, providerUser.getOriginal(), token);
|
|
235
|
-
return this.user;
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* Returns an instance of the authenticated user. Or throws
|
|
239
|
-
* an exception if the request is not authenticated.
|
|
240
|
-
*/
|
|
241
|
-
getUserOrFail() {
|
|
242
|
-
if (!this.user) {
|
|
243
|
-
throw new E_UNAUTHORIZED_ACCESS("Invalid or expired user session", {
|
|
244
|
-
guardDriverName: this.driverName
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
return this.user;
|
|
248
|
-
}
|
|
249
|
-
/**
|
|
250
|
-
* Login user using sessions. Optionally, you can also create
|
|
251
|
-
* a remember me token to automatically login user when their
|
|
252
|
-
* session expires.
|
|
253
|
-
*/
|
|
254
|
-
async login(user, remember = false) {
|
|
255
|
-
const session = this.#getSession();
|
|
256
|
-
const providerUser = await this.#userProvider.createUserForGuard(user);
|
|
257
|
-
this.#emitter.emit("session_auth:login_attempted", {
|
|
258
|
-
ctx: this.#ctx,
|
|
259
|
-
user,
|
|
260
|
-
guardName: this.#name
|
|
261
|
-
});
|
|
262
|
-
let token;
|
|
263
|
-
if (remember) {
|
|
264
|
-
if (!this.#options.useRememberMeTokens) {
|
|
265
|
-
throw new RuntimeException('Cannot use "rememberMe" feature. It has been disabled');
|
|
266
|
-
}
|
|
267
|
-
const userProvider = this.#userProvider;
|
|
268
|
-
token = await userProvider.createRememberToken(
|
|
269
|
-
providerUser.getOriginal(),
|
|
270
|
-
this.#options.rememberMeTokensAge
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
if (token) {
|
|
274
|
-
this.#createRememberMeCookie(token.value);
|
|
275
|
-
} else {
|
|
276
|
-
this.#ctx.response.clearCookie(this.rememberMeKeyName);
|
|
277
|
-
}
|
|
278
|
-
this.#createSessionForUser(providerUser.getId());
|
|
279
|
-
this.#loginSucceeded(session.sessionId, providerUser.getOriginal(), token);
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Logout a user by removing its state from the session
|
|
283
|
-
* store and delete the remember me cookie (if any).
|
|
284
|
-
*/
|
|
285
|
-
async logout() {
|
|
286
|
-
const session = this.#getSession();
|
|
287
|
-
const rememberMeCookie = this.#ctx.request.encryptedCookie(this.rememberMeKeyName);
|
|
288
|
-
session.forget(this.sessionKeyName);
|
|
289
|
-
this.#ctx.response.clearCookie(this.rememberMeKeyName);
|
|
290
|
-
if (this.user && rememberMeCookie && this.#options.useRememberMeTokens) {
|
|
291
|
-
const userProvider = this.#userProvider;
|
|
292
|
-
const token = await userProvider.verifyRememberToken(new Secret(rememberMeCookie));
|
|
293
|
-
if (token) {
|
|
294
|
-
await userProvider.deleteRemeberToken(this.user, token.identifier);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
this.#emitter.emit("session_auth:logged_out", {
|
|
298
|
-
ctx: this.#ctx,
|
|
299
|
-
guardName: this.#name,
|
|
300
|
-
user: this.user || null,
|
|
301
|
-
sessionId: session.sessionId
|
|
302
|
-
});
|
|
303
|
-
this.user = void 0;
|
|
304
|
-
this.viaRemember = false;
|
|
305
|
-
this.isAuthenticated = false;
|
|
306
|
-
this.isLoggedOut = true;
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* Authenticate the current HTTP request by verifying the bearer
|
|
310
|
-
* token or fails with an exception
|
|
311
|
-
*/
|
|
312
|
-
async authenticate() {
|
|
313
|
-
if (this.authenticationAttempted) {
|
|
314
|
-
return this.getUserOrFail();
|
|
315
|
-
}
|
|
316
|
-
this.authenticationAttempted = true;
|
|
317
|
-
const session = this.#getSession();
|
|
318
|
-
this.#emitter.emit("session_auth:authentication_attempted", {
|
|
319
|
-
ctx: this.#ctx,
|
|
320
|
-
sessionId: session.sessionId,
|
|
321
|
-
guardName: this.#name
|
|
322
|
-
});
|
|
323
|
-
const authUserId = session.get(this.sessionKeyName);
|
|
324
|
-
if (authUserId) {
|
|
325
|
-
return this.#authenticateViaId(authUserId, session.sessionId);
|
|
326
|
-
}
|
|
327
|
-
const rememberMeCookie = this.#ctx.request.encryptedCookie(this.rememberMeKeyName);
|
|
328
|
-
if (rememberMeCookie && this.#options.useRememberMeTokens) {
|
|
329
|
-
this.attemptedViaRemember = true;
|
|
330
|
-
return this.#authenticateViaRememberCookie(rememberMeCookie, session.sessionId);
|
|
331
|
-
}
|
|
332
|
-
throw this.#authenticationFailed(session.sessionId);
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* Silently check if the user is authenticated or not, without
|
|
336
|
-
* throwing any exceptions
|
|
337
|
-
*/
|
|
338
|
-
async check() {
|
|
339
|
-
try {
|
|
340
|
-
await this.authenticate();
|
|
341
|
-
return true;
|
|
342
|
-
} catch (error) {
|
|
343
|
-
if (error instanceof E_UNAUTHORIZED_ACCESS) {
|
|
344
|
-
return false;
|
|
345
|
-
}
|
|
346
|
-
throw error;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
/**
|
|
350
|
-
* Returns the session info for the clients to send during
|
|
351
|
-
* an HTTP request to mark the user as logged-in.
|
|
352
|
-
*/
|
|
353
|
-
async authenticateAsClient(user) {
|
|
354
|
-
const providerUser = await this.#userProvider.createUserForGuard(user);
|
|
355
|
-
const userId = providerUser.getId();
|
|
356
|
-
return {
|
|
357
|
-
session: {
|
|
358
|
-
[this.sessionKeyName]: userId
|
|
359
|
-
}
|
|
360
|
-
};
|
|
361
|
-
}
|
|
9
|
+
#name;
|
|
10
|
+
#ctx;
|
|
11
|
+
#options;
|
|
12
|
+
#userProvider;
|
|
13
|
+
#emitter;
|
|
14
|
+
driverName = "session";
|
|
15
|
+
authenticationAttempted = false;
|
|
16
|
+
attemptedViaRemember = false;
|
|
17
|
+
isAuthenticated = false;
|
|
18
|
+
viaRemember = false;
|
|
19
|
+
isLoggedOut = false;
|
|
20
|
+
user;
|
|
21
|
+
get sessionKeyName() {
|
|
22
|
+
return `auth_${this.#name}`;
|
|
23
|
+
}
|
|
24
|
+
get rememberMeKeyName() {
|
|
25
|
+
return `remember_${this.#name}`;
|
|
26
|
+
}
|
|
27
|
+
constructor(name, ctx, options, emitter, userProvider) {
|
|
28
|
+
this.#name = name;
|
|
29
|
+
this.#ctx = ctx;
|
|
30
|
+
this.#options = {
|
|
31
|
+
rememberMeTokensAge: "2 years",
|
|
32
|
+
...options
|
|
33
|
+
};
|
|
34
|
+
this.#emitter = emitter;
|
|
35
|
+
this.#userProvider = userProvider;
|
|
36
|
+
}
|
|
37
|
+
#getSession() {
|
|
38
|
+
if (!("session" in this.#ctx)) throw new RuntimeException("Cannot authenticate user. Install and configure \"@adonisjs/session\" package");
|
|
39
|
+
return this.#ctx.session;
|
|
40
|
+
}
|
|
41
|
+
#authenticationFailed(sessionId) {
|
|
42
|
+
this.isAuthenticated = false;
|
|
43
|
+
this.viaRemember = false;
|
|
44
|
+
this.user = void 0;
|
|
45
|
+
this.isLoggedOut = false;
|
|
46
|
+
const error = new E_UNAUTHORIZED_ACCESS("Invalid or expired user session", { guardDriverName: this.driverName });
|
|
47
|
+
this.#emitter.emit("session_auth:authentication_failed", {
|
|
48
|
+
ctx: this.#ctx,
|
|
49
|
+
guardName: this.#name,
|
|
50
|
+
error,
|
|
51
|
+
sessionId
|
|
52
|
+
});
|
|
53
|
+
return error;
|
|
54
|
+
}
|
|
55
|
+
#authenticationSucceeded(sessionId, user, rememberMeToken) {
|
|
56
|
+
this.isAuthenticated = true;
|
|
57
|
+
this.viaRemember = !!rememberMeToken;
|
|
58
|
+
this.user = user;
|
|
59
|
+
this.isLoggedOut = false;
|
|
60
|
+
this.#emitter.emit("session_auth:authentication_succeeded", {
|
|
61
|
+
ctx: this.#ctx,
|
|
62
|
+
guardName: this.#name,
|
|
63
|
+
sessionId,
|
|
64
|
+
user,
|
|
65
|
+
rememberMeToken
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
#loginSucceeded(sessionId, user, rememberMeToken) {
|
|
69
|
+
this.user = user;
|
|
70
|
+
this.isLoggedOut = false;
|
|
71
|
+
this.#emitter.emit("session_auth:login_succeeded", {
|
|
72
|
+
ctx: this.#ctx,
|
|
73
|
+
guardName: this.#name,
|
|
74
|
+
sessionId,
|
|
75
|
+
user,
|
|
76
|
+
rememberMeToken
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
#createSessionForUser(userId) {
|
|
80
|
+
const session = this.#getSession();
|
|
81
|
+
session.put(this.sessionKeyName, userId);
|
|
82
|
+
session.regenerate();
|
|
83
|
+
}
|
|
84
|
+
#createRememberMeCookie(value) {
|
|
85
|
+
this.#ctx.response.encryptedCookie(this.rememberMeKeyName, value.release(), {
|
|
86
|
+
maxAge: this.#options.rememberMeTokensAge,
|
|
87
|
+
httpOnly: true
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
async #authenticateViaId(userId, sessionId) {
|
|
91
|
+
const providerUser = await this.#userProvider.findById(userId);
|
|
92
|
+
if (!providerUser) throw this.#authenticationFailed(sessionId);
|
|
93
|
+
this.#authenticationSucceeded(sessionId, providerUser.getOriginal());
|
|
94
|
+
return this.user;
|
|
95
|
+
}
|
|
96
|
+
async #authenticateViaRememberCookie(rememberMeCookie, sessionId) {
|
|
97
|
+
const userProvider = this.#userProvider;
|
|
98
|
+
const token = await userProvider.verifyRememberToken(new Secret(rememberMeCookie));
|
|
99
|
+
if (!token) throw this.#authenticationFailed(sessionId);
|
|
100
|
+
const providerUser = await userProvider.findById(token.tokenableId);
|
|
101
|
+
if (!providerUser) throw this.#authenticationFailed(sessionId);
|
|
102
|
+
const recycledToken = await userProvider.recycleRememberToken(providerUser.getOriginal(), token.identifier, this.#options.rememberMeTokensAge);
|
|
103
|
+
this.#createRememberMeCookie(recycledToken.value);
|
|
104
|
+
this.#createSessionForUser(providerUser.getId());
|
|
105
|
+
this.#authenticationSucceeded(sessionId, providerUser.getOriginal(), token);
|
|
106
|
+
return this.user;
|
|
107
|
+
}
|
|
108
|
+
getUserOrFail() {
|
|
109
|
+
if (!this.user) throw new E_UNAUTHORIZED_ACCESS("Invalid or expired user session", { guardDriverName: this.driverName });
|
|
110
|
+
return this.user;
|
|
111
|
+
}
|
|
112
|
+
async login(user, remember = false) {
|
|
113
|
+
const session = this.#getSession();
|
|
114
|
+
const providerUser = await this.#userProvider.createUserForGuard(user);
|
|
115
|
+
this.#emitter.emit("session_auth:login_attempted", {
|
|
116
|
+
ctx: this.#ctx,
|
|
117
|
+
user,
|
|
118
|
+
guardName: this.#name
|
|
119
|
+
});
|
|
120
|
+
let token;
|
|
121
|
+
if (remember) {
|
|
122
|
+
if (!this.#options.useRememberMeTokens) throw new RuntimeException("Cannot use \"rememberMe\" feature. It has been disabled");
|
|
123
|
+
token = await this.#userProvider.createRememberToken(providerUser.getOriginal(), this.#options.rememberMeTokensAge);
|
|
124
|
+
}
|
|
125
|
+
if (token) this.#createRememberMeCookie(token.value);
|
|
126
|
+
else this.#ctx.response.clearCookie(this.rememberMeKeyName);
|
|
127
|
+
this.#createSessionForUser(providerUser.getId());
|
|
128
|
+
this.#loginSucceeded(session.sessionId, providerUser.getOriginal(), token);
|
|
129
|
+
}
|
|
130
|
+
async logout() {
|
|
131
|
+
const session = this.#getSession();
|
|
132
|
+
const rememberMeCookie = this.#ctx.request.encryptedCookie(this.rememberMeKeyName);
|
|
133
|
+
session.forget(this.sessionKeyName);
|
|
134
|
+
this.#ctx.response.clearCookie(this.rememberMeKeyName);
|
|
135
|
+
if (this.user && rememberMeCookie && this.#options.useRememberMeTokens) {
|
|
136
|
+
const userProvider = this.#userProvider;
|
|
137
|
+
const token = await userProvider.verifyRememberToken(new Secret(rememberMeCookie));
|
|
138
|
+
if (token) await userProvider.deleteRemeberToken(this.user, token.identifier);
|
|
139
|
+
}
|
|
140
|
+
this.#emitter.emit("session_auth:logged_out", {
|
|
141
|
+
ctx: this.#ctx,
|
|
142
|
+
guardName: this.#name,
|
|
143
|
+
user: this.user || null,
|
|
144
|
+
sessionId: session.sessionId
|
|
145
|
+
});
|
|
146
|
+
this.user = void 0;
|
|
147
|
+
this.viaRemember = false;
|
|
148
|
+
this.isAuthenticated = false;
|
|
149
|
+
this.isLoggedOut = true;
|
|
150
|
+
}
|
|
151
|
+
async authenticate() {
|
|
152
|
+
if (this.authenticationAttempted) return this.getUserOrFail();
|
|
153
|
+
this.authenticationAttempted = true;
|
|
154
|
+
const session = this.#getSession();
|
|
155
|
+
this.#emitter.emit("session_auth:authentication_attempted", {
|
|
156
|
+
ctx: this.#ctx,
|
|
157
|
+
sessionId: session.sessionId,
|
|
158
|
+
guardName: this.#name
|
|
159
|
+
});
|
|
160
|
+
const authUserId = session.get(this.sessionKeyName);
|
|
161
|
+
if (authUserId) return this.#authenticateViaId(authUserId, session.sessionId);
|
|
162
|
+
const rememberMeCookie = this.#ctx.request.encryptedCookie(this.rememberMeKeyName);
|
|
163
|
+
if (rememberMeCookie && this.#options.useRememberMeTokens) {
|
|
164
|
+
this.attemptedViaRemember = true;
|
|
165
|
+
return this.#authenticateViaRememberCookie(rememberMeCookie, session.sessionId);
|
|
166
|
+
}
|
|
167
|
+
throw this.#authenticationFailed(session.sessionId);
|
|
168
|
+
}
|
|
169
|
+
async check() {
|
|
170
|
+
try {
|
|
171
|
+
await this.authenticate();
|
|
172
|
+
return true;
|
|
173
|
+
} catch (error) {
|
|
174
|
+
if (error instanceof E_UNAUTHORIZED_ACCESS) return false;
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async authenticateAsClient(user) {
|
|
179
|
+
const userId = (await this.#userProvider.createUserForGuard(user)).getId();
|
|
180
|
+
return { session: { [this.sessionKeyName]: userId } };
|
|
181
|
+
}
|
|
362
182
|
};
|
|
363
|
-
|
|
364
|
-
// modules/session_guard/remember_me_token.ts
|
|
365
|
-
import { createHash } from "crypto";
|
|
366
|
-
import string from "@adonisjs/core/helpers/string";
|
|
367
|
-
import { Secret as Secret2, base64, safeEqual } from "@adonisjs/core/helpers";
|
|
368
183
|
var RememberMeToken = class {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
return {
|
|
422
|
-
userId,
|
|
423
|
-
expiresAt,
|
|
424
|
-
...this.seed(size)
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
/**
|
|
428
|
-
* Creates a secret opaque token and its hash.
|
|
429
|
-
*
|
|
430
|
-
* @param size - The size of the random string to generate
|
|
431
|
-
*
|
|
432
|
-
* @example
|
|
433
|
-
* const { secret, hash } = RememberMeToken.seed(32)
|
|
434
|
-
* console.log('Secret:', secret.release())
|
|
435
|
-
* console.log('Hash:', hash)
|
|
436
|
-
*/
|
|
437
|
-
static seed(size) {
|
|
438
|
-
const seed = string.random(size);
|
|
439
|
-
const secret = new Secret2(seed);
|
|
440
|
-
const hash = createHash("sha256").update(secret.release()).digest("hex");
|
|
441
|
-
return { secret, hash };
|
|
442
|
-
}
|
|
443
|
-
/**
|
|
444
|
-
* Identifer is a unique sequence to identify the
|
|
445
|
-
* token within database. It should be the
|
|
446
|
-
* primary/unique key
|
|
447
|
-
*/
|
|
448
|
-
identifier;
|
|
449
|
-
/**
|
|
450
|
-
* Reference to the user id for whom the token
|
|
451
|
-
* is generated.
|
|
452
|
-
*/
|
|
453
|
-
tokenableId;
|
|
454
|
-
/**
|
|
455
|
-
* The value is a public representation of a token. It is created
|
|
456
|
-
* by combining the "identifier"."secret"
|
|
457
|
-
*/
|
|
458
|
-
value;
|
|
459
|
-
/**
|
|
460
|
-
* Hash is computed from the seed to later verify the validity
|
|
461
|
-
* of seed
|
|
462
|
-
*/
|
|
463
|
-
hash;
|
|
464
|
-
/**
|
|
465
|
-
* Date/time when the token instance was created
|
|
466
|
-
*/
|
|
467
|
-
createdAt;
|
|
468
|
-
/**
|
|
469
|
-
* Date/time when the token was updated
|
|
470
|
-
*/
|
|
471
|
-
updatedAt;
|
|
472
|
-
/**
|
|
473
|
-
* Timestamp at which the token will expire
|
|
474
|
-
*/
|
|
475
|
-
expiresAt;
|
|
476
|
-
/**
|
|
477
|
-
* Creates a new RememberMeToken instance
|
|
478
|
-
*
|
|
479
|
-
* @param attributes - Token attributes including identifier, user ID, hash, etc.
|
|
480
|
-
*
|
|
481
|
-
* @example
|
|
482
|
-
* const token = new RememberMeToken({
|
|
483
|
-
* identifier: 1,
|
|
484
|
-
* tokenableId: 123,
|
|
485
|
-
* hash: 'sha256hash',
|
|
486
|
-
* createdAt: new Date(),
|
|
487
|
-
* updatedAt: new Date(),
|
|
488
|
-
* expiresAt: new Date(Date.now() + 86400000)
|
|
489
|
-
* })
|
|
490
|
-
*/
|
|
491
|
-
constructor(attributes) {
|
|
492
|
-
this.identifier = attributes.identifier;
|
|
493
|
-
this.tokenableId = attributes.tokenableId;
|
|
494
|
-
this.hash = attributes.hash;
|
|
495
|
-
this.createdAt = attributes.createdAt;
|
|
496
|
-
this.updatedAt = attributes.updatedAt;
|
|
497
|
-
this.expiresAt = attributes.expiresAt;
|
|
498
|
-
if (attributes.secret) {
|
|
499
|
-
this.value = new Secret2(
|
|
500
|
-
`${base64.urlEncode(String(this.identifier))}.${base64.urlEncode(
|
|
501
|
-
attributes.secret.release()
|
|
502
|
-
)}`
|
|
503
|
-
);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
/**
|
|
507
|
-
* Check if the token has been expired. Verifies
|
|
508
|
-
* the "expiresAt" timestamp with the current
|
|
509
|
-
* date.
|
|
510
|
-
*
|
|
511
|
-
* @example
|
|
512
|
-
* if (token.isExpired()) {
|
|
513
|
-
* console.log('Remember me token has expired')
|
|
514
|
-
* } else {
|
|
515
|
-
* console.log('Token is still valid')
|
|
516
|
-
* }
|
|
517
|
-
*/
|
|
518
|
-
isExpired() {
|
|
519
|
-
return this.expiresAt < /* @__PURE__ */ new Date();
|
|
520
|
-
}
|
|
521
|
-
/**
|
|
522
|
-
* Verifies the value of a token against the pre-defined hash
|
|
523
|
-
*
|
|
524
|
-
* @param secret - The secret to verify against the stored hash
|
|
525
|
-
*
|
|
526
|
-
* @example
|
|
527
|
-
* const isValid = token.verify(new Secret('user-provided-secret'))
|
|
528
|
-
* if (isValid) {
|
|
529
|
-
* console.log('Remember me token is valid')
|
|
530
|
-
* }
|
|
531
|
-
*/
|
|
532
|
-
verify(secret) {
|
|
533
|
-
const newHash = createHash("sha256").update(secret.release()).digest("hex");
|
|
534
|
-
return safeEqual(this.hash, newHash);
|
|
535
|
-
}
|
|
184
|
+
static decode(value) {
|
|
185
|
+
if (typeof value !== "string") return null;
|
|
186
|
+
if (!value) return null;
|
|
187
|
+
const [identifier, ...tokenValue] = value.split(".");
|
|
188
|
+
if (!identifier || tokenValue.length === 0) return null;
|
|
189
|
+
const decodedIdentifier = base64.urlDecode(identifier);
|
|
190
|
+
const decodedSecret = base64.urlDecode(tokenValue.join("."));
|
|
191
|
+
if (!decodedIdentifier || !decodedSecret) return null;
|
|
192
|
+
return {
|
|
193
|
+
identifier: decodedIdentifier,
|
|
194
|
+
secret: new Secret(decodedSecret)
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
static createTransientToken(userId, size, expiresIn) {
|
|
198
|
+
const expiresAt = /* @__PURE__ */ new Date();
|
|
199
|
+
expiresAt.setSeconds(expiresAt.getSeconds() + string.seconds.parse(expiresIn));
|
|
200
|
+
return {
|
|
201
|
+
userId,
|
|
202
|
+
expiresAt,
|
|
203
|
+
...this.seed(size)
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
static seed(size) {
|
|
207
|
+
const secret = new Secret(string.random(size));
|
|
208
|
+
return {
|
|
209
|
+
secret,
|
|
210
|
+
hash: createHash("sha256").update(secret.release()).digest("hex")
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
identifier;
|
|
214
|
+
tokenableId;
|
|
215
|
+
value;
|
|
216
|
+
hash;
|
|
217
|
+
createdAt;
|
|
218
|
+
updatedAt;
|
|
219
|
+
expiresAt;
|
|
220
|
+
constructor(attributes) {
|
|
221
|
+
this.identifier = attributes.identifier;
|
|
222
|
+
this.tokenableId = attributes.tokenableId;
|
|
223
|
+
this.hash = attributes.hash;
|
|
224
|
+
this.createdAt = attributes.createdAt;
|
|
225
|
+
this.updatedAt = attributes.updatedAt;
|
|
226
|
+
this.expiresAt = attributes.expiresAt;
|
|
227
|
+
if (attributes.secret) this.value = new Secret(`${base64.urlEncode(String(this.identifier))}.${base64.urlEncode(attributes.secret.release())}`);
|
|
228
|
+
}
|
|
229
|
+
isExpired() {
|
|
230
|
+
return this.expiresAt < /* @__PURE__ */ new Date();
|
|
231
|
+
}
|
|
232
|
+
verify(secret) {
|
|
233
|
+
const newHash = createHash("sha256").update(secret.release()).digest("hex");
|
|
234
|
+
return safeEqual(this.hash, newHash);
|
|
235
|
+
}
|
|
536
236
|
};
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
/**
|
|
633
|
-
* Returns a query client instance from the parent model
|
|
634
|
-
*
|
|
635
|
-
* @example
|
|
636
|
-
* const db = await provider.getDb()
|
|
637
|
-
* const tokens = await db.from('remember_me_tokens').select('*')
|
|
638
|
-
*/
|
|
639
|
-
async getDb() {
|
|
640
|
-
const model = this.options.tokenableModel;
|
|
641
|
-
return model.$adapter.query(model).client;
|
|
642
|
-
}
|
|
643
|
-
/**
|
|
644
|
-
* Create a token for a user
|
|
645
|
-
*
|
|
646
|
-
* @param user - The user instance to create a token for
|
|
647
|
-
* @param expiresIn - Token expiration time
|
|
648
|
-
*
|
|
649
|
-
* @example
|
|
650
|
-
* const token = await provider.create(user, '30d')
|
|
651
|
-
* console.log('Remember token:', token.value.release())
|
|
652
|
-
*/
|
|
653
|
-
async create(user, expiresIn) {
|
|
654
|
-
this.#ensureIsPersisted(user);
|
|
655
|
-
const queryClient = await this.getDb();
|
|
656
|
-
const transientToken = RememberMeToken.createTransientToken(
|
|
657
|
-
user.$primaryKeyValue,
|
|
658
|
-
this.tokenSecretLength,
|
|
659
|
-
expiresIn
|
|
660
|
-
);
|
|
661
|
-
const dbRow = {
|
|
662
|
-
tokenable_id: transientToken.userId,
|
|
663
|
-
hash: transientToken.hash,
|
|
664
|
-
created_at: /* @__PURE__ */ new Date(),
|
|
665
|
-
updated_at: /* @__PURE__ */ new Date(),
|
|
666
|
-
expires_at: transientToken.expiresAt
|
|
667
|
-
};
|
|
668
|
-
const result = await queryClient.table(this.table).insert(dbRow).returning("id");
|
|
669
|
-
const id = this.#isObject(result[0]) ? result[0].id : result[0];
|
|
670
|
-
if (!id) {
|
|
671
|
-
throw new RuntimeException2(
|
|
672
|
-
`Cannot save access token. The result "${inspect(result)}" of insert query is unexpected`
|
|
673
|
-
);
|
|
674
|
-
}
|
|
675
|
-
return new RememberMeToken({
|
|
676
|
-
identifier: id,
|
|
677
|
-
tokenableId: dbRow.tokenable_id,
|
|
678
|
-
secret: transientToken.secret,
|
|
679
|
-
hash: dbRow.hash,
|
|
680
|
-
createdAt: dbRow.created_at,
|
|
681
|
-
updatedAt: dbRow.updated_at,
|
|
682
|
-
expiresAt: dbRow.expires_at
|
|
683
|
-
});
|
|
684
|
-
}
|
|
685
|
-
/**
|
|
686
|
-
* Find a token for a user by the token id
|
|
687
|
-
*
|
|
688
|
-
* @param user - The user instance that owns the token
|
|
689
|
-
* @param identifier - The token identifier to search for
|
|
690
|
-
*
|
|
691
|
-
* @example
|
|
692
|
-
* const token = await provider.find(user, 123)
|
|
693
|
-
* if (token) {
|
|
694
|
-
* console.log('Found token with id:', token.identifier)
|
|
695
|
-
* }
|
|
696
|
-
*/
|
|
697
|
-
async find(user, identifier) {
|
|
698
|
-
this.#ensureIsPersisted(user);
|
|
699
|
-
const queryClient = await this.getDb();
|
|
700
|
-
const dbRow = await queryClient.query().from(this.table).where({ id: identifier, tokenable_id: user.$primaryKeyValue }).limit(1).first();
|
|
701
|
-
if (!dbRow) {
|
|
702
|
-
return null;
|
|
703
|
-
}
|
|
704
|
-
return this.dbRowToRememberMeToken(dbRow);
|
|
705
|
-
}
|
|
706
|
-
/**
|
|
707
|
-
* Delete a token by its id
|
|
708
|
-
*
|
|
709
|
-
* @param user - The user instance that owns the token
|
|
710
|
-
* @param identifier - The token identifier to delete
|
|
711
|
-
*
|
|
712
|
-
* @example
|
|
713
|
-
* const deletedCount = await provider.delete(user, 123)
|
|
714
|
-
* console.log('Deleted tokens:', deletedCount)
|
|
715
|
-
*/
|
|
716
|
-
async delete(user, identifier) {
|
|
717
|
-
this.#ensureIsPersisted(user);
|
|
718
|
-
const queryClient = await this.getDb();
|
|
719
|
-
const affectedRows = await queryClient.query().from(this.table).where({ id: identifier, tokenable_id: user.$primaryKeyValue }).del().exec();
|
|
720
|
-
return affectedRows;
|
|
721
|
-
}
|
|
722
|
-
/**
|
|
723
|
-
* Returns all the tokens a given user
|
|
724
|
-
*
|
|
725
|
-
* @param user - The user instance to get tokens for
|
|
726
|
-
*
|
|
727
|
-
* @example
|
|
728
|
-
* const tokens = await provider.all(user)
|
|
729
|
-
* console.log('User has', tokens.length, 'remember tokens')
|
|
730
|
-
* tokens.forEach(token => console.log(token.identifier))
|
|
731
|
-
*/
|
|
732
|
-
async all(user) {
|
|
733
|
-
this.#ensureIsPersisted(user);
|
|
734
|
-
const queryClient = await this.getDb();
|
|
735
|
-
const dbRows = await queryClient.query().from(this.table).where({ tokenable_id: user.$primaryKeyValue }).orderBy("id", "desc").exec();
|
|
736
|
-
return dbRows.map((dbRow) => {
|
|
737
|
-
return this.dbRowToRememberMeToken(dbRow);
|
|
738
|
-
});
|
|
739
|
-
}
|
|
740
|
-
/**
|
|
741
|
-
* Verifies a publicly shared remember me token and returns an
|
|
742
|
-
* RememberMeToken for it.
|
|
743
|
-
*
|
|
744
|
-
* Returns null when unable to verify the token or find it
|
|
745
|
-
* inside the storage
|
|
746
|
-
*
|
|
747
|
-
* @param tokenValue - The token value to verify
|
|
748
|
-
*
|
|
749
|
-
* @example
|
|
750
|
-
* const token = await provider.verify(new Secret('rmt_abc123.def456'))
|
|
751
|
-
* if (token && !token.isExpired()) {
|
|
752
|
-
* console.log('Valid remember token for user:', token.tokenableId)
|
|
753
|
-
* }
|
|
754
|
-
*/
|
|
755
|
-
async verify(tokenValue) {
|
|
756
|
-
const decodedToken = RememberMeToken.decode(tokenValue.release());
|
|
757
|
-
if (!decodedToken) {
|
|
758
|
-
return null;
|
|
759
|
-
}
|
|
760
|
-
const db = await this.getDb();
|
|
761
|
-
const dbRow = await db.query().from(this.table).where({ id: decodedToken.identifier }).limit(1).first();
|
|
762
|
-
if (!dbRow) {
|
|
763
|
-
return null;
|
|
764
|
-
}
|
|
765
|
-
const rememberMeToken = this.dbRowToRememberMeToken(dbRow);
|
|
766
|
-
if (!rememberMeToken.verify(decodedToken.secret) || rememberMeToken.isExpired()) {
|
|
767
|
-
return null;
|
|
768
|
-
}
|
|
769
|
-
return rememberMeToken;
|
|
770
|
-
}
|
|
771
|
-
/**
|
|
772
|
-
* Recycles a remember me token by deleting the old one and
|
|
773
|
-
* creates a new one.
|
|
774
|
-
*
|
|
775
|
-
* Ideally, the recycle should update the existing token, but we
|
|
776
|
-
* skip that for now and come back to it later and handle race
|
|
777
|
-
* conditions as well.
|
|
778
|
-
*
|
|
779
|
-
* @param user - The user that owns the token
|
|
780
|
-
* @param identifier - The token identifier to recycle
|
|
781
|
-
* @param expiresIn - New expiration time
|
|
782
|
-
*
|
|
783
|
-
* @example
|
|
784
|
-
* const newToken = await provider.recycle(user, 123, '30d')
|
|
785
|
-
* console.log('Recycled token:', newToken.value.release())
|
|
786
|
-
*/
|
|
787
|
-
async recycle(user, identifier, expiresIn) {
|
|
788
|
-
await this.delete(user, identifier);
|
|
789
|
-
return this.create(user, expiresIn);
|
|
790
|
-
}
|
|
237
|
+
var DbRememberMeTokensProvider = class DbRememberMeTokensProvider {
|
|
238
|
+
static forModel(model, options) {
|
|
239
|
+
return new DbRememberMeTokensProvider({
|
|
240
|
+
tokenableModel: model,
|
|
241
|
+
...options || {}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
table;
|
|
245
|
+
tokenSecretLength;
|
|
246
|
+
constructor(options) {
|
|
247
|
+
this.options = options;
|
|
248
|
+
this.table = options.table || "remember_me_tokens";
|
|
249
|
+
this.tokenSecretLength = options.tokenSecretLength || 40;
|
|
250
|
+
}
|
|
251
|
+
#isObject(value) {
|
|
252
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
253
|
+
}
|
|
254
|
+
#ensureIsPersisted(user) {
|
|
255
|
+
const model = this.options.tokenableModel;
|
|
256
|
+
if (user instanceof model === false) throw new RuntimeException(`Invalid user object. It must be an instance of the "${model.name}" model`);
|
|
257
|
+
if (!user.$primaryKeyValue) throw new RuntimeException(`Cannot use "${model.name}" model for managing remember me tokens. The value of column "${model.primaryKey}" is undefined or null`);
|
|
258
|
+
}
|
|
259
|
+
dbRowToRememberMeToken(dbRow) {
|
|
260
|
+
return new RememberMeToken({
|
|
261
|
+
identifier: dbRow.id,
|
|
262
|
+
tokenableId: dbRow.tokenable_id,
|
|
263
|
+
hash: dbRow.hash,
|
|
264
|
+
createdAt: typeof dbRow.created_at === "number" ? new Date(dbRow.created_at) : dbRow.created_at,
|
|
265
|
+
updatedAt: typeof dbRow.updated_at === "number" ? new Date(dbRow.updated_at) : dbRow.updated_at,
|
|
266
|
+
expiresAt: typeof dbRow.expires_at === "number" ? new Date(dbRow.expires_at) : dbRow.expires_at
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
async getDb() {
|
|
270
|
+
const model = this.options.tokenableModel;
|
|
271
|
+
return model.$adapter.query(model).client;
|
|
272
|
+
}
|
|
273
|
+
async create(user, expiresIn) {
|
|
274
|
+
this.#ensureIsPersisted(user);
|
|
275
|
+
const queryClient = await this.getDb();
|
|
276
|
+
const transientToken = RememberMeToken.createTransientToken(user.$primaryKeyValue, this.tokenSecretLength, expiresIn);
|
|
277
|
+
const dbRow = {
|
|
278
|
+
tokenable_id: transientToken.userId,
|
|
279
|
+
hash: transientToken.hash,
|
|
280
|
+
created_at: /* @__PURE__ */ new Date(),
|
|
281
|
+
updated_at: /* @__PURE__ */ new Date(),
|
|
282
|
+
expires_at: transientToken.expiresAt
|
|
283
|
+
};
|
|
284
|
+
const result = await queryClient.table(this.table).insert(dbRow).returning("id");
|
|
285
|
+
const id = this.#isObject(result[0]) ? result[0].id : result[0];
|
|
286
|
+
if (!id) throw new RuntimeException(`Cannot save access token. The result "${inspect(result)}" of insert query is unexpected`);
|
|
287
|
+
return new RememberMeToken({
|
|
288
|
+
identifier: id,
|
|
289
|
+
tokenableId: dbRow.tokenable_id,
|
|
290
|
+
secret: transientToken.secret,
|
|
291
|
+
hash: dbRow.hash,
|
|
292
|
+
createdAt: dbRow.created_at,
|
|
293
|
+
updatedAt: dbRow.updated_at,
|
|
294
|
+
expiresAt: dbRow.expires_at
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
async find(user, identifier) {
|
|
298
|
+
this.#ensureIsPersisted(user);
|
|
299
|
+
const dbRow = await (await this.getDb()).query().from(this.table).where({
|
|
300
|
+
id: identifier,
|
|
301
|
+
tokenable_id: user.$primaryKeyValue
|
|
302
|
+
}).limit(1).first();
|
|
303
|
+
if (!dbRow) return null;
|
|
304
|
+
return this.dbRowToRememberMeToken(dbRow);
|
|
305
|
+
}
|
|
306
|
+
async delete(user, identifier) {
|
|
307
|
+
this.#ensureIsPersisted(user);
|
|
308
|
+
return await (await this.getDb()).query().from(this.table).where({
|
|
309
|
+
id: identifier,
|
|
310
|
+
tokenable_id: user.$primaryKeyValue
|
|
311
|
+
}).del().exec();
|
|
312
|
+
}
|
|
313
|
+
async all(user) {
|
|
314
|
+
this.#ensureIsPersisted(user);
|
|
315
|
+
return (await (await this.getDb()).query().from(this.table).where({ tokenable_id: user.$primaryKeyValue }).orderBy("id", "desc").exec()).map((dbRow) => {
|
|
316
|
+
return this.dbRowToRememberMeToken(dbRow);
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
async verify(tokenValue) {
|
|
320
|
+
const decodedToken = RememberMeToken.decode(tokenValue.release());
|
|
321
|
+
if (!decodedToken) return null;
|
|
322
|
+
const dbRow = await (await this.getDb()).query().from(this.table).where({ id: decodedToken.identifier }).limit(1).first();
|
|
323
|
+
if (!dbRow) return null;
|
|
324
|
+
const rememberMeToken = this.dbRowToRememberMeToken(dbRow);
|
|
325
|
+
if (!rememberMeToken.verify(decodedToken.secret) || rememberMeToken.isExpired()) return null;
|
|
326
|
+
return rememberMeToken;
|
|
327
|
+
}
|
|
328
|
+
async recycle(user, identifier, expiresIn) {
|
|
329
|
+
await this.delete(user, identifier);
|
|
330
|
+
return this.create(user, expiresIn);
|
|
331
|
+
}
|
|
791
332
|
};
|
|
792
|
-
|
|
793
|
-
// modules/session_guard/user_providers/lucid.ts
|
|
794
|
-
import { RuntimeException as RuntimeException3 } from "@adonisjs/core/exceptions";
|
|
795
333
|
var SessionLucidUserProvider = class {
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
`Cannot use "${model.name}" model for verifying remember me tokens. Make sure to assign a token provider to the model.`
|
|
841
|
-
);
|
|
842
|
-
}
|
|
843
|
-
return model.rememberMeTokens;
|
|
844
|
-
}
|
|
845
|
-
/**
|
|
846
|
-
* Creates an adapter user for the guard
|
|
847
|
-
*
|
|
848
|
-
* @param user - The user model instance
|
|
849
|
-
*
|
|
850
|
-
* @example
|
|
851
|
-
* const guardUser = await provider.createUserForGuard(user)
|
|
852
|
-
* console.log('User ID:', guardUser.getId())
|
|
853
|
-
* console.log('Original user:', guardUser.getOriginal())
|
|
854
|
-
*/
|
|
855
|
-
async createUserForGuard(user) {
|
|
856
|
-
const model = await this.getModel();
|
|
857
|
-
if (user instanceof model === false) {
|
|
858
|
-
throw new RuntimeException3(
|
|
859
|
-
`Invalid user object. It must be an instance of the "${model.name}" model`
|
|
860
|
-
);
|
|
861
|
-
}
|
|
862
|
-
return {
|
|
863
|
-
getId() {
|
|
864
|
-
if (!user.$primaryKeyValue) {
|
|
865
|
-
throw new RuntimeException3(
|
|
866
|
-
`Cannot use "${model.name}" model for authentication. The value of column "${model.primaryKey}" is undefined or null`
|
|
867
|
-
);
|
|
868
|
-
}
|
|
869
|
-
return user.$primaryKeyValue;
|
|
870
|
-
},
|
|
871
|
-
getOriginal() {
|
|
872
|
-
return user;
|
|
873
|
-
}
|
|
874
|
-
};
|
|
875
|
-
}
|
|
876
|
-
/**
|
|
877
|
-
* Finds a user by their primary key value
|
|
878
|
-
*
|
|
879
|
-
* @param identifier - The user identifier to search for
|
|
880
|
-
*
|
|
881
|
-
* @example
|
|
882
|
-
* const guardUser = await provider.findById(123)
|
|
883
|
-
* if (guardUser) {
|
|
884
|
-
* const originalUser = guardUser.getOriginal()
|
|
885
|
-
* console.log('Found user:', originalUser.email)
|
|
886
|
-
* }
|
|
887
|
-
*/
|
|
888
|
-
async findById(identifier) {
|
|
889
|
-
const model = await this.getModel();
|
|
890
|
-
const user = await model.find(identifier);
|
|
891
|
-
if (!user) {
|
|
892
|
-
return null;
|
|
893
|
-
}
|
|
894
|
-
return this.createUserForGuard(user);
|
|
895
|
-
}
|
|
896
|
-
/**
|
|
897
|
-
* Creates a remember token for a given user
|
|
898
|
-
*
|
|
899
|
-
* @param user - The user to create a token for
|
|
900
|
-
* @param expiresIn - Token expiration time
|
|
901
|
-
*
|
|
902
|
-
* @example
|
|
903
|
-
* const token = await provider.createRememberToken(user, '30d')
|
|
904
|
-
* console.log('Remember token:', token.value.release())
|
|
905
|
-
*/
|
|
906
|
-
async createRememberToken(user, expiresIn) {
|
|
907
|
-
const tokensProvider = await this.getTokensProvider();
|
|
908
|
-
return tokensProvider.create(user, expiresIn);
|
|
909
|
-
}
|
|
910
|
-
/**
|
|
911
|
-
* Verify a token by its publicly shared value
|
|
912
|
-
*
|
|
913
|
-
* @param tokenValue - The token value to verify
|
|
914
|
-
*
|
|
915
|
-
* @example
|
|
916
|
-
* const token = await provider.verifyRememberToken(
|
|
917
|
-
* new Secret('rmt_abc123.def456')
|
|
918
|
-
* )
|
|
919
|
-
* if (token && !token.isExpired()) {
|
|
920
|
-
* console.log('Valid remember token for user:', token.tokenableId)
|
|
921
|
-
* }
|
|
922
|
-
*/
|
|
923
|
-
async verifyRememberToken(tokenValue) {
|
|
924
|
-
const tokensProvider = await this.getTokensProvider();
|
|
925
|
-
return tokensProvider.verify(tokenValue);
|
|
926
|
-
}
|
|
927
|
-
/**
|
|
928
|
-
* Delete a token for a user by the token identifier
|
|
929
|
-
*
|
|
930
|
-
* @param user - The user that owns the token
|
|
931
|
-
* @param identifier - The token identifier to delete
|
|
932
|
-
*
|
|
933
|
-
* @example
|
|
934
|
-
* const deletedCount = await provider.deleteRemeberToken(user, 123)
|
|
935
|
-
* console.log('Deleted tokens:', deletedCount)
|
|
936
|
-
*/
|
|
937
|
-
async deleteRemeberToken(user, identifier) {
|
|
938
|
-
const tokensProvider = await this.getTokensProvider();
|
|
939
|
-
return tokensProvider.delete(user, identifier);
|
|
940
|
-
}
|
|
941
|
-
/**
|
|
942
|
-
* Recycle a token for a user by the token identifier
|
|
943
|
-
*
|
|
944
|
-
* @param user - The user that owns the token
|
|
945
|
-
* @param identifier - The token identifier to recycle
|
|
946
|
-
* @param expiresIn - New expiration time
|
|
947
|
-
*
|
|
948
|
-
* @example
|
|
949
|
-
* const newToken = await provider.recycleRememberToken(user, 123, '30d')
|
|
950
|
-
* console.log('Recycled token:', newToken.value.release())
|
|
951
|
-
*/
|
|
952
|
-
async recycleRememberToken(user, identifier, expiresIn) {
|
|
953
|
-
const tokensProvider = await this.getTokensProvider();
|
|
954
|
-
return tokensProvider.recycle(user, identifier, expiresIn);
|
|
955
|
-
}
|
|
334
|
+
model;
|
|
335
|
+
constructor(options) {
|
|
336
|
+
this.options = options;
|
|
337
|
+
}
|
|
338
|
+
async getModel() {
|
|
339
|
+
if (this.model && !("hot" in import.meta)) return this.model;
|
|
340
|
+
this.model = (await this.options.model()).default;
|
|
341
|
+
return this.model;
|
|
342
|
+
}
|
|
343
|
+
async getTokensProvider() {
|
|
344
|
+
const model = await this.getModel();
|
|
345
|
+
if (!model.rememberMeTokens) throw new RuntimeException(`Cannot use "${model.name}" model for verifying remember me tokens. Make sure to assign a token provider to the model.`);
|
|
346
|
+
return model.rememberMeTokens;
|
|
347
|
+
}
|
|
348
|
+
async createUserForGuard(user) {
|
|
349
|
+
const model = await this.getModel();
|
|
350
|
+
if (user instanceof model === false) throw new RuntimeException(`Invalid user object. It must be an instance of the "${model.name}" model`);
|
|
351
|
+
return {
|
|
352
|
+
getId() {
|
|
353
|
+
if (!user.$primaryKeyValue) throw new RuntimeException(`Cannot use "${model.name}" model for authentication. The value of column "${model.primaryKey}" is undefined or null`);
|
|
354
|
+
return user.$primaryKeyValue;
|
|
355
|
+
},
|
|
356
|
+
getOriginal() {
|
|
357
|
+
return user;
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
async findById(identifier) {
|
|
362
|
+
const user = await (await this.getModel()).find(identifier);
|
|
363
|
+
if (!user) return null;
|
|
364
|
+
return this.createUserForGuard(user);
|
|
365
|
+
}
|
|
366
|
+
async createRememberToken(user, expiresIn) {
|
|
367
|
+
return (await this.getTokensProvider()).create(user, expiresIn);
|
|
368
|
+
}
|
|
369
|
+
async verifyRememberToken(tokenValue) {
|
|
370
|
+
return (await this.getTokensProvider()).verify(tokenValue);
|
|
371
|
+
}
|
|
372
|
+
async deleteRemeberToken(user, identifier) {
|
|
373
|
+
return (await this.getTokensProvider()).delete(user, identifier);
|
|
374
|
+
}
|
|
375
|
+
async recycleRememberToken(user, identifier, expiresIn) {
|
|
376
|
+
return (await this.getTokensProvider()).recycle(user, identifier, expiresIn);
|
|
377
|
+
}
|
|
956
378
|
};
|
|
957
|
-
|
|
958
|
-
// modules/session_guard/define_config.ts
|
|
959
379
|
function sessionGuard(config) {
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
}
|
|
966
|
-
};
|
|
380
|
+
return { async resolver(name, app) {
|
|
381
|
+
const emitter = await app.container.make("emitter");
|
|
382
|
+
const provider = "resolver" in config.provider ? await config.provider.resolver(app) : config.provider;
|
|
383
|
+
return (ctx) => new SessionGuard(name, ctx, config, emitter, provider);
|
|
384
|
+
} };
|
|
967
385
|
}
|
|
968
386
|
function sessionUserProvider(config) {
|
|
969
|
-
|
|
387
|
+
return new SessionLucidUserProvider(config);
|
|
970
388
|
}
|
|
971
|
-
export {
|
|
972
|
-
DbRememberMeTokensProvider,
|
|
973
|
-
RememberMeToken,
|
|
974
|
-
SessionGuard,
|
|
975
|
-
SessionLucidUserProvider,
|
|
976
|
-
sessionGuard,
|
|
977
|
-
sessionUserProvider
|
|
978
|
-
};
|
|
389
|
+
export { DbRememberMeTokensProvider, RememberMeToken, SessionGuard, SessionLucidUserProvider, sessionGuard, sessionUserProvider };
|