@maximemrf/adonisjs-jwt 0.8.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,7 +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';
4
+ import { validateGuardOptions } from './validation.js';
5
+ import { resolveDriver } from './driver_resolver.js';
5
6
  export class JwtGuard {
6
7
  #ctx;
7
8
  #userProvider;
@@ -9,7 +10,7 @@ export class JwtGuard {
9
10
  #options;
10
11
  #tokenName;
11
12
  #refreshTokenName = 'refreshToken';
12
- #jwksManager;
13
+ #driver;
13
14
  #cookieOptions;
14
15
  constructor(ctx, userProvider, option) {
15
16
  this.#ctx = ctx;
@@ -19,12 +20,45 @@ export class JwtGuard {
19
20
  if (!this.#options.content)
20
21
  this.#options.content = (user) => ({ userId: user.getId() });
21
22
  this.#tokenName = this.#options.tokenName ?? 'token';
22
- 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;
23
28
  this.#cookieOptions = {
24
29
  httpOnly: true,
25
30
  secure: true,
26
31
  ...this.#options.cookie,
27
32
  };
33
+ validateGuardOptions(this.#options, 'JwtGuard');
34
+ this.#driver = resolveDriver(this.#options, 'JwtGuard');
35
+ }
36
+ #signAccessToken(payload) {
37
+ return this.#driver.sign(payload, { expiresIn: this.#options.tokenExpiresIn });
38
+ }
39
+ async #verifyAccessToken(token) {
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;
53
+ }
54
+ }
55
+ if (this.#options.useCookies) {
56
+ const cookieToken = this.#ctx.request.cookie(this.#tokenName);
57
+ if (cookieToken) {
58
+ return cookieToken;
59
+ }
60
+ }
61
+ return undefined;
28
62
  }
29
63
  /**
30
64
  * A unique name for the guard driver
@@ -48,17 +82,14 @@ export class JwtGuard {
48
82
  * Generate a JWT token for a given user.
49
83
  */
50
84
  async generate(user) {
51
- if (this.#options.jwks) {
85
+ if (!this.#driver.canSign) {
52
86
  throw new errors.E_UNAUTHORIZED_ACCESS("You can't use the auth.generate method with jwks", {
53
87
  guardDriverName: this.driverName,
54
88
  });
55
89
  }
56
90
  const providerUser = await this.#userProvider.createUserForGuard(user);
57
- const token = jwt.sign(this.#options.content(providerUser), this.#options.secret, this.#options.expiresIn
58
- ? {
59
- expiresIn: this.#options.expiresIn,
60
- }
61
- : {});
91
+ const signPayload = this.#options.content(providerUser);
92
+ const token = this.#signAccessToken(signPayload);
62
93
  let refreshToken;
63
94
  if (this.#refreshTokenUserProvider) {
64
95
  const generatedRefreshToken = await this.#refreshTokenUserProvider.createToken(user, this.#options.refreshTokenAbilities ?? [], this.#options.refreshTokenExpiresIn
@@ -71,7 +102,7 @@ export class JwtGuard {
71
102
  if (this.#options.useCookies) {
72
103
  this.#ctx.response.cookie(`${this.#tokenName}`, token, {
73
104
  ...this.#cookieOptions,
74
- maxAge: this.#options.expiresIn,
105
+ maxAge: this.#options.tokenExpiresIn,
75
106
  });
76
107
  }
77
108
  if (this.#options.useCookiesForRefreshToken && refreshToken) {
@@ -83,7 +114,7 @@ export class JwtGuard {
83
114
  return {
84
115
  type: 'bearer',
85
116
  token: token,
86
- expiresIn: this.#options.expiresIn,
117
+ expiresIn: this.#options.tokenExpiresIn,
87
118
  refreshToken: refreshToken,
88
119
  refreshTokenExpiresIn: this.#options.refreshTokenExpiresIn,
89
120
  };
@@ -103,58 +134,39 @@ export class JwtGuard {
103
134
  }
104
135
  this.authenticationAttempted = true;
105
136
  /**
106
- * Try to read the token from the cookies.
107
- */
108
- let token = this.#ctx.request.cookie(`${this.#tokenName}`);
109
- /**
110
- * 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
111
138
  */
139
+ const token = this.#extractToken();
112
140
  if (!token) {
113
- /**
114
- * Ensure the auth header exists
115
- */
116
- const authHeader = this.#ctx.request.header('authorization');
117
- if (!authHeader) {
118
- throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
119
- guardDriverName: this.driverName,
120
- });
121
- }
122
- /**
123
- * Split the header value and read the token from it
124
- */
125
- ;
126
- [, token] = authHeader.split('Bearer ');
127
- if (!token) {
128
- throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
129
- guardDriverName: this.driverName,
130
- });
131
- }
141
+ throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
142
+ guardDriverName: this.driverName,
143
+ });
132
144
  }
