@maximemrf/adonisjs-jwt 0.9.0 → 1.0.0-rc.1

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,8 +1,8 @@
1
1
  import { errors } from '@adonisjs/auth';
2
- import jwt from 'jsonwebtoken';
2
+ import { DEFAULT_TOKEN_EXPIRES_IN, } from './types.js';
3
3
  import { Secret } from '@adonisjs/core/helpers';
4
- import { JwksManager } from './jwks.js';
5
- import { createPrivateKey, createPublicKey } from 'node:crypto';
4
+ import { validateGuardOptions } from './validation.js';
5
+ import { resolveDriver } from './driver_resolver.js';
6
6
  export class JwtGuard {
7
7
  #ctx;
8
8
  #userProvider;
@@ -10,7 +10,7 @@ export class JwtGuard {
10
10
  #options;
11
11
  #tokenName;
12
12
  #refreshTokenName = 'refreshToken';
13
- #jwksManager;
13
+ #driver;
14
14
  #cookieOptions;
15
15
  constructor(ctx, userProvider, option) {
16
16
  this.#ctx = ctx;
@@ -20,85 +20,45 @@ export class JwtGuard {
20
20
  if (!this.#options.content)
21
21
  this.#options.content = (user) => ({ userId: user.getId() });
22
22
  this.#tokenName = this.#options.tokenName ?? 'token';
23
- this.#jwksManager = this.#options.jwks ? new JwksManager(this.#options.jwks) : undefined;
23
+ this.#refreshTokenName = this.#options.refreshTokenName ?? 'refreshToken';
24
+ /**
25
+ * Never issue access tokens without an expiration
26
+ */
27
+ this.#options.tokenExpiresIn = this.#options.tokenExpiresIn ?? DEFAULT_TOKEN_EXPIRES_IN;
24
28
  this.#cookieOptions = {
25
29
  httpOnly: true,
26
30
  secure: true,
27
31
  ...this.#options.cookie,
28
32
  };
29
- const asymmetricPartial = this.#options.privateKey !== undefined ||
30
- this.#options.publicKey !== undefined ||
31
- this.#options.algorithm !== undefined;
32
- if (asymmetricPartial && !this.#usesAsymmetric()) {
33
- throw new Error('JwtGuard asymmetric mode requires `privateKey`, `publicKey`, and `algorithm` to be set together');
34
- }
35
- if (this.#options.jwks && this.#usesAsymmetric()) {
36
- throw new Error('JwtGuard cannot use `jwks` together with asymmetric `privateKey` / `publicKey`');
37
- }
38
- if (!this.#options.jwks && !this.#usesAsymmetric() && !this.#options.secret) {
39
- throw new Error('JwtGuard requires `secret` (symmetric), or `privateKey` + `publicKey` + `algorithm` (asymmetric), or `jwks`');
40
- }
41
- if (this.#usesAsymmetric()) {
42
- this.#assertAsymmetricKeyMatchesAlgorithm();
43
- }
44
- }
45
- #usesAsymmetric() {
46
- return (this.#options.privateKey !== undefined &&
47
- this.#options.publicKey !== undefined &&
48
- this.#options.algorithm !== undefined);
49
- }
50
- #assertAsymmetricKeyMatchesAlgorithm() {
51
- const algorithm = this.#options.algorithm;
52
- const expectedKeyType = algorithm.startsWith('RS')
53
- ? 'rsa'
54
- : algorithm.startsWith('ES')
55
- ? 'ec'
56
- : null;
57
- if (!expectedKeyType) {
58
- throw new Error(`Unsupported asymmetric algorithm "${algorithm}"`);
59
- }
60
- try {
61
- const privateKey = createPrivateKey(this.#options.privateKey);
62
- const publicKey = createPublicKey(this.#options.publicKey);
63
- if (privateKey.asymmetricKeyType !== expectedKeyType) {
64
- throw new Error(`privateKey type "${privateKey.asymmetricKeyType}" does not match algorithm "${algorithm}"`);
65
- }
66
- if (publicKey.asymmetricKeyType !== expectedKeyType) {
67
- throw new Error(`publicKey type "${publicKey.asymmetricKeyType}" does not match algorithm "${algorithm}"`);
68
- }
69
- }
70
- catch (error) {
71
- const message = error instanceof Error ? error.message : 'Invalid asymmetric key configuration';
72
- throw new Error(`JwtGuard asymmetric key validation failed: ${message}`);
73
- }
33
+ validateGuardOptions(this.#options, 'JwtGuard');
34
+ this.#driver = resolveDriver(this.#options, 'JwtGuard');
74
35
  }
75
36
  #signAccessToken(payload) {
76
- const expires = this.#options.expiresIn ? { expiresIn: this.#options.expiresIn } : {};
77
- if (this.#usesAsymmetric()) {
78
- return jwt.sign(payload, this.#options.privateKey, {
79
- ...expires,
80
- algorithm: this.#options.algorithm,
81
- });
82
- }
83
- return jwt.sign(payload, this.#options.secret, expires);
37
+ return this.#driver.sign(payload, { expiresIn: this.#options.tokenExpiresIn });
84
38
  }
85
39
  async #verifyAccessToken(token) {
86
- if (this.#jwksManager) {
87
- const decoded = jwt.decode(token, { complete: true });
88
- if (!decoded || !decoded.header || !decoded.header.kid) {
89
- throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
90
- guardDriverName: this.driverName,
91
- });
40
+ return this.#driver.verify(token);
41
+ }
42
+ #extractToken() {
43
+ /**
44
+ * The header is set explicitly by the client, so it takes precedence
45
+ * over the cookie, which the browser sends on every request (a stale
46
+ * cookie must not shadow a valid bearer token)
47
+ */
48
+ const authHeader = this.#ctx.request.header('authorization');
49
+ if (authHeader && authHeader.toLowerCase().startsWith('bearer ')) {
50
+ const headerToken = authHeader.slice(7).trim();
51
+ if (headerToken) {
52
+ return headerToken;
92
53
  }
93
- const key = await this.#jwksManager.getSigningKey(decoded.header.kid);
94
- return jwt.verify(token, key);
95
54
  }
96
- if (this.#usesAsymmetric()) {
97
- return jwt.verify(token, this.#options.publicKey, {
98
- algorithms: [this.#options.algorithm],
99
- });
55
+ if (this.#options.useCookies) {
56
+ const cookieToken = this.#ctx.request.cookie(this.#tokenName);
57
+ if (cookieToken) {
58
+ return cookieToken;
59
+ }
100
60
  }
101
- return jwt.verify(token, this.#options.secret);
61
+ return undefined;
102
62
  }
103
63
  /**
104
64
  * A unique name for the guard driver
@@ -122,7 +82,7 @@ export class JwtGuard {
122
82
  * Generate a JWT token for a given user.
123
83
  */
124
84
  async generate(user) {
125
- if (this.#options.jwks) {
85
+ if (!this.#driver.canSign) {
126
86
  throw new errors.E_UNAUTHORIZED_ACCESS("You can't use the auth.generate method with jwks", {
127
87
  guardDriverName: this.driverName,
128
88
  });
@@ -142,7 +102,7 @@ export class JwtGuard {
142
102
  if (this.#options.useCookies) {
143
103
  this.#ctx.response.cookie(`${this.#tokenName}`, token, {
144
104
  ...this.#cookieOptions,
145
- maxAge: this.#options.expiresIn,
105
+ maxAge: this.#options.tokenExpiresIn,
146
106
  });
147
107
  }
148
108
  if (this.#options.useCookiesForRefreshToken && refreshToken) {
@@ -154,7 +114,7 @@ export class JwtGuard {
154
114
  return {
155
115
  type: 'bearer',
156
116
  token: token,
157
- expiresIn: this.#options.expiresIn,
117
+ expiresIn: this.#options.tokenExpiresIn,
158
118
  refreshToken: refreshToken,
159
119
  refreshTokenExpiresIn: this.#options.refreshTokenExpiresIn,
160
120
  };
@@ -174,32 +134,13 @@ export class JwtGuard {
174
134
  }
175
135
  this.authenticationAttempted = true;
176
136
  /**
177
- * Try to read the token from the cookies.
178
- */
179
- let token = this.#ctx.request.cookie(`${this.#tokenName}`);
180
- /**
181
- * If token is missing on cookies, then try to read it from the header authorization
137
+ * Read the token from the Authorization header or the cookie
182
138
  */
139
+ const token = this.#extractToken();
183
140
  if (!token) {
184
- /**
185
- * Ensure the auth header exists
186
- */
187
- const authHeader = this.#ctx.request.header('authorization');
188
- if (!authHeader) {
189
- throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
190
- guardDriverName: this.driverName,
191
- });
192
- }
193
- /**
194
- * Split the header value and read the token from it
195
- */
196
- ;
197
- [, token] = authHeader.split('Bearer ');
198
- if (!token) {
199
- throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
200
- guardDriverName: this.driverName,
201
- });
202
- }
141
+ throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
142
+ guardDriverName: this.driverName,
143
+ });
203
144
  }
204
145
  /**
205
146
  * Verify token
@@ -213,7 +154,19 @@ export class JwtGuard {
213
154
  guardDriverName: this.driverName,
214
155
  });
215
156
  }
216
- if (!payload || typeof payload !== 'object' || !('userId' in payload)) {
157
+ if (!payload || typeof payload !== 'object') {
158
+ throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
159
+ guardDriverName: this.driverName,
160
+ });
161
+ }
162
+ if (this.#options.verifyPayload && !(await this.#options.verifyPayload(payload))) {
163
+ throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
164
+ guardDriverName: this.driverName,
165
+ });
166
+ }
167
+ const getUserId = this.#options.getUserId ?? ((claims) => claims.userId);
168
+ const userId = getUserId(payload);
169
+ if (userId === undefined || userId === null) {
217
170
  throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
218
171
  guardDriverName: this.driverName,
219
172
  });
@@ -221,7 +174,7 @@ export class JwtGuard {
221
174
  /**
222
175
  * Fetch the user by user ID and save a reference to it
223
176
  */
224
- const providerUser = await this.#userProvider.findById(payload.userId);
177
+ const providerUser = await this.#userProvider.findById(userId);
225
178
  if (!providerUser) {
226
179
  throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
227
180
  guardDriverName: this.driverName,
@@ -234,7 +187,7 @@ export class JwtGuard {
234
187
  }
235
188
  async generateWithRefreshToken(refreshToken) {
236
189
  this.authenticationAttempted = true;
237
- if (this.#options.jwks) {
190
+ if (!this.#driver.canSign) {
238
191
  throw new errors.E_UNAUTHORIZED_ACCESS('JWKS is not supported for refresh token', {
239
192
  guardDriverName: this.driverName,
240
193
  });
@@ -253,6 +206,16 @@ export class JwtGuard {
253
206
  guardDriverName: this.driverName,
254
207
  });
255
208
  }
209
+ /**
210
+ * When refresh token abilities are configured, reject tokens that
211
+ * don't have all of them (e.g. an API token from another provider)
212
+ */
213
+ const requiredAbilities = this.#options.refreshTokenAbilities ?? [];
214
+ if (!requiredAbilities.every((ability) => accessToken.allows(ability))) {
215
+ throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
216
+ guardDriverName: this.driverName,
217
+ });
218
+ }
256
219
  /**
257
220
  * Fetch the user by user ID
258
221
  */
@@ -262,10 +225,9 @@ export class JwtGuard {
262
225
  guardDriverName: this.driverName,
263
226
  });
264
227
  }
