@adonisjs/auth 10.0.0-next.1 → 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.
@@ -1,1324 +1,662 @@
1
- import {
2
- E_UNAUTHORIZED_ACCESS
3
- } from "../../chunk-S5G5RTJX.js";
4
- import "../../chunk-UXA4FHST.js";
5
-
6
- // modules/access_tokens_guard/access_token.ts
7
- import { createHash } from "crypto";
8
- import string from "@adonisjs/core/helpers/string";
1
+ import { n as E_UNAUTHORIZED_ACCESS } from "../../errors-BQxhZmkE.js";
2
+ import "../../symbols-Ct253Khf.js";
9
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";
10
7
  import { Secret, base64, safeEqual } from "@adonisjs/core/helpers";
11
-
12
- // modules/access_tokens_guard/crc32.ts
13
8
  var CRC32 = class {
14
- /**
15
- * Lookup table calculated for 0xEDB88320 divisor
16
- */
17
- #lookupTable = [
18
- 0,
19
- 1996959894,
20
- 3993919788,
21
- 2567524794,
22
- 124634137,
23
- 1886057615,
24
- 3915621685,
25
- 2657392035,
26
- 249268274,
27
- 2044508324,
28
- 3772115230,
29
- 2547177864,
30
- 162941995,
31
- 2125561021,
32
- 3887607047,
33
- 2428444049,
34
- 498536548,
35
- 1789927666,
36
- 4089016648,
37
- 2227061214,
38
- 450548861,
39
- 1843258603,
40
- 4107580753,
41
- 2211677639,
42
- 325883990,
43
- 1684777152,
44
- 4251122042,
45
- 2321926636,
46
- 335633487,
47
- 1661365465,
48
- 4195302755,
49
- 2366115317,
50
- 997073096,
51
- 1281953886,
52
- 3579855332,
53
- 2724688242,
54
- 1006888145,
55
- 1258607687,
56
- 3524101629,
57
- 2768942443,
58
- 901097722,
59
- 1119000684,
60
- 3686517206,
61
- 2898065728,
62
- 853044451,
63
- 1172266101,
64
- 3705015759,
65
- 2882616665,
66
- 651767980,
67
- 1373503546,
68
- 3369554304,
69
- 3218104598,
70
- 565507253,
71
- 1454621731,
72
- 3485111705,
73
- 3099436303,
74
- 671266974,
75
- 1594198024,
76
- 3322730930,
77
- 2970347812,
78
- 795835527,
79
- 1483230225,
80
- 3244367275,
81
- 3060149565,
82
- 1994146192,
83
- 31158534,
84
- 2563907772,
85
- 4023717930,
86
- 1907459465,
87
- 112637215,
88
- 2680153253,
89
- 3904427059,
90
- 2013776290,
91
- 251722036,
92
- 2517215374,
93
- 3775830040,
94
- 2137656763,
95
- 141376813,
96
- 2439277719,
97
- 3865271297,
98
- 1802195444,
99
- 476864866,
100
- 2238001368,
101
- 4066508878,
102
- 1812370925,
103
- 453092731,
104
- 2181625025,
105
- 4111451223,
106
- 1706088902,
107
- 314042704,
108
- 2344532202,
109
- 4240017532,
110
- 1658658271,
111
- 366619977,
112
- 2362670323,
113
- 4224994405,
114
- 1303535960,
115
- 984961486,
116
- 2747007092,
117
- 3569037538,
118
- 1256170817,
119
- 1037604311,
120
- 2765210733,
121
- 3554079995,
122
- 1131014506,
123
- 879679996,
124
- 2909243462,
125
- 3663771856,
126
- 1141124467,
127
- 855842277,
128
- 2852801631,
129
- 3708648649,
130
- 1342533948,
131
- 654459306,
132
- 3188396048,
133
- 3373015174,
134
- 1466479909,
135
- 544179635,
136
- 3110523913,
137
- 3462522015,
138
- 1591671054,
139
- 702138776,
140
- 2966460450,
141
- 3352799412,
142
- 1504918807,
143
- 783551873,
144
- 3082640443,
145
- 3233442989,
146
- 3988292384,
147
- 2596254646,
148
- 62317068,
149
- 1957810842,
150
- 3939845945,
151
- 2647816111,
152
- 81470997,
153
- 1943803523,
154
- 3814918930,
155
- 2489596804,
156
- 225274430,
157
- 2053790376,
158
- 3826175755,
159
- 2466906013,
160
- 167816743,
161
- 2097651377,
162
- 4027552580,
163
- 2265490386,
164
- 503444072,
165
- 1762050814,
166
- 4150417245,
167
- 2154129355,
168
- 426522225,
169
- 1852507879,
170
- 4275313526,
171
- 2312317920,
172
- 282753626,
173
- 1742555852,
174
- 4189708143,
175
- 2394877945,
176
- 397917763,
177
- 1622183637,
178
- 3604390888,
179
- 2714866558,
180
- 953729732,
181
- 1340076626,
182
- 3518719985,
183
- 2797360999,
184
- 1068828381,
185
- 1219638859,
186
- 3624741850,
187
- 2936675148,
188
- 906185462,
189
- 1090812512,
190
- 3747672003,
191
- 2825379669,
192
- 829329135,
193
- 1181335161,
194
- 3412177804,
195
- 3160834842,
196
- 628085408,
197
- 1382605366,
198
- 3423369109,
199
- 3138078467,
200
- 570562233,
201
- 1426400815,
202
- 3317316542,
203
- 2998733608,
204
- 733239954,
205
- 1555261956,
206
- 3268935591,
207
- 3050360625,
208
- 752459403,
209
- 1541320221,
210
- 2607071920,
211
- 3965973030,
212
- 1969922972,
213
- 40735498,
214
- 2617837225,
215
- 3943577151,
216
- 1913087877,
217
- 83908371,
218
- 2512341634,
219
- 3803740692,
220
- 2075208622,
221
- 213261112,
222
- 2463272603,
223
- 3855990285,
224
- 2094854071,
225
- 198958881,
226
- 2262029012,
227
- 4057260610,
228
- 1759359992,
229
- 534414190,
230
- 2176718541,
231
- 4139329115,
232
- 1873836001,
233
- 414664567,
234
- 2282248934,
235
- 4279200368,
236
- 1711684554,
237
- 285281116,
238
- 2405801727,
239
- 4167216745,
240
- 1634467795,
241
- 376229701,
242
- 2685067896,
243
- 3608007406,
244
- 1308918612,
245
- 956543938,
246
- 2808555105,
247
- 3495958263,
248
- 1231636301,
249
- 1047427035,
250
- 2932959818,
251
- 3654703836,
252
- 1088359270,
253
- 936918e3,
254
- 2847714899,
255
- 3736837829,
256
- 1202900863,
257
- 817233897,
258
- 3183342108,
259
- 3401237130,
260
- 1404277552,
261
- 615818150,
262
- 3134207493,
263
- 3453421203,
264
- 1423857449,
265
- 601450431,
266
- 3009837614,
267
- 3294710456,
268
- 1567103746,
269
- 711928724,
270
- 3020668471,
271
- 3272380065,
272
- 1510334235,
273
- 755167117
274
- ];
275
- #initialCRC = 4294967295;
276
- #calculateBytes(bytes, accumulator) {
277
- let crc = accumulator || this.#initialCRC;
278
- for (const byte of bytes) {
279
- const tableIndex = (crc ^ byte) & 255;
280
- const tableVal = this.#lookupTable[tableIndex];
281
- crc = crc >>> 8 ^ tableVal;
282
- }
283
- return crc;
284
- }
285
- #crcToUint(crc) {
286
- return this.#toUint32(crc ^ 4294967295);
287
- }
288
- #strToBytes(input) {
289
- const encoder = new TextEncoder();
290
- return encoder.encode(input);
291
- }
292
- #toUint32(num) {
293
- if (num >= 0) {
294
- return num;
295
- }
296
- return 4294967295 - num * -1 + 1;
297
- }
298
- /**
299
- * Calculate CRC32 checksum for a string input
300
- *
301
- * @param input - The string to calculate checksum for
302
- *
303
- * @example
304
- * const crc = new CRC32()
305
- * const checksum = crc.calculate('hello-world')
306
- * console.log('CRC32:', checksum)
307
- */
308
- calculate(input) {
309
- return this.forString(input);
310
- }
311
- /**
312
- * Calculate CRC32 checksum for a string
313
- *
314
- * @param input - The string to process
315
- *
316
- * @example
317
- * const crc = new CRC32()
318
- * const result = crc.forString('test-string')
319
- */
320
- forString(input) {
321
- const bytes = this.#strToBytes(input);
322
- return this.forBytes(bytes);
323
- }
324
- /**
325
- * Calculate CRC32 checksum for byte array
326
- *
327
- * @param bytes - The byte array to process
328
- * @param accumulator - Optional accumulator for chained calculations
329
- *
330
- * @example
331
- * const crc = new CRC32()
332
- * const bytes = new TextEncoder().encode('hello')
333
- * const result = crc.forBytes(bytes)
334
- */
335
- forBytes(bytes, accumulator) {
336
- const crc = this.#calculateBytes(bytes, accumulator);
337
- return this.#crcToUint(crc);
338
- }
9
+ #lookupTable = [
10
+ 0,
11
+ 1996959894,
12
+ 3993919788,
13
+ 2567524794,
14
+ 124634137,
15
+ 1886057615,
16
+ 3915621685,
17
+ 2657392035,
18
+ 249268274,
19
+ 2044508324,
20
+ 3772115230,
21
+ 2547177864,
22
+ 162941995,
23
+ 2125561021,
24
+ 3887607047,
25
+ 2428444049,
26
+ 498536548,
27
+ 1789927666,
28
+ 4089016648,
29
+ 2227061214,
30
+ 450548861,
31
+ 1843258603,
32
+ 4107580753,
33
+ 2211677639,
34
+ 325883990,
35
+ 1684777152,
36
+ 4251122042,
37
+ 2321926636,
38
+ 335633487,
39
+ 1661365465,
40
+ 4195302755,
41
+ 2366115317,
42
+ 997073096,
43
+ 1281953886,
44
+ 3579855332,
45
+ 2724688242,
46
+ 1006888145,
47
+ 1258607687,
48
+ 3524101629,
49
+ 2768942443,
50
+ 901097722,
51
+ 1119000684,
52
+ 3686517206,
53
+ 2898065728,
54
+ 853044451,
55
+ 1172266101,
56
+ 3705015759,
57
+ 2882616665,
58
+ 651767980,
59
+ 1373503546,
60
+ 3369554304,
61
+ 3218104598,
62
+ 565507253,
63
+ 1454621731,
64
+ 3485111705,
65
+ 3099436303,
66
+ 671266974,
67
+ 1594198024,
68
+ 3322730930,
69
+ 2970347812,
70
+ 795835527,
71
+ 1483230225,
72
+ 3244367275,
73
+ 3060149565,
74
+ 1994146192,
75
+ 31158534,
76
+ 2563907772,
77
+ 4023717930,
78
+ 1907459465,
79
+ 112637215,
80
+ 2680153253,
81
+ 3904427059,
82
+ 2013776290,
83
+ 251722036,
84
+ 2517215374,
85
+ 3775830040,
86
+ 2137656763,
87
+ 141376813,
88
+ 2439277719,
89
+ 3865271297,
90
+ 1802195444,
91
+ 476864866,
92
+ 2238001368,
93
+ 4066508878,
94
+ 1812370925,
95
+ 453092731,
96
+ 2181625025,
97
+ 4111451223,
98
+ 1706088902,
99
+ 314042704,
100
+ 2344532202,
101
+ 4240017532,
102
+ 1658658271,
103
+ 366619977,
104
+ 2362670323,
105
+ 4224994405,
106
+ 1303535960,
107
+ 984961486,
108
+ 2747007092,
109
+ 3569037538,
110
+ 1256170817,
111
+ 1037604311,
112
+ 2765210733,
113
+ 3554079995,
114
+ 1131014506,
115
+ 879679996,
116
+ 2909243462,
117
+ 3663771856,
118
+ 1141124467,
119
+ 855842277,
120
+ 2852801631,
121
+ 3708648649,
122
+ 1342533948,
123
+ 654459306,
124
+ 3188396048,
125
+ 3373015174,
126
+ 1466479909,
127
+ 544179635,
128
+ 3110523913,
129
+ 3462522015,
130
+ 1591671054,
131
+ 702138776,
132
+ 2966460450,
133
+ 3352799412,
134
+ 1504918807,
135
+ 783551873,
136
+ 3082640443,
137
+ 3233442989,
138
+ 3988292384,
139
+ 2596254646,
140
+ 62317068,
141
+ 1957810842,
142
+ 3939845945,
143
+ 2647816111,
144
+ 81470997,
145
+ 1943803523,
146
+ 3814918930,
147
+ 2489596804,
148
+ 225274430,
149
+ 2053790376,
150
+ 3826175755,
151
+ 2466906013,
152
+ 167816743,
153
+ 2097651377,
154
+ 4027552580,
155
+ 2265490386,
156
+ 503444072,
157
+ 1762050814,
158
+ 4150417245,
159
+ 2154129355,
160
+ 426522225,
161
+ 1852507879,
162
+ 4275313526,
163
+ 2312317920,
164
+ 282753626,
165
+ 1742555852,
166
+ 4189708143,
167
+ 2394877945,
168
+ 397917763,
169
+ 1622183637,
170
+ 3604390888,
171
+ 2714866558,
172
+ 953729732,
173
+ 1340076626,
174
+ 3518719985,
175
+ 2797360999,
176
+ 1068828381,
177
+ 1219638859,
178
+ 3624741850,
179
+ 2936675148,
180
+ 906185462,
181
+ 1090812512,
182
+ 3747672003,
183
+ 2825379669,
184
+ 829329135,
185
+ 1181335161,
186
+ 3412177804,
187
+ 3160834842,
188
+ 628085408,
189
+ 1382605366,
190
+ 3423369109,
191
+ 3138078467,
192
+ 570562233,
193
+ 1426400815,
194
+ 3317316542,
195
+ 2998733608,
196
+ 733239954,
197
+ 1555261956,
198
+ 3268935591,
199
+ 3050360625,
200
+ 752459403,
201
+ 1541320221,
202
+ 2607071920,
203
+ 3965973030,
204
+ 1969922972,
205
+ 40735498,
206
+ 2617837225,
207
+ 3943577151,
208
+ 1913087877,
209
+ 83908371,
210
+ 2512341634,
211
+ 3803740692,
212
+ 2075208622,
213
+ 213261112,
214
+ 2463272603,
215
+ 3855990285,
216
+ 2094854071,
217
+ 198958881,
218
+ 2262029012,
219
+ 4057260610,
220
+ 1759359992,
221
+ 534414190,
222
+ 2176718541,
223
+ 4139329115,
224
+ 1873836001,
225
+ 414664567,
226
+ 2282248934,
227
+ 4279200368,
228
+ 1711684554,
229
+ 285281116,
230
+ 2405801727,
231
+ 4167216745,
232
+ 1634467795,
233
+ 376229701,
234
+ 2685067896,
235
+ 3608007406,
236
+ 1308918612,
237
+ 956543938,
238
+ 2808555105,
239
+ 3495958263,
240
+ 1231636301,
241
+ 1047427035,
242
+ 2932959818,
243
+ 3654703836,
244
+ 1088359270,
245
+ 936918e3,
246
+ 2847714899,
247
+ 3736837829,
248
+ 1202900863,
249
+ 817233897,
250
+ 3183342108,
251
+ 3401237130,
252
+ 1404277552,
253
+ 615818150,
254
+ 3134207493,
255
+ 3453421203,
256
+ 1423857449,
257
+ 601450431,
258
+ 3009837614,
259
+ 3294710456,
260
+ 1567103746,
261
+ 711928724,
262
+ 3020668471,
263
+ 3272380065,
264
+ 1510334235,
265
+ 755167117
266
+ ];
267
+ #initialCRC = 4294967295;
268
+ #calculateBytes(bytes, accumulator) {
269
+ let crc = accumulator || this.#initialCRC;
270
+ for (const byte of bytes) {
271
+ const tableIndex = (crc ^ byte) & 255;
272
+ const tableVal = this.#lookupTable[tableIndex];
273
+ crc = crc >>> 8 ^ tableVal;
274
+ }
275
+ return crc;
276
+ }
277
+ #crcToUint(crc) {
278
+ return this.#toUint32(crc ^ 4294967295);
279
+ }
280
+ #strToBytes(input) {
281
+ return new TextEncoder().encode(input);
282
+ }
283
+ #toUint32(num) {
284
+ if (num >= 0) return num;
285
+ return 4294967295 - num * -1 + 1;
286
+ }
287
+ calculate(input) {
288
+ return this.forString(input);
289
+ }
290
+ forString(input) {
291
+ const bytes = this.#strToBytes(input);
292
+ return this.forBytes(bytes);
293
+ }
294
+ forBytes(bytes, accumulator) {
295
+ const crc = this.#calculateBytes(bytes, accumulator);
296
+ return this.#crcToUint(crc);
297
+ }
339
298
  };