133
145
  /**
134
146
  * Verify token
135
147
  */
136
148
  let payload;
137
149
  try {
138
- if (this.#jwksManager) {
139
- const decoded = jwt.decode(token, { complete: true });
140
- if (!decoded || !decoded.header || !decoded.header.kid) {
141
- throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
142
- guardDriverName: this.driverName,
143
- });
144
- }
145
- const key = await this.#jwksManager.getSigningKey(decoded.header.kid);
146
- payload = jwt.verify(token, key);
147
- }
148
- else {
149
- payload = jwt.verify(token, this.#options.secret);
150
- }
150
+ payload = await this.#verifyAccessToken(token);
151
151
  }
152
152
  catch {
153
153
  throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
154
154
  guardDriverName: this.driverName,
155
155
  });
156
156
  }
157
- 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) {
158
170
  throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
159
171
  guardDriverName: this.driverName,
160
172
  });
@@ -162,7 +174,7 @@ export class JwtGuard {
162
174
  /**
163
175
  * Fetch the user by user ID and save a reference to it
164
176
  */
165
- const providerUser = await this.#userProvider.findById(payload.userId);
177
+ const providerUser = await this.#userProvider.findById(userId);
166
178
  if (!providerUser) {
167
179
  throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
168
180
  guardDriverName: this.driverName,
@@ -175,7 +187,7 @@ export class JwtGuard {
175
187
  }
176
188
  async generateWithRefreshToken(refreshToken) {
177
189
  this.authenticationAttempted = true;
178
- if (this.#options.jwks) {
190
+ if (!this.#driver.canSign) {
179
191
  throw new errors.E_UNAUTHORIZED_ACCESS('JWKS is not supported for refresh token', {
180
192
  guardDriverName: this.driverName,
181
193
  });
@@ -194,6 +206,16 @@ export class JwtGuard {
194
206
  guardDriverName: this.driverName,
195
207
  });
196
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
+ }
197
219
  /**
198
220
  * Fetch the user by user ID
199
221
  */
@@ -203,10 +225,9 @@ export class JwtGuard {
203
225
  guardDriverName: this.driverName,
204
226
  });
205
227
  }
206
- this.isAuthenticated = true;
207
- this.user = providerUser.getOriginal();
208
228
  /**
209
- * 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
210
231
  */
211
232
  const isDeleted = await this.#refreshTokenUserProvider.invalidateToken(new Secret(refreshToken));
212
233
  if (!isDeleted) {
@@ -214,19 +235,35 @@ export class JwtGuard {
214
235
  guardDriverName: this.driverName,
215
236
  });
216
237
  }
217
- 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;
218
248
  }
219
249
  async #findRefreshToken() {
220
- const bodyToken = this.#ctx.request.input('refreshToken');
221
- if (bodyToken) {
222
- return bodyToken;
223
- }
250
+ /**
251
+ * The cookie is the configured transport, so it takes precedence
252
+ */
224
253
  if (this.#options.useCookiesForRefreshToken) {
225
254
  const cookieToken = this.#ctx.request.cookie(this.#refreshTokenName);
226
255
  if (cookieToken) {
227
256
  return cookieToken;
228
257
  }
229
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
+ }
230
267
  const authHeader = this.#ctx.request.header('authorization');
231
268
  if (authHeader) {
232
269
  if (authHeader.toLowerCase().startsWith('bearer ')) {
@@ -249,6 +286,7 @@ export class JwtGuard {
249
286
  guardDriverName: this.driverName,
250
287
  });
251
288
  }