265
- this.isAuthenticated = true;
266
- this.user = providerUser.getOriginal();
267
228
  /**
268
- * Delete the refresh token from the database
229
+ * Delete the refresh token from the database. Only one of two concurrent
230
+ * requests using the same refresh token can delete it
269
231
  */
270
232
  const isDeleted = await this.#refreshTokenUserProvider.invalidateToken(new Secret(refreshToken));
271
233
  if (!isDeleted) {
@@ -273,19 +235,35 @@ export class JwtGuard {
273
235
  guardDriverName: this.driverName,
274
236
  });
275
237
  }
276
- return this.generate(this.user);
238
+ const user = providerUser.getOriginal();
239
+ const result = await this.generate(user);
240
+ /**
241
+ * Like after `authenticate()`, `currentToken` holds the access token
242
+ * of the request, here the one that has just been issued
243
+ */
244
+ this.isAuthenticated = true;
245
+ this.user = user;
246
+ this.user.currentToken = result.token;
247
+ return result;
277
248
  }
278
249
  async #findRefreshToken() {
279
- const bodyToken = this.#ctx.request.input('refreshToken');
280
- if (bodyToken) {
281
- return bodyToken;
282
- }
250
+ /**
251
+ * The cookie is the configured transport, so it takes precedence
252
+ */
283
253
  if (this.#options.useCookiesForRefreshToken) {
284
254
  const cookieToken = this.#ctx.request.cookie(this.#refreshTokenName);
285
255
  if (cookieToken) {
286
256
  return cookieToken;
287
257
  }
288
258
  }