340
-
341
- // modules/access_tokens_guard/access_token.ts
342
299
  var AccessToken = class {
343
- /**
344
- * Decodes a publicly shared token and return the series
345
- * and the token value from it.
346
- *
347
- * Returns null when unable to decode the token because of
348
- * invalid format or encoding.
349
- *
350
- * @param prefix - The token prefix to validate against
351
- * @param value - The token value to decode
352
- *
353
- * @example
354
- * const decoded = AccessToken.decode('oat_', 'oat_abc123.def456')
355
- * if (decoded) {
356
- * console.log('Token ID:', decoded.identifier)
357
- * console.log('Secret:', decoded.secret.release())
358
- * }
359
- */
360
- static decode(prefix, value) {
361
- if (typeof value !== "string" || !value.startsWith(`${prefix}`)) {
362
- return null;
363
- }
364
- const token = value.replace(new RegExp(`^${prefix}`), "");
365
- if (!token) {
366
- return null;
367
- }
368
- const [identifier, ...tokenValue] = token.split(".");
369
- if (!identifier || tokenValue.length === 0) {
370
- return null;
371
- }
372
- const decodedIdentifier = base64.urlDecode(identifier);
373
- const decodedSecret = base64.urlDecode(tokenValue.join("."));
374
- if (!decodedIdentifier || !decodedSecret) {
375
- return null;
376
- }
377
- return {
378
- identifier: decodedIdentifier,
379
- secret: new Secret(decodedSecret)
380
- };
381
- }
382
- /**
383
- * Creates a transient token that can be shared with the persistence
384
- * layer.
385
- *
386
- * @param userId - The ID of the user for whom the token is created
387
- * @param size - The size of the random secret to generate
388
- * @param expiresIn - Optional expiration time (seconds or duration string)
389
- *
390
- * @example
391
- * const transientToken = AccessToken.createTransientToken(123, 32, '7d')
392
- * // Store transientToken in database
393
- */
394
- static createTransientToken(userId, size, expiresIn) {
395
- let expiresAt;
396
- if (expiresIn) {
397
- expiresAt = /* @__PURE__ */ new Date();
398
- expiresAt.setSeconds(expiresAt.getSeconds() + string.seconds.parse(expiresIn));
399
- }
400
- return {
401
- userId,
402
- expiresAt,
403
- ...this.seed(size)
404
- };
405
- }
406
- /**
407
- * Creates a secret opaque token and its hash. The secret is
408
- * suffixed with a crc32 checksum for secret scanning tools
409
- * to easily identify the token.
410
- *
411
- * @param size - The size of the random string to generate
412
- *
413
- * @example
414
- * const { secret, hash } = AccessToken.seed(32)
415
- * console.log('Secret:', secret.release())
416
- * console.log('Hash:', hash)
417
- */
418
- static seed(size) {
419
- const seed = string.random(size);
420
- const secret = new Secret(`${seed}${new CRC32().calculate(seed)}`);
421
- const hash = createHash("sha256").update(secret.release()).digest("hex");
422
- return { secret, hash };
423
- }
424
- /**
425
- * Identifer is a unique sequence to identify the
426
- * token within database. It should be the
427
- * primary/unique key
428
- */
429
- identifier;
430
- /**
431
- * Reference to the user id for whom the token
432
- * is generated.
433
- */
434
- tokenableId;
435
- /**
436
- * The value is a public representation of a token. It is created
437
- * by combining the "identifier"."secret"
438
- */
439
- value;
440
- /**
441
- * Recognizable name for the token
442
- */
443
- name;
444
- /**
445
- * A unique type to identify a bucket of tokens inside the
446
- * storage layer.
447
- */
448
- type;
449
- /**
450
- * Hash is computed from the seed to later verify the validity
451
- * of seed
452
- */
453
- hash;
454
- /**
455
- * Date/time when the token instance was created
456
- */
457
- createdAt;
458
- /**
459
- * Date/time when the token was updated
460
- */
461
- updatedAt;
462
- /**
463
- * Timestamp at which the token was used for authentication
464
- */
465
- lastUsedAt;
466
- /**
467
- * Timestamp at which the token will expire
468
- */
469
- expiresAt;
470
- /**
471
- * An array of abilities the token can perform. The abilities
472
- * is an array of abritary string values
473
- */
474
- abilities;
475
- /**
476
- * Creates a new AccessToken instance
477
- *
478
- * @param attributes - Token attributes including identifier, user ID, type, hash, etc.
479
- *
480
- * @example
481
- * const token = new AccessToken({
482
- * identifier: 1,
483
- * tokenableId: 123,
484
- * type: 'api',
485
- * hash: 'sha256hash',
486
- * createdAt: new Date(),
487
- * updatedAt: new Date(),
488
- * lastUsedAt: null,
489
- * expiresAt: new Date(Date.now() + 86400000),
490
- * name: 'Mobile App Token',
491
- * abilities: ['read:posts', 'write:posts']
492
- * })
493
- */
494
- constructor(attributes) {
495
- this.identifier = attributes.identifier;
496
- this.tokenableId = attributes.tokenableId;
497
- this.name = attributes.name;
498
- this.hash = attributes.hash;
499
- this.type = attributes.type;
500
- this.createdAt = attributes.createdAt;
501
- this.updatedAt = attributes.updatedAt;
502
- this.expiresAt = attributes.expiresAt;
503
- this.lastUsedAt = attributes.lastUsedAt;
504
- this.abilities = attributes.abilities || ["*"];
505
- if (attributes.secret) {
506
- if (!attributes.prefix) {
507
- throw new RuntimeException("Cannot compute token value without the prefix");
508
- }
509
- this.value = new Secret(
510
- `${attributes.prefix}${base64.urlEncode(String(this.identifier))}.${base64.urlEncode(
511
- attributes.secret.release()
512
- )}`
513
- );
514
- }
515
- }
516
- /**
517
- * Check if the token allows the given ability.
518
- *
519
- * @param ability - The ability to check
520
- *
521
- * @example
522
- * if (token.allows('read:posts')) {
523
- * console.log('User can read posts')
524
- * }
525
- */
526
- allows(ability) {
527
- return this.abilities.includes(ability) || this.abilities.includes("*");
528
- }
529
- /**
530
- * Check if the token denies the ability.
531
- *
532
- * @param ability - The ability to check
533
- *
534
- * @example
535
- * if (token.denies('delete:posts')) {
536
- * console.log('User cannot delete posts')
537
- * }
538
- */
539
- denies(ability) {
540
- return !this.abilities.includes(ability) && !this.abilities.includes("*");
541
- }
542
- /**
543
- * Authorize ability access using the current access token
544
- *
545
- * @param ability - The ability to authorize
546
- *
547
- * @throws {E_UNAUTHORIZED_ACCESS} When the token denies the ability
548
- *
549
- * @example
550
- * token.authorize('write:posts') // Throws if not allowed
551
- * console.log('Authorization successful')
552
- */
553
- authorize(ability) {
554
- if (this.denies(ability)) {
555
- throw new E_UNAUTHORIZED_ACCESS("Unauthorized access", { guardDriverName: "access_tokens" });
556
- }
557
- }
558
- /**
559
- * Check if the token has been expired. Verifies
560
- * the "expiresAt" timestamp with the current
561
- * date.
562
- *
563
- * Tokens with no expiry never expire
564
- *
565
- * @example
566
- * if (token.isExpired()) {
567
- * console.log('Token has expired')
568
- * } else {
569
- * console.log('Token is still valid')
570
- * }
571
- */
572
- isExpired() {
573
- if (!this.expiresAt) {
574
- return false;
575
- }
576
- return this.expiresAt < /* @__PURE__ */ new Date();
577
- }
578
- /**
579
- * Verifies the value of a token against the pre-defined hash
580
- *
581
- * @param secret - The secret to verify against the stored hash
582
- *
583
- * @example
584
- * const isValid = token.verify(new Secret('user-provided-secret'))
585
- * if (isValid) {
586
- * console.log('Token is valid')
587
- * }
588
- */
589
- verify(secret) {
590
- const newHash = createHash("sha256").update(secret.release()).digest("hex");
591
- return safeEqual(this.hash, newHash);
592
- }
593
- /**
594
- * Converts the token to a JSON representation suitable for API responses
595
- *
596
- * @example
597
- * const tokenData = token.toJSON()
598
- * console.log(tokenData.type) // 'bearer'
599
- * console.log(tokenData.token) // 'oat_abc123.def456'
600
- */
601
- toJSON() {
602
- return {
603
- type: "bearer",
604
- name: this.name,
605
- token: this.value ? this.value.release() : void 0,
606
- abilities: this.abilities,
607
- lastUsedAt: this.lastUsedAt,
608
- expiresAt: this.expiresAt
609
- };
610
- }
300
+ static decode(prefix, value) {
301
+ if (typeof value !== "string" || !value.startsWith(`${prefix}`)) return null;
302
+ const token = value.replace(/* @__PURE__ */ new RegExp(`^${prefix}`), "");
303
+ if (!token) return null;
304
+ const [identifier, ...tokenValue] = token.split(".");
305
+ if (!identifier || tokenValue.length === 0) return null;
306
+ const decodedIdentifier = base64.urlDecode(identifier);
307
+ const decodedSecret = base64.urlDecode(tokenValue.join("."));
308
+ if (!decodedIdentifier || !decodedSecret) return null;
309
+ return {
310
+ identifier: decodedIdentifier,
311
+ secret: new Secret(decodedSecret)
312
+ };
313
+ }
314
+ static createTransientToken(userId, size, expiresIn) {
315
+ let expiresAt;
316
+ if (expiresIn) {
317
+ expiresAt = /* @__PURE__ */ new Date();
318
+ expiresAt.setSeconds(expiresAt.getSeconds() + string.seconds.parse(expiresIn));
319
+ }
320
+ return {
321
+ userId,
322
+ expiresAt,
323
+ ...this.seed(size)
324
+ };
325
+ }
326
+ static seed(size) {
327
+ const seed = string.random(size);
328
+ const secret = new Secret(`${seed}${new CRC32().calculate(seed)}`);
329
+ return {
330
+ secret,
331
+ hash: createHash("sha256").update(secret.release()).digest("hex")
332
+ };
333
+ }
334
+ identifier;
335
+ tokenableId;
336
+ value;
337
+ name;
338
+ type;
339
+ hash;
340
+ createdAt;
341
+ updatedAt;
342
+ lastUsedAt;
343
+ expiresAt;
344
+ abilities;
345
+ constructor(attributes) {
346
+ this.identifier = attributes.identifier;
347
+ this.tokenableId = attributes.tokenableId;
348
+ this.name = attributes.name;
349
+ this.hash = attributes.hash;
350
+ this.type = attributes.type;
351
+ this.createdAt = attributes.createdAt;
352
+ this.updatedAt = attributes.updatedAt;
353
+ this.expiresAt = attributes.expiresAt;
354
+ this.lastUsedAt = attributes.lastUsedAt;
355
+ this.abilities = attributes.abilities || ["*"];
356
+ if (attributes.secret) {
357
+ if (!attributes.prefix) throw new RuntimeException("Cannot compute token value without the prefix");
358
+ this.value = new Secret(`${attributes.prefix}${base64.urlEncode(String(this.identifier))}.${base64.urlEncode(attributes.secret.release())}`);
359
+ }
360
+ }
361
+ allows(ability) {
362
+ return this.abilities.includes(ability) || this.abilities.includes("*");
363
+ }
364
+ denies(ability) {
365
+ return !this.abilities.includes(ability) && !this.abilities.includes("*");
366
+ }
367
+ authorize(ability) {
368
+ if (this.denies(ability)) throw new E_UNAUTHORIZED_ACCESS("Unauthorized access", { guardDriverName: "access_tokens" });
369
+ }
370
+ isExpired() {
371
+ if (!this.expiresAt) return false;
372
+ return this.expiresAt < /* @__PURE__ */ new Date();
373
+ }
374
+ verify(secret) {
375
+ const newHash = createHash("sha256").update(secret.release()).digest("hex");
376
+ return safeEqual(this.hash, newHash);
377
+ }
378
+ toJSON() {
379
+ return {
380
+ type: "bearer",
381
+ name: this.name,
382
+ token: this.value ? this.value.release() : void 0,
383
+ abilities: this.abilities,
384
+ lastUsedAt: this.lastUsedAt,
385
+ expiresAt: this.expiresAt
386
+ };
387
+ }
611
388
  };