289
+ this.#clearCookies();
252
290
  if (!refreshToken) {
253
291
  try {
254
292
  refreshToken = await this.#findRefreshToken();
@@ -259,6 +297,17 @@ export class JwtGuard {
259
297
  }
260
298
  await this.#refreshTokenUserProvider.invalidateToken(new Secret(refreshToken));
261
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
+ }
262
311
  /**
263
312
  * Same as authenticate, but does not throw an exception
264
313
  */
@@ -1,8 +1,9 @@
1
- import type { symbols } from '@adonisjs/auth';
1
+ import { type symbols } from '@adonisjs/auth';
2
2
  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,16 +42,101 @@ 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"];
52
+ /**
53
+ * Algorithms supported for asymmetric (RSA / ECDSA) JWT signing and verification.
54
+ * Use with `privateKey` + `publicKey` so access tokens can be verified elsewhere with only the public key.
55
+ */
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
+ };
44
66
  export type JwtGuardOptions<RealUser extends any = unknown> = {
45
- secret: string;
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;
72
+ /**
73
+ * Symmetric signing secret (HMAC). Used when asymmetric keys are not set.
74
+ */
75
+ secret?: string;
76
+ /**
77
+ * PEM-encoded private key for signing access tokens (asymmetric mode).
78
+ */
79
+ privateKey?: string;
80
+ /**
81
+ * PEM-encoded public key for verifying access tokens (asymmetric mode).
82
+ */
83
+ publicKey?: string;
84
+ /**
85
+ * Required with `privateKey` and `publicKey`.
86
+ */
87
+ algorithm?: JwtAsymmetricAlgorithm;
46
88
  jwks?: Options;
47
89
  refreshTokenUserProvider?: AccessTokensUserProviderContract<RealUser>;
48
90
  tokenName?: string;
49
- 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;
50
100
  refreshTokenExpiresIn?: number | StringValue;
51
101
  useCookies?: boolean;
52
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
+ */
53
107
  refreshTokenAbilities?: string[];
54
108
  cookie?: JwtCookieOptions;
55
- 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;
56
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,28 +1,29 @@
1
1
  {
2
2
  "name": "@maximemrf/adonisjs-jwt",
3
- "description": "Adonisjs v6 package for authentication with jwt token",
4
- "version": "0.8.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
+ "prepare": "tsc && npm run copy:templates",
26
27
  "clean": "del-cli build",
27
28
  "copy:templates": "copyfiles \"stubs/**/*.stub\" build",
28
29
  "typecheck": "tsc --noEmit",
@@ -39,43 +40,51 @@
39
40
  "prepublishOnly": "npm run build"
40
41
  },
41
42
  "devDependencies": {
42
- "@adonisjs/assembler": "^8.0.0",
43
- "@adonisjs/auth": "^10.0.0",
44
- "@adonisjs/core": "^7.0.1",
45
- "@adonisjs/eslint-config": "^3.0.0",
46
- "@adonisjs/lucid": "^22.0.0",
47
- "@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",
48
49
  "@adonisjs/tsconfig": "^2.0.0",
49
50
  "@japa/assert": "^4.2.0",
50
51
  "@japa/file-system": "^3.0.0",
51
52
  "@japa/runner": "^5.3.0",
52
53
  "@poppinss/ts-exec": "^1.4.4",
54
+ "@types/jsonwebtoken": "^9.0.10",
53
55
  "@types/nock": "^11.1.0",
54
- "@types/node": "^22.15.18",
55
- "c8": "^10.1.3",
56
+ "@types/node": "^24.19.2",
57
+ "c8": "^11.0.0",
56
58
  "copyfiles": "^2.4.1",
57
59
  "del-cli": "^7.0.0",
58
- "eslint": "^9.39.3",
60
+ "eslint": "^9.39.5",
59
61
  "luxon": "^3.7.2",
60
- "nock": "^14.0.11",
61
- "np": "^11.0.2",
62
- "prettier": "^3.8.1",
63
- "sqlite3": "^5.1.7",
62
+ "nock": "^14.0.16",
63
+ "np": "^11.2.2",
64
+ "prettier": "^3.9.6",
65
+ "sqlite3": "^6.0.1",
64
66
  "timekeeper": "^2.3.1",
65
- "typescript": "~5.9.3",
66
- "youch": "^4.1.0"
67
+ "typescript": "~6.0.3",
68
+ "youch": "^4.1.1"
67
69
  },
68
70
  "dependencies": {
69
- "@types/jsonwebtoken": "^9.0.10",
70
71
  "jsonwebtoken": "^9.0.3",
71
- "jwks-rsa": "^3.2.2",
72
+ "jwks-rsa": "^4.1.0",
72
73
  "ms": "^2.1.3"
73
74
  },
74
75
  "peerDependencies": {
76
+ "@adonisjs/auth": "^10.1.0",
75
77
  "@adonisjs/core": "^7.0.1"
76
78
  },
77
79
  "author": "MaximeMRF",
78
80
  "license": "MIT",
81
+ "repository": {
82
+ "type": "git",
83
+ "url": "git+https://github.com/MaximeMRF/adonisjs-jwt.git"
84
+ },
85
+ "bugs": {
86
+ "url": "https://github.com/MaximeMRF/adonisjs-jwt/issues"
87
+ },
79
88
  "keywords": [
80
89
  "adonisjs",
81
90
  "adonis",
@@ -108,4 +117,4 @@
108
117
  "tests/**"
109
118
  ]
110
119
  }
111
- }
120
+ }