@maximemrf/adonisjs-jwt 0.7.0-alpha.2 → 0.7.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.
package/README.md CHANGED
@@ -79,6 +79,11 @@ const authConfig = defineConfig({
79
79
  refreshTokenExpiresIn: '7d',
80
80
  // ability to separate cookie usage for refresh token
81
81
  useCookiesForRefreshToken: true,
82
+ // ability to configure the cookies options
83
+ cookie: {
84
+ httpOnly: true,
85
+ secure: true,
86
+ },
82
87
  // limit the abilities of the refresh token
83
88
  refreshTokenAbilities: ['refresh_token'],
84
89
  // content is a function that takes the user and returns the content of the token, it can be optional, by default it returns only the user id
@@ -116,6 +121,40 @@ useCookies: true
116
121
 
117
122
  If you just want to use jwt with the bearer token no need to set `useCookies` to `false` you can just remove it.
118
123
 
124
+ You can also pass options to the cookies. Note that `maxAge` and `expires` are omitted from the options because they are automatically handled by the package based on the token validity (`tokenExpiresIn` and `refreshTokenExpiresIn`).
125
+ By default, `httpOnly: true` and `secure: true` are enforced for better security, but you can override them using the `cookie` object configuration.
126
+
127
+ ```typescript
128
+ cookie: {
129
+ httpOnly: true, // Default
130
+ secure: true, // Default
131
+ path: '/',
132
+ sameSite: 'lax',
133
+ // maxAge and expires cannot be set here
134
+ }
135
+ ```
136
+
137
+ ## JWKS
138
+
139
+ You can use JWKS to verify the token by setting the `jwks` option in the guard configuration.
140
+
141
+ ```typescript
142
+ // ...
143
+ jwt: jwtGuard({
144
+ // ...
145
+ jwks: {
146
+ jwksUri: 'https://your-auth-server/.well-known/jwks.json',
147
+ // you can pass any options accepted by jwks-rsa package
148
+ cache: true,
149
+ rateLimit: true,
150
+ },
151
+ }),
152
+ // ...
153
+ ```
154
+
155
+ > [!WARNING]
156
+ > If you enable JWKS, you cannot use the `auth.use('jwt').generate(user)` and `auth.use('jwt').generateWithRefreshToken()` method because the token is signed by an external provider. You can only use the `authenticate` (or `check` / `getUserOrFail`) method to verify the token.
157
+
119
158
  ## Refresh Tokens
120
159
 
121
160
  To use refresh tokens, you have to set the `refreshTokenUserProvider` in the guard configuration, see the example above.
@@ -1,6 +1,6 @@
1
1
  import { GuardConfigProvider } from '@adonisjs/auth/types';
2
2
  import type { HttpContext } from '@adonisjs/core/http';
3
- import { JwtGuardUser, JwtUserProviderContract } from './types.js';
3
+ import { JwtGuardUser, JwtUserProviderContract, JwtCookieOptions } from './types.js';
4
4
  import { JwtGuard } from './jwt.js';
5
5
  import type { StringValue } from 'ms';
6
6
  import { AccessTokensUserProviderContract } from '@adonisjs/auth/types/access_tokens';
@@ -17,4 +17,5 @@ export declare function jwtGuard<UserProvider extends JwtUserProviderContract<un
17
17
  secret?: string;
18
18
  content: <T>(user: JwtGuardUser<T>) => Record<string | number, any>;
19
19
  jwks?: Options;
20
+ cookie?: JwtCookieOptions;
20
21
  }): GuardConfigProvider<(ctx: HttpContext) => JwtGuard<UserProvider>>;
@@ -1,5 +1,4 @@
1
1
  import { JwtGuard } from './jwt.js';