259
+ /**
260
+ * Only read the request body: a refresh token passed in the
261
+ * query string would end up in access logs and Referer headers
262
+ */
263
+ const bodyToken = this.#ctx.request.body()[this.#refreshTokenName];
264
+ if (typeof bodyToken === 'string' && bodyToken) {
265
+ return bodyToken;
266
+ }
289
267
  const authHeader = this.#ctx.request.header('authorization');
290
268
  if (authHeader) {
291
269
  if (authHeader.toLowerCase().startsWith('bearer ')) {
@@ -308,6 +286,7 @@ export class JwtGuard {
308
286
  guardDriverName: this.driverName,
309
287
  });
310
288
  }
289
+ this.#clearCookies();
311
290
  if (!refreshToken) {
312
291
  try {
313
292
  refreshToken = await this.#findRefreshToken();
@@ -318,6 +297,17 @@ export class JwtGuard {
318
297
  }
319
298
  await this.#refreshTokenUserProvider.invalidateToken(new Secret(refreshToken));
320
299
  }
300
+ /**
301
+ * Remove the access and refresh token cookies set by the guard
302
+ */
303
+ #clearCookies() {
304
+ if (this.#options.useCookies) {
305
+ this.#ctx.response.clearCookie(this.#tokenName, this.#cookieOptions);
306
+ }
307
+ if (this.#options.useCookiesForRefreshToken) {
308
+ this.#ctx.response.clearCookie(this.#refreshTokenName, this.#cookieOptions);
309
+ }
310
+ }
321
311
  /**
322
312
  * Same as authenticate, but does not throw an exception
323
313
  */