612
-
613
- // modules/access_tokens_guard/guard.ts
614
- import { Secret as Secret2 } from "@adonisjs/core/helpers";
615
389
  var AccessTokensGuard = class {
616
- /**
617
- * A unique name for the guard.
618
- */
619
- #name;
620
- /**
621
- * Reference to the current HTTP context
622
- */
623
- #ctx;
624
- /**
625
- * Provider to lookup user details
626
- */
627
- #userProvider;
628
- /**
629
- * Emitter to emit events
630
- */
631
- #emitter;
632
- /**
633
- * Driver name of the guard
634
- */
635
- driverName = "access_tokens";
636
- /**
637
- * Whether or not the authentication has been attempted
638
- * during the current request.
639
- */
640
- authenticationAttempted = false;
641
- /**
642
- * A boolean to know if the current request has
643
- * been authenticated
644
- */
645
- isAuthenticated = false;
646
- /**
647
- * Reference to an instance of the authenticated user.
648
- * The value only exists after calling one of the
649
- * following methods.
650
- *
651
- * - authenticate
652
- * - check
653
- *
654
- * You can use the "getUserOrFail" method to throw an exception if
655
- * the request is not authenticated.
656
- */
657
- user;
658
- /**
659
- * Creates a new AccessTokensGuard instance
660
- *
661
- * @param name - Unique name for the guard instance
662
- * @param ctx - HTTP context for the current request
663
- * @param emitter - Event emitter for guard events
664
- * @param userProvider - User provider for token verification
665
- *
666
- * @example
667
- * const guard = new AccessTokensGuard(
668
- * 'api',
669
- * ctx,
670
- * emitter,
671
- * new TokenUserProvider()
672
- * )
673
- */
674
- constructor(name, ctx, emitter, userProvider) {
675
- this.#name = name;
676
- this.#ctx = ctx;
677
- this.#emitter = emitter;
678
- this.#userProvider = userProvider;
679
- }
680
- /**
681
- * Emits authentication failure and returns an exception
682
- * to end the authentication cycle.
683
- */
684
- #authenticationFailed() {
685
- const error = new E_UNAUTHORIZED_ACCESS("Unauthorized access", {
686
- guardDriverName: this.driverName
687
- });
688
- this.#emitter.emit("access_tokens_auth:authentication_failed", {
689
- ctx: this.#ctx,
690
- guardName: this.#name,
691
- error
692
- });
693
- return error;
694
- }
695
- /**
696
- * Returns the bearer token from the request headers or fails
697
- */
698
- #getBearerToken() {
699
- const bearerToken = this.#ctx.request.header("authorization", "");
700
- const [type, token] = bearerToken.split(" ");
701
- if (!type || type.toLowerCase() !== "bearer" || !token) {
702
- throw this.#authenticationFailed();
703
- }
704
- return token;
705
- }
706
- /**
707
- * Returns an instance of the authenticated user. Or throws
708
- * an exception if the request is not authenticated.
709
- *
710
- * @throws {E_UNAUTHORIZED_ACCESS} When user is not authenticated
711
- *
712
- * @example
713
- * const user = guard.getUserOrFail()
714
- * console.log('User ID:', user.id)
715
- * console.log('Current token:', user.currentAccessToken.name)
716
- */
717
- getUserOrFail() {
718
- if (!this.user) {
719
- throw new E_UNAUTHORIZED_ACCESS("Unauthorized access", {
720
- guardDriverName: this.driverName
721
- });
722
- }
723
- return this.user;
724
- }
725
- /**
726
- * Authenticate the current HTTP request by verifying the bearer
727
- * token or fails with an exception
728
- *
729
- * @throws {E_UNAUTHORIZED_ACCESS} When authentication fails
730
- *
731
- * @example
732
- * try {
733
- * const user = await guard.authenticate()
734
- * console.log('Authenticated as:', user.email)
735
- * console.log('Token abilities:', user.currentAccessToken.abilities)
736
- * } catch (error) {
737
- * console.log('Authentication failed')
738
- * }
739
- */
740
- async authenticate() {
741
- if (this.authenticationAttempted) {
742
- return this.getUserOrFail();
743
- }
744
- this.authenticationAttempted = true;
745
- this.#emitter.emit("access_tokens_auth:authentication_attempted", {
746
- ctx: this.#ctx,
747
- guardName: this.#name
748
- });
749
- const bearerToken = new Secret2(this.#getBearerToken());
750
- const token = await this.#userProvider.verifyToken(bearerToken);
751
- if (!token) {
752
- throw this.#authenticationFailed();
753
- }
754
- const providerUser = await this.#userProvider.findById(token.tokenableId);
755
- if (!providerUser) {
756
- throw this.#authenticationFailed();
757
- }
758
- this.isAuthenticated = true;
759
- this.user = providerUser.getOriginal();
760
- this.user.currentAccessToken = token;
761
- this.#emitter.emit("access_tokens_auth:authentication_succeeded", {
762
- ctx: this.#ctx,
763
- token,
764
- guardName: this.#name,
765
- user: this.user
766
- });
767
- return this.user;
768
- }
769
- /**
770
- * Create a token for a user (sign in)
771
- *
772
- * @param user - The user to create a token for
773
- * @param abilities - Optional array of abilities the token should have
774
- * @param options - Optional token configuration
775
- *
776
- * @example
777
- * const token = await guard.createToken(user, ['read', 'write'], {
778
- * name: 'Mobile App',
779
- * expiresIn: '7d'
780
- * })
781
- * console.log('Token:', token.value.release())
782
- */
783
- async createToken(user, abilities, options) {
784
- return await this.#userProvider.createToken(user, abilities, options);
785
- }
786
- /**
787
- * Invalidates the currently authenticated token (sign out)
788
- *
789
- * @example
790
- * await guard.invalidateToken()
791
- * console.log('Token invalidated successfully')
792
- */
793
- async invalidateToken() {
794
- const bearerToken = new Secret2(this.#getBearerToken());
795
- return await this.#userProvider.invalidateToken(bearerToken);
796
- }
797
- /**
798
- * Returns the Authorization header clients can use to authenticate
799
- * the request.
800
- *
801
- * @param user - The user to authenticate as
802
- * @param abilities - Optional array of abilities
803
- * @param options - Optional token configuration
804
- *
805
- * @example
806
- * const clientAuth = await guard.authenticateAsClient(user, ['read'])
807
- * // Use clientAuth.headers.authorization in API tests
808
- */
809
- async authenticateAsClient(user, abilities, options) {
810
- const token = await this.#userProvider.createToken(user, abilities, options);
811
- return {
812
- headers: {
813
- authorization: `Bearer ${token.value.release()}`
814
- }
815
- };
816
- }
817
- /**
818
- * Silently check if the user is authenticated or not. The
819
- * method is same as the "authenticate" method but does not
820
- * throw any exceptions.
821
- *
822
- * @example
823
- * const isAuthenticated = await guard.check()
824
- * if (isAuthenticated) {
825
- * const user = guard.user
826
- * console.log('User is authenticated:', user.email)
827
- * }
828
- */
829
- async check() {
830
- try {
831
- await this.authenticate();
832
- return true;
833
- } catch (error) {
834
- if (error instanceof E_UNAUTHORIZED_ACCESS) {
835
- return false;
836
- }
837
- throw error;
838
- }
839
- }
390
+ #name;
391
+ #ctx;
392
+ #userProvider;
393
+ #emitter;
394
+ driverName = "access_tokens";
395
+ authenticationAttempted = false;
396
+ isAuthenticated = false;
397
+ user;
398
+ constructor(name, ctx, emitter, userProvider) {
399
+ this.#name = name;
400
+ this.#ctx = ctx;
401
+ this.#emitter = emitter;
402
+ this.#userProvider = userProvider;
403
+ }
404
+ #authenticationFailed() {
405
+ const error = new E_UNAUTHORIZED_ACCESS("Unauthorized access", { guardDriverName: this.driverName });
406
+ this.#emitter.emit("access_tokens_auth:authentication_failed", {
407
+ ctx: this.#ctx,
408
+ guardName: this.#name,
409
+ error
410
+ });
411
+ return error;
412
+ }
413
+ #getBearerToken() {
414
+ const [type, token] = this.#ctx.request.header("authorization", "").split(" ");
415
+ if (!type || type.toLowerCase() !== "bearer" || !token) throw this.#authenticationFailed();
416
+ return token;
417
+ }
418
+ getUserOrFail() {
419
+ if (!this.user) throw new E_UNAUTHORIZED_ACCESS("Unauthorized access", { guardDriverName: this.driverName });
420
+ return this.user;
421
+ }
422
+ async authenticate() {
423
+ if (this.authenticationAttempted) return this.getUserOrFail();
424
+ this.authenticationAttempted = true;
425
+ this.#emitter.emit("access_tokens_auth:authentication_attempted", {
426
+ ctx: this.#ctx,
427
+ guardName: this.#name
428
+ });
429
+ const bearerToken = new Secret(this.#getBearerToken());
430
+ const token = await this.#userProvider.verifyToken(bearerToken);
431
+ if (!token) throw this.#authenticationFailed();
432
+ const providerUser = await this.#userProvider.findById(token.tokenableId);
433
+ if (!providerUser) throw this.#authenticationFailed();
434
+ this.isAuthenticated = true;
435
+ this.user = providerUser.getOriginal();
436
+ this.user.currentAccessToken = token;
437
+ this.#emitter.emit("access_tokens_auth:authentication_succeeded", {
438
+ ctx: this.#ctx,
439
+ token,
440
+ guardName: this.#name,
441
+ user: this.user
442
+ });
443
+ return this.user;
444
+ }
445
+ async createToken(user, abilities, options) {
446
+ return await this.#userProvider.createToken(user, abilities, options);
447
+ }
448
+ async invalidateToken() {
449
+ const bearerToken = new Secret(this.#getBearerToken());
450
+ return await this.#userProvider.invalidateToken(bearerToken);
451
+ }
452
+ async authenticateAsClient(user, abilities, options) {
453
+ return { headers: { authorization: `Bearer ${(await this.#userProvider.createToken(user, abilities, options)).value.release()}` } };
454
+ }
455
+ async check() {
456
+ try {
457
+ await this.authenticate();
458
+ return true;
459
+ } catch (error) {
460
+ if (error instanceof E_UNAUTHORIZED_ACCESS) return false;
461
+ throw error;
462
+ }
463
+ }
840
464
  };