2
- import { JwksManager } from './jwks.js';
3
2
  export function jwtGuard(config) {
4
3
  return {
5
4
  async resolver(_, app) {
@@ -15,7 +14,7 @@ export function jwtGuard(config) {
15
14
  refreshTokenAbilities: config.refreshTokenAbilities,
16
15
  content: config.content,
17
16
  jwks: config.jwks,
18
- jwksClient: config.jwks ? new JwksManager(config.jwks).getClient() : undefined,
17
+ cookie: config.cookie,
19
18
  };
20
19
  return (ctx) => new JwtGuard(ctx, config.provider, options);
21
20
  },
package/build/src/jwt.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { errors } from '@adonisjs/auth';
2
2
  import jwt from 'jsonwebtoken';
3
3
  import { Secret } from '@adonisjs/core/helpers';
4
+ import { JwksManager } from './jwks.js';
4
5
  export class JwtGuard {
5
6
  #ctx;
6
7
  #userProvider;
@@ -9,6 +10,7 @@ export class JwtGuard {
9
10
  #tokenName;
10
11
  #refreshTokenName = 'refreshToken';
11
12
  #jwksManager;
13
+ #cookieOptions;
12
14
  constructor(ctx, userProvider, option) {
13
15
  this.#ctx = ctx;
14
16
  this.#userProvider = userProvider;
@@ -17,7 +19,12 @@ export class JwtGuard {
17
19
  if (!this.#options.content)
18
20
  this.#options.content = (user) => ({ userId: user.getId() });
19
21
  this.#tokenName = this.#options.tokenName ?? 'token';
20
- this.#jwksManager = this.#options.jwksManager;
22
+ this.#jwksManager = this.#options.jwks ? new JwksManager(this.#options.jwks) : undefined;
23
+ this.#cookieOptions = {
24
+ httpOnly: true,
25
+ secure: true,
26
+ ...this.#options.cookie,
27
+ };
21
28
  }
22
29
  /**
23
30
  * A unique name for the guard driver
@@ -41,6 +48,11 @@ export class JwtGuard {
41
48
  * Generate a JWT token for a given user.
42
49
  */
43
50
  async generate(user) {
51
+ if (this.#options.jwks) {
52
+ throw new errors.E_UNAUTHORIZED_ACCESS("You can't use the auth.generate method with jwks", {
53
+ guardDriverName: this.driverName,
54
+ });
55
+ }
44
56
  const providerUser = await this.#userProvider.createUserForGuard(user);
45
57
  const token = jwt.sign(this.#options.content(providerUser), this.#options.secret, this.#options.expiresIn
46
58
  ? {
@@ -58,12 +70,14 @@ export class JwtGuard {
58
70
  }
59
71
  if (this.#options.useCookies) {
60
72
  this.#ctx.response.cookie(`${this.#tokenName}`, token, {
61
- httpOnly: true,
73
+ ...this.#cookieOptions,
74
+ maxAge: this.#options.expiresIn,
62
75
  });
63
76
  }
64
77
  if (this.#options.useCookiesForRefreshToken && refreshToken) {
65
78
  this.#ctx.response.cookie(`${this.#refreshTokenName}`, refreshToken, {
66
- httpOnly: true,
79
+ ...this.#cookieOptions,
80
+ maxAge: this.#options.refreshTokenExpiresIn,
67
81
  });
68
82
  }
69
83
  return {
@@ -88,17 +102,10 @@ export class JwtGuard {
88
102
  return this.getUserOrFail();
89
103
  }
90
104
  this.authenticationAttempted = true;
91
- const cookieHeader = this.#ctx.request.request.headers.cookie;
92
- let token;
93
105
  /**
94
- * If cookies are enabled, then read the token from the cookies
106
+ * Try to read the token from the cookies.
95
107
  */
96
- if (cookieHeader) {
97
- const regex = new RegExp(`${this.#tokenName}=([^;]*)`);
98
- token =
99
- this.#ctx.request.cookie(`${this.#tokenName}`) ??
100
- (this.#ctx.request.request.headers.cookie.match(regex) || [])[1];
101
- }
108
+ let token = this.#ctx.request.cookie(`${this.#tokenName}`);
102
109
  /**
103
110
  * If token is missing on cookies, then try to read it from the header authorization
104
111
  */
@@ -142,7 +149,7 @@ export class JwtGuard {
142
149
  payload = jwt.verify(token, this.#options.secret);
143
150
  }
144
151
  }
145
- catch (error) {
152
+ catch {
146
153
  throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
147
154
  guardDriverName: this.driverName,
148
155
  });
@@ -168,6 +175,11 @@ export class JwtGuard {
168
175
  }
169
176
  async generateWithRefreshToken(refreshToken) {
170
177
  this.authenticationAttempted = true;
178
+ if (this.#options.jwks) {
179
+ throw new errors.E_UNAUTHORIZED_ACCESS('JWKS is not supported for refresh token', {
180
+ guardDriverName: this.driverName,
181
+ });
182
+ }
171
183
  if (!this.#refreshTokenUserProvider) {
172
184
  throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
173
185
  guardDriverName: this.driverName,
@@ -278,7 +290,7 @@ export class JwtGuard {
278
290
  const token = await this.generate(user);
279
291
  return {
280
292
  headers: {
281
- authorization: `Bearer ${this.#options.useCookies ? token : token.token}`,
293
+ authorization: `Bearer ${token.token}`,
282
294
  },
283
295
  };
284
296
  }
@@ -2,7 +2,8 @@ import { symbols } from '@adonisjs/auth';
2
2
  import type { StringValue } from 'ms';
3
3
  import { AccessTokensUserProviderContract } from '@adonisjs/auth/types/access_tokens';
4
4
  import { Options } from 'jwks-rsa';
5
- import { JwksManager } from './jwks.js';
5
+ import type { CookieOptions } from '@adonisjs/core/types/http';
6
+ export type JwtCookieOptions = Omit<Partial<CookieOptions>, 'maxAge' | 'expires'>;
6
7
  /**
7
8
  * The bridge between the User provider and the
8
9
  * Guard
@@ -43,7 +44,6 @@ export type BaseJwtContent = {
43
44
  export type JwtGuardOptions<RealUser extends any = unknown> = {
44
45
  secret: string;
45
46
  jwks?: Options;
46
- jwksManager?: JwksManager;
47
47
  refreshTokenUserProvider?: AccessTokensUserProviderContract<RealUser>;
48
48
  tokenName?: string;
49
49
  expiresIn?: number | StringValue;
@@ -51,5 +51,6 @@ export type JwtGuardOptions<RealUser extends any = unknown> = {
51
51
  useCookies?: boolean;
52
52
  useCookiesForRefreshToken?: boolean;
53
53
  refreshTokenAbilities?: string[];
54
+ cookie?: JwtCookieOptions;
54
55
  content?: (user: JwtGuardUser<RealUser>) => Record<string, any>;
55
56
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@maximemrf/adonisjs-jwt",
3
3
  "description": "Adonisjs v6 package for authentication with jwt token",
4
- "version": "0.7.0-alpha.2",
4
+ "version": "0.7.1",
5
5
  "engines": {
6
6
  "node": ">=20.6.0"
7
7
  },
@@ -35,8 +35,8 @@
35
35
  },
36
36
  "devDependencies": {
37
37
  "@adonisjs/assembler": "^7.8.2",
38
- "@adonisjs/auth": "^9.5.1",
39
- "@adonisjs/core": "^6.19.3",
38
+ "@adonisjs/auth": "^9.6.0",
39
+ "@adonisjs/core": "^6.20.0",
40
40
  "@adonisjs/eslint-config": "^2.1.2",
41
41
  "@adonisjs/lucid": "^21.8.2",
42
42
  "@adonisjs/prettier-config": "^1.4.5",
@@ -44,15 +44,17 @@
44
44
  "@japa/assert": "^4.2.0",
45
45
  "@japa/file-system": "^3.0.0",
46
46
  "@japa/runner": "^5.3.0",
47
- "@swc/core": "^1.15.8",
47
+ "@types/nock": "^11.1.0",
48
+ "@swc/core": "^1.15.11",
48
49
  "@types/node": "^22.15.18",
49
50
  "c8": "^10.1.3",
50
51
  "copyfiles": "^2.4.1",
51
52
  "del-cli": "^7.0.0",
52
53
  "eslint": "^9.39.2",
53
54
  "luxon": "^3.7.2",
54
- "np": "^10.3.0",
55
- "prettier": "^3.8.0",
55
+ "nock": "^14.0.11",
56
+ "np": "^11.0.2",
57
+ "prettier": "^3.8.1",
56
58
  "sqlite3": "^5.1.7",
57
59
  "timekeeper": "^2.3.1",
58
60
  "ts-node-maintained": "^10.9.6",
@@ -98,4 +100,4 @@
98
100
  "tests/**"
99
101
  ]
100
102
  }
101
- }
103
+ }