@@ -3,6 +3,7 @@ import type { StringValue } from 'ms';
3
3
  import type { AccessTokensUserProviderContract } from '@adonisjs/auth/types/access_tokens';
4
4
  import type { Options } from 'jwks-rsa';
5
5
  import type { CookieOptions } from '@adonisjs/core/types/http';
6
+ import type { JwtDriver } from './drivers/types.js';
6
7
  export type JwtCookieOptions = Omit<Partial<CookieOptions>, 'maxAge' | 'expires'>;
7
8
  /**
8
9
  * The bridge between the User provider and the
@@ -41,12 +42,33 @@ export interface JwtUserProviderContract<RealUser> {
41
42
  export type BaseJwtContent = {
42
43
  userId: string | number | BigInt;
43
44
  };
45
+ /**
46
+ * Lifetime of access tokens when `tokenExpiresIn` is not configured
47
+ */
48
+ export declare const DEFAULT_TOKEN_EXPIRES_IN = "1h";
49
+ export declare const ALLOWED_SYMMETRIC_ALGORITHMS: readonly ["HS256", "HS384", "HS512"];
50
+ export type JwtSymmetricAlgorithm = (typeof ALLOWED_SYMMETRIC_ALGORITHMS)[number];
51
+ export declare const ALLOWED_ASYMMETRIC_ALGORITHMS: readonly ["RS256", "RS384", "RS512", "ES256", "ES384", "ES512"];
44
52
  /**
45
53
  * Algorithms supported for asymmetric (RSA / ECDSA) JWT signing and verification.
46
54
  * Use with `privateKey` + `publicKey` so access tokens can be verified elsewhere with only the public key.
47
55
  */