841
-
842
- // modules/access_tokens_guard/token_providers/db.ts
843
- import { inspect } from "util";
844
- import { RuntimeException as RuntimeException2 } from "@adonisjs/core/exceptions";
845
- var DbAccessTokensProvider = class _DbAccessTokensProvider {
846
- /**
847
- * Creates a new DbAccessTokensProvider instance
848
- *
849
- * @param options - Configuration options for the provider
850
- *
851
- * @example
852
- * const provider = new DbAccessTokensProvider({
853
- * tokenableModel: () => import('#models/user'),
854
- * table: 'auth_access_tokens',
855
- * tokenSecretLength: 40,
856
- * type: 'auth_token',
857
- * prefix: 'oat_'
858
- * })
859
- */
860
- constructor(options) {
861
- this.options = options;
862
- this.table = options.table || "auth_access_tokens";
863
- this.tokenSecretLength = options.tokenSecretLength || 40;
864
- this.type = options.type || "auth_token";
865
- this.prefix = options.prefix || "oat_";
866
- }
867
- /**
868
- * Create tokens provider instance for a given Lucid model
869
- *
870
- * @param model - The tokenable model factory function
871
- * @param options - Optional configuration options
872
- *
873
- * @example
874
- * const provider = DbAccessTokensProvider.forModel(
875
- * () => import('#models/user'),
876
- * { prefix: 'api_', type: 'api_token' }
877
- * )
878
- */
879
- static forModel(model, options) {
880
- return new _DbAccessTokensProvider({ tokenableModel: model, ...options || {} });
881
- }
882
- /**
883
- * A unique type for the value. The type is used to identify a
884
- * bucket of tokens within the storage layer.
885
- *
886
- * Defaults to auth_token
887
- */
888
- type;
889
- /**
890
- * A unique prefix to append to the publicly shared token value.
891
- *
892
- * Defaults to oat
893
- */
894
- prefix;
895
- /**
896
- * Database table to use for querying access tokens
897
- */
898
- table;
899
- /**
900
- * The length for the token secret. A secret is a cryptographically
901
- * secure random string.
902
- */
903
- tokenSecretLength;
904
- /**
905
- * Check if value is an object
906
- */
907
- #isObject(value) {
908
- return value !== null && typeof value === "object" && !Array.isArray(value);
909
- }
910
- /**
911
- * Ensure the provided user is an instance of the user model and
912
- * has a primary key
913
- */
914
- #ensureIsPersisted(user) {
915
- const model = this.options.tokenableModel;
916
- if (user instanceof model === false) {
917
- throw new RuntimeException2(
918
- `Invalid user object. It must be an instance of the "${model.name}" model`
919
- );
920
- }
921
- if (!user.$primaryKeyValue) {
922
- throw new RuntimeException2(
923
- `Cannot use "${model.name}" model for managing access tokens. The value of column "${model.primaryKey}" is undefined or null`
924
- );
925
- }
926
- }
927
- /**
928
- * Maps a database row to an AccessToken instance
929
- *
930
- * @param dbRow - The database row containing token data
931
- *
932
- * @example
933
- * const token = provider.dbRowToAccessToken({
934
- * id: 1,
935
- * tokenable_id: 123,
936
- * type: 'auth_token',
937
- * hash: 'sha256hash',
938
- * // ... other columns
939
- * })
940
- */
941
- dbRowToAccessToken(dbRow) {
942
- return new AccessToken({
943
- identifier: dbRow.id,
944
- tokenableId: dbRow.tokenable_id,
945
- type: dbRow.type,
946
- name: dbRow.name,
947
- hash: dbRow.hash,
948
- abilities: JSON.parse(dbRow.abilities),
949
- createdAt: typeof dbRow.created_at === "number" ? new Date(dbRow.created_at) : dbRow.created_at,
950
- updatedAt: typeof dbRow.updated_at === "number" ? new Date(dbRow.updated_at) : dbRow.updated_at,
951
- lastUsedAt: typeof dbRow.last_used_at === "number" ? new Date(dbRow.last_used_at) : dbRow.last_used_at,
952
- expiresAt: typeof dbRow.expires_at === "number" ? new Date(dbRow.expires_at) : dbRow.expires_at
953
- });
954
- }
955
- /**
956
- * Returns a query client instance from the parent model
957
- *
958
- * @example
959
- * const db = await provider.getDb()
960
- * const tokens = await db.from('auth_access_tokens').select('*')
961
- */
962
- async getDb() {
963
- const model = this.options.tokenableModel;
964
- return model.$adapter.query(model).client;
965
- }
966
- /**
967
- * Create a token for a user
968
- *
969
- * @param user - The user instance to create a token for
970
- * @param abilities - Array of abilities the token should have
971
- * @param options - Optional token configuration
972
- *
973
- * @example
974
- * const token = await provider.create(user, ['read', 'write'], {
975
- * name: 'Mobile App Token',
976
- * expiresIn: '7d'
977
- * })
978
- * console.log('Token:', token.value.release())
979
- */
980
- async create(user, abilities = ["*"], options) {
981
- this.#ensureIsPersisted(user);
982
- const queryClient = await this.getDb();
983
- const transientToken = AccessToken.createTransientToken(
984
- user.$primaryKeyValue,
985
- this.tokenSecretLength,
986
- options?.expiresIn || this.options.expiresIn
987
- );
988
- const dbRow = {
989
- tokenable_id: transientToken.userId,
990
- type: this.type,
991
- name: options?.name || null,
992
- hash: transientToken.hash,
993
- abilities: JSON.stringify(abilities),
994
- created_at: /* @__PURE__ */ new Date(),
995
- updated_at: /* @__PURE__ */ new Date(),
996
- last_used_at: null,
997
- expires_at: transientToken.expiresAt || null
998
- };
999
- const result = await queryClient.table(this.table).insert(dbRow).returning("id");
1000
- const id = this.#isObject(result[0]) ? result[0].id : result[0];
1001
- if (!id) {
1002
- throw new RuntimeException2(
1003
- `Cannot save access token. The result "${inspect(result)}" of insert query is unexpected`
1004
- );
1005
- }
1006
- return new AccessToken({
1007
- identifier: id,
1008
- tokenableId: dbRow.tokenable_id,
1009
- type: dbRow.type,
1010
- prefix: this.prefix,
1011
- secret: transientToken.secret,
1012
- name: dbRow.name,
1013
- hash: dbRow.hash,
1014
- abilities: JSON.parse(dbRow.abilities),
1015
- createdAt: dbRow.created_at,
1016
- updatedAt: dbRow.updated_at,
1017
- lastUsedAt: dbRow.last_used_at,
1018
- expiresAt: dbRow.expires_at
1019
- });
1020
- }
1021
- /**
1022
- * Find a token for a user by the token id
1023
- *
1024
- * @param user - The user instance that owns the token
1025
- * @param identifier - The token identifier to search for
1026
- *
1027
- * @example
1028
- * const token = await provider.find(user, 123)
1029
- * if (token) {
1030
- * console.log('Found token:', token.name)
1031
- * }
1032
- */
1033
- async find(user, identifier) {
1034
- this.#ensureIsPersisted(user);
1035
- const queryClient = await this.getDb();
1036
- const dbRow = await queryClient.query().from(this.table).where({ id: identifier, tokenable_id: user.$primaryKeyValue, type: this.type }).limit(1).first();
1037
- if (!dbRow) {
1038
- return null;
1039
- }
1040
- return this.dbRowToAccessToken(dbRow);
1041
- }
1042
- /**
1043
- * Delete a token by its id
1044
- *
1045
- * @param user - The user instance that owns the token
1046
- * @param identifier - The token identifier to delete
1047
- *
1048
- * @example
1049
- * const deletedCount = await provider.delete(user, 123)
1050
- * console.log('Deleted tokens:', deletedCount)
1051
- */
1052
- async delete(user, identifier) {
1053
- this.#ensureIsPersisted(user);
1054
- const queryClient = await this.getDb();
1055
- const affectedRows = await queryClient.query().from(this.table).where({ id: identifier, tokenable_id: user.$primaryKeyValue, type: this.type }).del().exec();
1056
- return affectedRows;
1057
- }
1058
- /**
1059
- * Returns all the tokens for a given user
1060
- *
1061
- * @param user - The user instance to get tokens for
1062
- *
1063
- * @example
1064
- * const tokens = await provider.all(user)
1065
- * console.log('User has', tokens.length, 'tokens')
1066
- * tokens.forEach(token => console.log(token.name))
1067
- */
1068
- async all(user) {
1069
- this.#ensureIsPersisted(user);
1070
- const queryClient = await this.getDb();
1071
- const dbRows = await queryClient.query().from(this.table).where({ tokenable_id: user.$primaryKeyValue, type: this.type }).ifDialect("postgres", (query) => {
1072
- query.orderBy([
1073
- {
1074
- column: "last_used_at",
1075
- order: "desc",
1076
- nulls: "last"
1077
- }
1078
- ]);
1079
- }).unlessDialect("postgres", (query) => {
1080
- query.orderBy([
1081
- {
1082
- column: "last_used_at",
1083
- order: "asc",
1084
- nulls: "last"
1085
- }
1086
- ]);
1087
- }).orderBy("id", "desc").exec();
1088
- return dbRows.map((dbRow) => {
1089
- return this.dbRowToAccessToken(dbRow);
1090
- });
1091
- }
1092
- /**
1093
- * Verifies a publicly shared access token and returns an
1094
- * access token for it.
1095
- *
1096
- * Returns null when unable to verify the token or find it
1097
- * inside the storage
1098
- *
1099
- * @param tokenValue - The token value to verify
1100
- *
1101
- * @example
1102
- * const token = await provider.verify(new Secret('oat_abc123.def456'))
1103
- * if (token && !token.isExpired()) {
1104
- * console.log('Valid token for user:', token.tokenableId)
1105
- * }
1106
- */
1107
- async verify(tokenValue) {
1108
- const decodedToken = AccessToken.decode(this.prefix, tokenValue.release());
1109
- if (!decodedToken) {
1110
- return null;
1111
- }
1112
- const db = await this.getDb();
1113
- const dbRow = await db.query().from(this.table).where({ id: decodedToken.identifier, type: this.type }).limit(1).first();
1114
- if (!dbRow) {
1115
- return null;
1116
- }
1117
- dbRow.last_used_at = /* @__PURE__ */ new Date();
1118
- await db.from(this.table).where({ id: dbRow.id, type: dbRow.type }).update({ last_used_at: dbRow.last_used_at });
1119
- const accessToken = this.dbRowToAccessToken(dbRow);
1120
- if (!accessToken.verify(decodedToken.secret) || accessToken.isExpired()) {
1121
- return null;
1122
- }
1123
- return accessToken;
1124
- }
1125
- /**
1126
- * Invalidates a token identified by its publicly shared token
1127
- *
1128
- * @param tokenValue - The token value to invalidate
1129
- *
1130
- * @example
1131
- * const wasInvalidated = await provider.invalidate(new Secret('oat_abc123.def456'))
1132
- * if (wasInvalidated) {
1133
- * console.log('Token successfully invalidated')
1134
- * }
1135
- */
1136
- async invalidate(tokenValue) {
1137
- const decodedToken = AccessToken.decode(this.prefix, tokenValue.release());
1138
- if (!decodedToken) {
1139
- return false;
1140
- }
1141
- const db = await this.getDb();
1142
- const deleteCount = await db.query().from(this.table).where({ id: decodedToken.identifier, type: this.type }).del().exec();
1143
- return Boolean(deleteCount);
1144
- }
465
+ var DbAccessTokensProvider = class DbAccessTokensProvider {
466
+ static forModel(model, options) {
467
+ return new DbAccessTokensProvider({
468
+ tokenableModel: model,
469
+ ...options || {}
470
+ });
471
+ }
472
+ type;
473
+ prefix;
474
+ table;
475
+ tokenSecretLength;
476
+ constructor(options) {
477
+ this.options = options;
478
+ this.table = options.table || "auth_access_tokens";
479
+ this.tokenSecretLength = options.tokenSecretLength || 40;
480
+ this.type = options.type || "auth_token";
481
+ this.prefix = options.prefix || "oat_";
482
+ }
483
+ #isObject(value) {
484
+ return value !== null && typeof value === "object" && !Array.isArray(value);
485
+ }
486
+ #ensureIsPersisted(user) {
487
+ const model = this.options.tokenableModel;
488
+ if (user instanceof model === false) throw new RuntimeException(`Invalid user object. It must be an instance of the "${model.name}" model`);
489
+ if (!user.$primaryKeyValue) throw new RuntimeException(`Cannot use "${model.name}" model for managing access tokens. The value of column "${model.primaryKey}" is undefined or null`);
490
+ }
491
+ dbRowToAccessToken(dbRow) {
492
+ return new AccessToken({
493
+ identifier: dbRow.id,
494
+ tokenableId: dbRow.tokenable_id,
495
+ type: dbRow.type,
496
+ name: dbRow.name,
497
+ hash: dbRow.hash,
498
+ abilities: JSON.parse(dbRow.abilities),
499
+ createdAt: typeof dbRow.created_at === "number" ? new Date(dbRow.created_at) : dbRow.created_at,
500
+ updatedAt: typeof dbRow.updated_at === "number" ? new Date(dbRow.updated_at) : dbRow.updated_at,
501
+ lastUsedAt: typeof dbRow.last_used_at === "number" ? new Date(dbRow.last_used_at) : dbRow.last_used_at,
502
+ expiresAt: typeof dbRow.expires_at === "number" ? new Date(dbRow.expires_at) : dbRow.expires_at
503
+ });
504
+ }
505
+ async getDb() {
506
+ const model = this.options.tokenableModel;
507
+ return model.$adapter.query(model).client;
508
+ }
509
+ async create(user, abilities = ["*"], options) {
510
+ this.#ensureIsPersisted(user);
511
+ const queryClient = await this.getDb();
512
+ const transientToken = AccessToken.createTransientToken(user.$primaryKeyValue, this.tokenSecretLength, options?.expiresIn || this.options.expiresIn);
513
+ const dbRow = {
514
+ tokenable_id: transientToken.userId,
515
+ type: this.type,
516
+ name: options?.name || null,
517
+ hash: transientToken.hash,
518
+ abilities: JSON.stringify(abilities),
519
+ created_at: /* @__PURE__ */ new Date(),
520
+ updated_at: /* @__PURE__ */ new Date(),
521
+ last_used_at: null,
522
+ expires_at: transientToken.expiresAt || null
523
+ };
524
+ const result = await queryClient.table(this.table).insert(dbRow).returning("id");
525
+ const id = this.#isObject(result[0]) ? result[0].id : result[0];
526
+ if (!id) throw new RuntimeException(`Cannot save access token. The result "${inspect(result)}" of insert query is unexpected`);
527
+ return new AccessToken({
528
+ identifier: id,
529
+ tokenableId: dbRow.tokenable_id,
530
+ type: dbRow.type,
531
+ prefix: this.prefix,
532
+ secret: transientToken.secret,
533
+ name: dbRow.name,
534
+ hash: dbRow.hash,
535
+ abilities: JSON.parse(dbRow.abilities),
536
+ createdAt: dbRow.created_at,
537
+ updatedAt: dbRow.updated_at,
538
+ lastUsedAt: dbRow.last_used_at,
539
+ expiresAt: dbRow.expires_at
540
+ });
541
+ }
542
+ async find(user, identifier) {
543
+ this.#ensureIsPersisted(user);
544
+ const dbRow = await (await this.getDb()).query().from(this.table).where({
545
+ id: identifier,
546
+ tokenable_id: user.$primaryKeyValue,
547
+ type: this.type
548
+ }).limit(1).first();
549
+ if (!dbRow) return null;
550
+ return this.dbRowToAccessToken(dbRow);
551
+ }
552
+ async delete(user, identifier) {
553
+ this.#ensureIsPersisted(user);
554
+ return await (await this.getDb()).query().from(this.table).where({
555
+ id: identifier,
556
+ tokenable_id: user.$primaryKeyValue,
557
+ type: this.type
558
+ }).del().exec();
559
+ }
560
+ async all(user) {
561
+ this.#ensureIsPersisted(user);
562
+ return (await (await this.getDb()).query().from(this.table).where({
563
+ tokenable_id: user.$primaryKeyValue,
564
+ type: this.type
565
+ }).ifDialect("postgres", (query) => {
566
+ query.orderBy([{
567
+ column: "last_used_at",
568
+ order: "desc",
569
+ nulls: "last"
570
+ }]);
571
+ }).unlessDialect("postgres", (query) => {
572
+ query.orderBy([{
573
+ column: "last_used_at",
574
+ order: "asc",
575
+ nulls: "last"
576
+ }]);
577
+ }).orderBy("id", "desc").exec()).map((dbRow) => {
578
+ return this.dbRowToAccessToken(dbRow);
579
+ });
580
+ }
581
+ async verify(tokenValue) {
582
+ const decodedToken = AccessToken.decode(this.prefix, tokenValue.release());
583
+ if (!decodedToken) return null;
584
+ const db = await this.getDb();
585
+ const dbRow = await db.query().from(this.table).where({
586
+ id: decodedToken.identifier,
587
+ type: this.type
588
+ }).limit(1).first();
589
+ if (!dbRow) return null;
590
+ dbRow.last_used_at = /* @__PURE__ */ new Date();
591
+ await db.from(this.table).where({
592
+ id: dbRow.id,
593
+ type: dbRow.type
594
+ }).update({ last_used_at: dbRow.last_used_at });
595
+ const accessToken = this.dbRowToAccessToken(dbRow);
596
+ if (!accessToken.verify(decodedToken.secret) || accessToken.isExpired()) return null;
597
+ return accessToken;
598
+ }
599
+ async invalidate(tokenValue) {
600
+ const decodedToken = AccessToken.decode(this.prefix, tokenValue.release());
601
+ if (!decodedToken) return false;
602
+ const deleteCount = await (await this.getDb()).query().from(this.table).where({
603
+ id: decodedToken.identifier,
604
+ type: this.type
605
+ }).del().exec();
606
+ return Boolean(deleteCount);
607
+ }
1145
608
  };
