@maximemrf/adonisjs-jwt 1.0.0-rc.7 → 1.0.0-rc.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/src/drivers/asymmetric.js +16 -2
- package/build/src/guard.d.ts +6 -4
- package/build/src/guard.js +32 -6
- package/package.json +1 -1
|
@@ -27,8 +27,8 @@ export class AsymmetricDriver {
|
|
|
27
27
|
if (!expectedKeyType) {
|
|
28
28
|
throw new Error(`Unsupported asymmetric algorithm "${algorithm}"`);
|
|
29
29
|
}
|
|
30
|
-
const privateKey = createPrivateKey
|
|
31
|
-
const publicKey = createPublicKey
|
|
30
|
+
const privateKey = parseKey(createPrivateKey, this.#privateKey, 'privateKey');
|
|
31
|
+
const publicKey = parseKey(createPublicKey, this.#publicKey, 'publicKey');
|
|
32
32
|
if (privateKey.asymmetricKeyType !== expectedKeyType) {
|
|
33
33
|
throw new Error(`privateKey type "${privateKey.asymmetricKeyType}" does not match algorithm "${algorithm}"`);
|
|
34
34
|
}
|
|
@@ -42,6 +42,12 @@ export class AsymmetricDriver {
|
|
|
42
42
|
if (!createPublicKey(privateKey).equals(publicKey)) {
|
|
43
43
|
throw new Error('publicKey does not belong to the same key pair as privateKey');
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* The checks above do not cover everything jsonwebtoken requires (the
|
|
47
|
+
* curve of ES256/384/512, RSA keys of at least 2048 bits). Sign a token
|
|
48
|
+
* now, so a bad key fails at boot instead of every login.
|
|
49
|
+
*/
|
|
50
|
+
jwt.sign({}, this.#privateKey, { algorithm, expiresIn: 60 });
|
|
45
51
|
}
|
|
46
52
|
sign(payload, options) {
|
|
47
53
|
const signOptions = {
|
|
@@ -62,3 +68,11 @@ export class AsymmetricDriver {
|
|
|
62
68
|
return jwt.verify(token, this.#publicKey, verifyOptions);
|
|
63
69
|
}
|
|
64
70
|
}
|
|
71
|
+
function parseKey(parse, key, name) {
|
|
72
|
+
try {
|
|
73
|
+
return parse(key);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
throw new Error(`${name} is not a valid PEM key (${error.message}). If it comes from an env variable, replace the literal "\\n" with line breaks`);
|
|
77
|
+
}
|
|
78
|
+
}
|
package/build/src/guard.d.ts
CHANGED
|
@@ -49,10 +49,12 @@ export declare class JwtGuard<UserProvider extends JwtUserProviderContract<unkno
|
|
|
49
49
|
* during this request, or else the one sent by the client (cookie, body
|
|
50
50
|
* or Authorization header).
|
|
51
51
|
*
|
|
52
|
-
* The cookies are
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
52
|
+
* The cookies are cleared when the refresh token is revoked, and when
|
|
53
|
+
* there is no valid refresh token to revoke. In that case nothing is
|
|
54
|
+
* revoked and `E_UNAUTHORIZED_ACCESS` is raised, so a client that sends
|
|
55
|
+
* the wrong token (e.g. its access token) does not believe it has logged
|
|
56
|
+
* out while its refresh token stays valid. On any other error (e.g. the
|
|
57
|
+
* database is down), the cookies are kept so the client can retry.
|
|
56
58
|
*/
|
|
57
59
|
revoke(refreshToken?: string): Promise<void>;
|
|
58
60
|
/**
|
package/build/src/guard.js
CHANGED
|
@@ -108,6 +108,13 @@ export class JwtGuard {
|
|
|
108
108
|
const providerUser = await this.#userProvider.createUserForGuard(user);
|
|
109
109
|
const signPayload = this.#options.content(providerUser);
|
|
110
110
|
assertNoGuardClaims(signPayload, this.#options);
|
|
111
|
+
/**
|
|
112
|
+
* Without `userId`, the login succeeds but every request then gets a 401
|
|
113
|
+
* with no log: fail when issuing the token instead
|
|
114
|
+
*/
|
|
115
|
+
if (!this.#options.getUserId && !isUserId(signPayload.userId)) {
|
|
116
|
+
throw new RuntimeException('JWT guard `content` must return `userId`, or set `getUserId` to read the user id from the payload');
|
|
117
|
+
}
|
|
111
118
|
const token = this.#signAccessToken(signPayload);
|
|
112
119
|
let refreshToken;
|
|
113
120
|
if (this.#refreshTokenUserProvider) {
|
|
@@ -328,25 +335,44 @@ export class JwtGuard {
|
|
|
328
335
|
* during this request, or else the one sent by the client (cookie, body
|
|
329
336
|
* or Authorization header).
|
|
330
337
|
*
|
|
331
|
-
* The cookies are
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
338
|
+
* The cookies are cleared when the refresh token is revoked, and when
|
|
339
|
+
* there is no valid refresh token to revoke. In that case nothing is
|
|
340
|
+
* revoked and `E_UNAUTHORIZED_ACCESS` is raised, so a client that sends
|
|
341
|
+
* the wrong token (e.g. its access token) does not believe it has logged
|
|
342
|
+
* out while its refresh token stays valid. On any other error (e.g. the
|
|
343
|
+
* database is down), the cookies are kept so the client can retry.
|
|
335
344
|
*/
|
|
336
345
|
async revoke(refreshToken) {
|
|
337
346
|
if (!this.#refreshTokenUserProvider) {
|
|
338
347
|
throw new RuntimeException(REFRESH_TOKENS_NOT_CONFIGURED);
|
|
339
348
|
}
|
|
349
|
+
try {
|
|
350
|
+
await this.#revokeRefreshToken(this.#refreshTokenUserProvider, refreshToken);
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
if (error instanceof errors.E_UNAUTHORIZED_ACCESS)
|
|
354
|
+
this.#clearCookies();
|
|
355
|
+
throw error;
|
|
356
|
+
}
|
|
340
357
|
this.#clearCookies();
|
|
358
|
+
}
|
|
359
|
+
async #revokeRefreshToken(provider, refreshToken) {
|
|
341
360
|
refreshToken = refreshToken ?? this.#issuedRefreshToken ?? (await this.#findRefreshToken());
|
|
342
361
|
/**
|
|
343
362
|
* Only delete a refresh token whose secret is valid. The tokens
|
|
344
363
|
* provider deletes by id alone, so a forged token with a guessed
|
|
345
364
|
* id would otherwise revoke another user's refresh token.
|
|
365
|
+
*
|
|
366
|
+
* Like the refresh, require the configured abilities: with two guards
|
|
367
|
+
* that share a tokens provider, the logout of one guard must not
|
|
368
|
+
* revoke the refresh tokens of the other.
|
|
346
369
|
*/
|
|
347
370
|
const secret = new Secret(refreshToken);
|
|
348
|
-
|
|
349
|
-
|
|
371
|
+
const token = await provider.verifyToken(secret);
|
|
372
|
+
const requiredAbilities = this.#options.refreshTokenAbilities ?? [];
|
|
373
|
+
if (!token ||
|
|
374
|
+
!requiredAbilities.every((ability) => token.allows(ability)) ||
|
|
375
|
+
!(await provider.invalidateToken(secret))) {
|
|
350
376
|
throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
|
|
351
377
|
guardDriverName: this.driverName,
|
|
352
378
|
});
|