48
- export type JwtAsymmetricAlgorithm = 'RS256' | 'RS384' | 'RS512' | 'ES256' | 'ES384' | 'ES512';
56
+ export type JwtAsymmetricAlgorithm = (typeof ALLOWED_ASYMMETRIC_ALGORITHMS)[number];
57
+ export declare const ALLOWED_JWKS_ALGORITHMS: readonly ["RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "PS256", "PS384", "PS512"];
58
+ export type JwtJwksAlgorithm = (typeof ALLOWED_JWKS_ALGORITHMS)[number];
59
+ export type JwtGenerateResult = {
60
+ type: string;
61
+ token: string;
62
+ expiresIn?: number | StringValue;
63
+ refreshToken?: string;
64
+ refreshTokenExpiresIn?: number | StringValue;
65
+ };
49
66
  export type JwtGuardOptions<RealUser extends any = unknown> = {
67
+ /**
68
+ * Driver resolved by `jwtGuard()`. Not part of the public API: custom
69
+ * drivers are not supported and the `JwtDriver` interface may change.
70
+ */
71
+ driver?: JwtDriver;
50
72
  /**
51
73
  * Symmetric signing secret (HMAC). Used when asymmetric keys are not set.
52
74
  */
@@ -66,11 +88,55 @@ export type JwtGuardOptions<RealUser extends any = unknown> = {
66
88
  jwks?: Options;
67
89
  refreshTokenUserProvider?: AccessTokensUserProviderContract<RealUser>;
68
90
  tokenName?: string;
69
- expiresIn?: number | StringValue;
91
+ /**
92
+ * Name of the refresh token cookie and request body field.
93
+ * Defaults to `refreshToken`.
94
+ */
95
+ refreshTokenName?: string;
96
+ /**
97
+ * Lifetime of access tokens. Defaults to `DEFAULT_TOKEN_EXPIRES_IN`.
98
+ */
99
+ tokenExpiresIn?: number | StringValue;
70
100
  refreshTokenExpiresIn?: number | StringValue;
71
101
  useCookies?: boolean;
72
102
  useCookiesForRefreshToken?: boolean;
103
+ /**
104
+ * Abilities given to refresh tokens. When set, `generateWithRefreshToken`
105
+ * rejects refresh tokens that don't have all of them.
106
+ */
73
107
  refreshTokenAbilities?: string[];
74
108
  cookie?: JwtCookieOptions;
75
- content?: (user: JwtGuardUser<RealUser>) => Record<string, any>;
109
+ content?: (user: JwtGuardUser<RealUser>) => Record<string, any> & BaseJwtContent;
110
+ /**
111
+ * Expected JWT issuer (iss). Validated during token verification.
112
+ * Required in JWKS mode.
113
+ */
114
+ issuer?: string;
115
+ /**
116
+ * Expected JWT audience (aud). Validated during token verification.
117
+ * Required in JWKS mode, unless `verifyPayload` is set.
118
+ */
119
+ audience?: string | string[];
120
+ /**
121
+ * Algorithms accepted when verifying tokens in JWKS mode.
122
+ * Must be a non-empty subset of `ALLOWED_JWKS_ALGORITHMS` (default: all of them).
123
+ */
124
+ algorithms?: JwtJwksAlgorithm[];
125
+ /**
126
+ * Number of seconds of clock skew tolerated when checking `exp` / `nbf`.
127
+ */
128
+ clockTolerance?: number;
129
+ /**
130
+ * Extract the user id from a verified token payload.
131
+ * Defaults to `payload.userId`. Use e.g. `(payload) => payload.sub` for external providers.
132
+ */
133
+ getUserId?: JwtGetUserId;
134
+ /**
135
+ * Extra check on a verified token payload, run before looking up the user.
136
+ * Return `false` to reject the token. Use it to check claims other than
137
+ * `iss` and `aud`, e.g. `client_id` and `token_use` for Amazon Cognito.
138
+ */
139
+ verifyPayload?: JwtVerifyPayload;
76
140
  };
141
+ export type JwtVerifyPayload = (payload: Record<string, any>) => boolean | Promise<boolean>;
142
+ export type JwtGetUserId = (payload: Record<string, any>) => string | number | BigInt | undefined | null;
@@ -1 +1,19 @@
1
- export {};
1
+ /**
2
+ * Lifetime of access tokens when `tokenExpiresIn` is not configured
3
+ */
4
+ export const DEFAULT_TOKEN_EXPIRES_IN = '1h';
5
+ export const ALLOWED_SYMMETRIC_ALGORITHMS = ['HS256', 'HS384', 'HS512'];
6
+ export const ALLOWED_ASYMMETRIC_ALGORITHMS = [
7
+ 'RS256',
8
+ 'RS384',
9
+ 'RS512',
10
+ 'ES256',
11
+ 'ES384',
12
+ 'ES512',
13
+ ];
14
+ export const ALLOWED_JWKS_ALGORITHMS = [
15
+ ...ALLOWED_ASYMMETRIC_ALGORITHMS,
16
+ 'PS256',
17
+ 'PS384',
18
+ 'PS512',
19
+ ];
@@ -0,0 +1,2 @@
1
+ import type { JwtGuardOptions } from './types.js';
2
+ export declare function validateGuardOptions(options: JwtGuardOptions, prefix?: string): void;
@@ -0,0 +1,24 @@
1
+ export function validateGuardOptions(options, prefix = 'JwtGuard') {
2
+ if (typeof options.tokenExpiresIn === 'number' &&
3
+ !(Number.isFinite(options.tokenExpiresIn) && options.tokenExpiresIn > 0)) {
4
+ throw new Error(`${prefix} \`tokenExpiresIn\` must be a positive number of seconds`);
5
+ }
6
+ if (options.driver) {
7
+ return;
8
+ }
9
+ const asymmetricPartial = options.privateKey !== undefined ||
10
+ options.publicKey !== undefined ||
11
+ options.algorithm !== undefined;
12
+ const usesAsymmetric = options.privateKey !== undefined &&
13
+ options.publicKey !== undefined &&
14
+ options.algorithm !== undefined;
15
+ if (asymmetricPartial && !usesAsymmetric) {
16
+ throw new Error(`${prefix} asymmetric mode requires \`privateKey\`, \`publicKey\`, and \`algorithm\` to be set together`);
17
+ }
18
+ if (options.jwks && usesAsymmetric) {
19
+ throw new Error(`${prefix} cannot use \`jwks\` together with asymmetric \`privateKey\` / \`publicKey\``);
20
+ }
21
+ if (!options.jwks && !usesAsymmetric && !options.secret) {
22
+ throw new Error(`${prefix} requires \`secret\` (symmetric), or \`privateKey\` + \`publicKey\` + \`algorithm\` (asymmetric), or \`jwks\``);
23
+ }
24
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Path to the root directory where stubs are stored.
3
+ */
4
+ export declare const stubsRoot: string;
@@ -0,0 +1,5 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ /**
3
+ * Path to the root directory where stubs are stored.
4
+ */
5
+ export const stubsRoot = fileURLToPath(new URL('./', import.meta.url));
@@ -0,0 +1,35 @@
1
+ {{{
2
+ exports({
3
+ to: app.makePath('database/migrations', entity.filename)
4
+ })
5
+ }}}
6
+ import { BaseSchema } from '@adonisjs/lucid/schema'
7
+
8
+ export default class extends BaseSchema {
9
+ protected tableName = 'jwt_refresh_tokens'
10
+
11
+ async up() {
12
+ this.schema.createTable(this.tableName, (table) => {
13
+ table.increments()
14
+ table
15
+ .integer('tokenable_id')
16
+ .notNullable()
17
+ .unsigned()
18
+ .references('id')
19
+ .inTable('users')
20
+ .onDelete('CASCADE')
21
+ table.string('type').notNullable()
22
+ table.string('name').nullable()
23
+ table.string('hash', 80).notNullable()
24
+ table.text('abilities').notNullable()
25
+ table.timestamp('created_at', { precision: 6, useTz: true }).notNullable()
26
+ table.timestamp('updated_at', { precision: 6, useTz: true }).notNullable()
27
+ table.timestamp('expires_at', { precision: 6, useTz: true }).nullable()
28
+ table.timestamp('last_used_at', { precision: 6, useTz: true }).nullable()
29
+ })
30
+ }
31
+
32
+ async down() {
33
+ this.schema.dropTable(this.tableName)
34
+ }
35
+ }
package/package.json CHANGED
@@ -1,26 +1,26 @@
1
1
  {
2
2
  "name": "@maximemrf/adonisjs-jwt",
3
- "description": "Adonisjs v6 package for authentication with jwt token",
4
- "version": "0.9.0",
3
+ "description": "AdonisJS package for authentication with JWT tokens",
4
+ "version": "1.0.0-rc.1",
5
5
  "engines": {
6
- "node": ">=20.6.0"
6
+ "node": ">=24.0.0"
7
7
  },
8
8
  "type": "module",
9
+ "main": "./build/index.js",
10
+ "types": "./build/index.d.ts",
9
11
  "files": [
10
12
  "build/src",
11
13
  "build/stubs",
12
14
  "build/index.d.ts",
13
- "build/index.js"
15
+ "build/index.js",
16
+ "build/configure.d.ts",
17
+ "build/configure.js"
14
18
  ],
15
19
  "exports": {
16
20
  ".": "./build/index.js",
17
21
  "./types": "./build/src/types.js",
18
- "./jwt_config": "./build/src/define_config.js"
19
- },
20
- "imports": {
21
- "#generated/*": "./.adonisjs/server/*.js",
22
- "#transformers/*": "./app/transformers/*.js",
23
- "#database/*": "./database/*.js"
22
+ "./jwt_config": "./build/src/define_config.js",
23
+ "./stubs": "./build/stubs/main.js"
24
24
  },
25
25
  "scripts": {
26
26
  "prepare": "tsc && npm run copy:templates",
@@ -40,39 +40,40 @@
40
40
  "prepublishOnly": "npm run build"
41
41
  },
42
42
  "devDependencies": {
43
- "@adonisjs/assembler": "^8.2.0",
44
- "@adonisjs/auth": "^10.0.0",
45
- "@adonisjs/core": "^7.1.1",
46
- "@adonisjs/eslint-config": "^3.0.0",
47
- "@adonisjs/lucid": "^22.2.0",
48
- "@adonisjs/prettier-config": "^1.4.5",
43
+ "@adonisjs/assembler": "^8.4.0",
44
+ "@adonisjs/auth": "^10.1.0",
45
+ "@adonisjs/core": "^7.3.5",
46
+ "@adonisjs/eslint-config": "^3.1.0",
47
+ "@adonisjs/lucid": "^22.4.2",
48
+ "@adonisjs/prettier-config": "^1.5.0",
49
49
  "@adonisjs/tsconfig": "^2.0.0",
50
50
  "@japa/assert": "^4.2.0",
51
51
  "@japa/file-system": "^3.0.0",
52
52
  "@japa/runner": "^5.3.0",
53
53
  "@poppinss/ts-exec": "^1.4.4",
54
+ "@types/jsonwebtoken": "^9.0.10",
54
55
  "@types/nock": "^11.1.0",
55
- "@types/node": "^22.15.18",
56
+ "@types/node": "^24.19.2",
56
57
  "c8": "^11.0.0",
57
58
  "copyfiles": "^2.4.1",
58
59
  "del-cli": "^7.0.0",
59
- "eslint": "^9.39.4",
60
+ "eslint": "^9.39.5",
60
61
  "luxon": "^3.7.2",
61
- "nock": "^14.0.11",
62
- "np": "^11.0.2",
63
- "prettier": "^3.8.1",
62
+ "nock": "^14.0.16",
63
+ "np": "^11.2.2",
64
+ "prettier": "^3.9.6",
64
65
  "sqlite3": "^6.0.1",
65
66
  "timekeeper": "^2.3.1",
66
- "typescript": "~5.9.3",
67
- "youch": "^4.1.0"
67
+ "typescript": "~6.0.3",
68
+ "youch": "^4.1.1"
68
69
  },
69
70
  "dependencies": {
70
- "@types/jsonwebtoken": "^9.0.10",
71
71
  "jsonwebtoken": "^9.0.3",
72
- "jwks-rsa": "^4.0.1",
72
+ "jwks-rsa": "^4.1.0",
73
73
  "ms": "^2.1.3"
74
74
  },
75
75
  "peerDependencies": {
76
+ "@adonisjs/auth": "^10.1.0",
76
77
  "@adonisjs/core": "^7.0.1"
77
78
  },
78
79
  "author": "MaximeMRF",
@@ -116,4 +117,4 @@
116
117
  "tests/**"
117
118
  ]
118
119
  }
119
- }
120
+ }