1146
-
1147
- // modules/access_tokens_guard/user_providers/lucid.ts
1148
- import { RuntimeException as RuntimeException3 } from "@adonisjs/core/exceptions";
1149
609
  var AccessTokensLucidUserProvider = class {
1150
- /**
1151
- * Creates a new AccessTokensLucidUserProvider instance
1152
- *
1153
- * @param options - Configuration options for the user provider
1154
- *
1155
- * @example
1156
- * const provider = new AccessTokensLucidUserProvider({
1157
- * model: () => import('#models/user'),
1158
- * tokens: 'accessTokens'
1159
- * })
1160
- */
1161
- constructor(options) {
1162
- this.options = options;
1163
- }
1164
- /**
1165
- * Reference to the lazily imported model
1166
- */
1167
- model;
1168
- /**
1169
- * Imports the model from the provider, returns and caches it
1170
- * for further operations.
1171
- *
1172
- * @example
1173
- * const UserModel = await provider.getModel()
1174
- * const user = await UserModel.find(1)
1175
- */
1176
- async getModel() {
1177
- if (this.model && !("hot" in import.meta)) {
1178
- return this.model;
1179
- }
1180
- const importedModel = await this.options.model();
1181
- this.model = importedModel.default;
1182
- return this.model;
1183
- }
1184
- /**
1185
- * Returns the tokens provider associated with the user model
1186
- *
1187
- * @example
1188
- * const tokensProvider = await provider.getTokensProvider()
1189
- * const token = await tokensProvider.create(user, ['read'])
1190
- */
1191
- async getTokensProvider() {
1192
- const model = await this.getModel();
1193
- if (!model[this.options.tokens]) {
1194
- throw new RuntimeException3(
1195
- `Cannot use "${model.name}" model for verifying access tokens. Make sure to assign a token provider to the model.`
1196
- );
1197
- }
1198
- return model[this.options.tokens];
1199
- }
1200
- /**
1201
- * Creates an adapter user for the guard
1202
- *
1203
- * @param user - The user model instance
1204
- *
1205
- * @example
1206
- * const guardUser = await provider.createUserForGuard(user)
1207
- * console.log('User ID:', guardUser.getId())
1208
- * console.log('Original user:', guardUser.getOriginal())
1209
- */
1210
- async createUserForGuard(user) {
1211
- const model = await this.getModel();
1212
- if (user instanceof model === false) {
1213
- throw new RuntimeException3(
1214
- `Invalid user object. It must be an instance of the "${model.name}" model`
1215
- );
1216
- }
1217
- return {
1218
- getId() {
1219
- if (!user.$primaryKeyValue) {
1220
- throw new RuntimeException3(
1221
- `Cannot use "${model.name}" model for authentication. The value of column "${model.primaryKey}" is undefined or null`
1222
- );
1223
- }
1224
- return user.$primaryKeyValue;
1225
- },
1226
- getOriginal() {
1227
- return user;
1228
- }
1229
- };
1230
- }
1231
- /**
1232
- * Create a token for a given user
1233
- *
1234
- * @param user - The user to create a token for
1235
- * @param abilities - Optional array of abilities the token should have
1236
- * @param options - Optional token configuration
1237
- *
1238
- * @example
1239
- * const token = await provider.createToken(user, ['read', 'write'], {
1240
- * name: 'API Token',
1241
- * expiresIn: '30d'
1242
- * })
1243
- * console.log('Created token:', token.value.release())
1244
- */
1245
- async createToken(user, abilities, options) {
1246
- const tokensProvider = await this.getTokensProvider();
1247
- return tokensProvider.create(user, abilities, options);
1248
- }
1249
- /**
1250
- * Invalidates a token identified by its publicly shared token
1251
- *
1252
- * @param tokenValue - The token value to invalidate
1253
- *
1254
- * @example
1255
- * const wasInvalidated = await provider.invalidateToken(
1256
- * new Secret('oat_abc123.def456')
1257
- * )
1258
- * console.log('Token invalidated:', wasInvalidated)
1259
- */
1260
- async invalidateToken(tokenValue) {
1261
- const tokensProvider = await this.getTokensProvider();
1262
- return tokensProvider.invalidate(tokenValue);
1263
- }
1264
- /**
1265
- * Finds a user by the user id
1266
- *
1267
- * @param identifier - The user identifier to search for
1268
- *
1269
- * @example
1270
- * const guardUser = await provider.findById(123)
1271
- * if (guardUser) {
1272
- * const originalUser = guardUser.getOriginal()
1273
- * console.log('Found user:', originalUser.email)
1274
- * }
1275
- */
1276
- async findById(identifier) {
1277
- const model = await this.getModel();
1278
- const user = await model.find(identifier);
1279
- if (!user) {
1280
- return null;
1281
- }
1282
- return this.createUserForGuard(user);
1283
- }
1284
- /**
1285
- * Verifies a publicly shared access token and returns an
1286
- * access token for it.
1287
- *
1288
- * @param tokenValue - The token value to verify
1289
- *
1290
- * @example
1291
- * const token = await provider.verifyToken(
1292
- * new Secret('oat_abc123.def456')
1293
- * )
1294
- * if (token && !token.isExpired()) {
1295
- * console.log('Valid token with abilities:', token.abilities)
1296
- * }
1297
- */
1298
- async verifyToken(tokenValue) {
1299
- const tokensProvider = await this.getTokensProvider();
1300
- return tokensProvider.verify(tokenValue);
1301
- }
610
+ model;
611
+ constructor(options) {
612
+ this.options = options;
613
+ }
614
+ async getModel() {
615
+ if (this.model && !("hot" in import.meta)) return this.model;
616
+ this.model = (await this.options.model()).default;
617
+ return this.model;
618
+ }
619
+ async getTokensProvider() {
620
+ const model = await this.getModel();
621
+ if (!model[this.options.tokens]) throw new RuntimeException(`Cannot use "${model.name}" model for verifying access tokens. Make sure to assign a token provider to the model.`);
622
+ return model[this.options.tokens];
623
+ }
624
+ async createUserForGuard(user) {
625
+ const model = await this.getModel();
626
+ if (user instanceof model === false) throw new RuntimeException(`Invalid user object. It must be an instance of the "${model.name}" model`);
627
+ return {
628
+ getId() {
629
+ if (!user.$primaryKeyValue) throw new RuntimeException(`Cannot use "${model.name}" model for authentication. The value of column "${model.primaryKey}" is undefined or null`);
630
+ return user.$primaryKeyValue;
631
+ },
632
+ getOriginal() {
633
+ return user;
634
+ }
635
+ };
636
+ }
637
+ async createToken(user, abilities, options) {
638
+ return (await this.getTokensProvider()).create(user, abilities, options);
639
+ }
640
+ async invalidateToken(tokenValue) {
641
+ return (await this.getTokensProvider()).invalidate(tokenValue);
642
+ }
643
+ async findById(identifier) {
644
+ const user = await (await this.getModel()).find(identifier);
645
+ if (!user) return null;
646
+ return this.createUserForGuard(user);
647
+ }
648
+ async verifyToken(tokenValue) {
649
+ return (await this.getTokensProvider()).verify(tokenValue);
650
+ }
1302
651
  };
1303
-
1304
- // modules/access_tokens_guard/define_config.ts
1305
652
  function tokensGuard(config) {
1306
- return {
1307
- async resolver(name, app) {
1308
- const emitter = await app.container.make("emitter");
1309
- const provider = "resolver" in config.provider ? await config.provider.resolver(app) : config.provider;
1310
- return (ctx) => new AccessTokensGuard(name, ctx, emitter, provider);
1311
- }
1312
- };
653
+ return { async resolver(name, app) {
654
+ const emitter = await app.container.make("emitter");
655
+ const provider = "resolver" in config.provider ? await config.provider.resolver(app) : config.provider;
656
+ return (ctx) => new AccessTokensGuard(name, ctx, emitter, provider);
657
+ } };
1313
658
  }
1314
659
  function tokensUserProvider(config) {
1315
- return new AccessTokensLucidUserProvider(config);
660
+ return new AccessTokensLucidUserProvider(config);
1316
661
  }
1317
- export {
1318
- AccessToken,
1319
- AccessTokensGuard,
1320
- AccessTokensLucidUserProvider,
1321
- DbAccessTokensProvider,
1322
- tokensGuard,
1323
- tokensUserProvider
1324
- };
662
+ export { AccessToken, AccessTokensGuard, AccessTokensLucidUserProvider, DbAccessTokensProvider, tokensGuard, tokensUserProvider };