@maximemrf/adonisjs-jwt 0.9.0 → 1.0.0-rc.2
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 +219 -23
- package/build/configure.d.ts +2 -0
- package/build/configure.js +246 -0
- package/build/index.d.ts +3 -0
- package/build/index.js +3 -0
- package/build/src/define_config.d.ts +14 -6
- package/build/src/define_config.js +51 -55
- package/build/src/driver_resolver.d.ts +16 -0
- package/build/src/driver_resolver.js +46 -0
- package/build/src/drivers/asymmetric.d.ts +19 -0
- package/build/src/drivers/asymmetric.js +57 -0
- package/build/src/drivers/jwks.d.ts +30 -0
- package/build/src/drivers/jwks.js +68 -0
- package/build/src/drivers/symmetric.d.ts +16 -0
- package/build/src/drivers/symmetric.js +38 -0
- package/build/src/drivers/types.d.ts +8 -0
- package/build/src/drivers/types.js +1 -0
- package/build/src/{jwt.d.ts → guard.d.ts} +4 -17
- package/build/src/{jwt.js → guard.js} +102 -109
- package/build/src/types.d.ts +75 -3
- package/build/src/types.js +19 -1
- package/build/src/validation.d.ts +2 -0
- package/build/src/validation.js +24 -0
- package/build/stubs/main.d.ts +4 -0
- package/build/stubs/main.js +5 -0
- package/build/stubs/migration.stub +35 -0
- package/package.json +27 -26
|
@@ -1,62 +1,27 @@
|
|
|
1
|
-
import { JwtGuard } from './
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
config.publicKey !== undefined ||
|
|
6
|
-
config.algorithm !== undefined;
|
|
7
|
-
if (!partial) {
|
|
8
|
-
return;
|
|
9
|
-
}
|
|
10
|
-
if (!config.privateKey || !config.publicKey || !config.algorithm) {
|
|
11
|
-
throw new Error('JWT guard asymmetric mode requires `privateKey`, `publicKey`, and `algorithm` to be set together');
|
|
12
|
-
}
|
|
13
|
-
if (config.jwks) {
|
|
14
|
-
throw new Error('JWT guard cannot use `jwks` together with asymmetric `privateKey` / `publicKey`');
|
|
15
|
-
}
|
|
16
|
-
const expectedKeyType = config.algorithm.startsWith('RS')
|
|
17
|
-
? 'rsa'
|
|
18
|
-
: config.algorithm.startsWith('ES')
|
|
19
|
-
? 'ec'
|
|
20
|
-
: null;
|
|
21
|
-
if (!expectedKeyType) {
|
|
22
|
-
throw new Error(`Unsupported asymmetric algorithm "${config.algorithm}"`);
|
|
23
|
-
}
|
|
24
|
-
try {
|
|
25
|
-
const privateKey = createPrivateKey(config.privateKey);
|
|
26
|
-
const publicKey = createPublicKey(config.publicKey);
|
|
27
|
-
if (privateKey.asymmetricKeyType !== expectedKeyType) {
|
|
28
|
-
throw new Error(`privateKey type "${privateKey.asymmetricKeyType}" does not match algorithm "${config.algorithm}"`);
|
|
29
|
-
}
|
|
30
|
-
if (publicKey.asymmetricKeyType !== expectedKeyType) {
|
|
31
|
-
throw new Error(`publicKey type "${publicKey.asymmetricKeyType}" does not match algorithm "${config.algorithm}"`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
const message = error instanceof Error ? error.message : 'Invalid asymmetric key configuration';
|
|
36
|
-
throw new Error(`JWT guard asymmetric key validation failed: ${message}`);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
1
|
+
import { JwtGuard } from './guard.js';
|
|
2
|
+
import { validateGuardOptions } from './validation.js';
|
|
3
|
+
import { resolveDriver } from './driver_resolver.js';
|
|
4
|
+
import { hkdfSync } from 'node:crypto';
|
|
39
5
|
export function jwtGuard(config) {
|
|
40
6
|
return {
|
|
41
|
-
async resolver(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
config.
|
|
46
|
-
|
|
7
|
+
async resolver(name, app) {
|
|
8
|
+
const asymmetricKeys = {
|
|
9
|
+
privateKey: config.privateKey,
|
|
10
|
+
publicKey: config.publicKey,
|
|
11
|
+
algorithm: config.algorithm,
|
|
12
|
+
};
|
|
13
|
+
const usesAsymmetric = Object.values(asymmetricKeys).some((value) => value !== undefined);
|
|
14
|
+
/**
|
|
15
|
+
* Only fall back on the app key when no other signing method is configured
|
|
16
|
+
*/
|
|
17
|
+
const resolvedSecret = config.secret ??
|
|
18
|
+
(usesAsymmetric || config.jwks ? undefined : deriveSecretFromAppKey(readAppKey(app), name));
|
|
47
19
|
const options = {
|
|
48
|
-
...(usesAsymmetric
|
|
49
|
-
? {
|
|
50
|
-
privateKey: config.privateKey,
|
|
51
|
-
publicKey: config.publicKey,
|
|
52
|
-
algorithm: config.algorithm,
|
|
53
|
-
}
|
|
54
|
-
: {
|
|
55
|
-
secret: config.secret ?? appKey,
|
|
56
|
-
}),
|
|
20
|
+
...(usesAsymmetric ? asymmetricKeys : { secret: resolvedSecret }),
|
|
57
21
|
refreshTokenUserProvider: config.refreshTokenUserProvider,
|
|
58
22
|
tokenName: config.tokenName,
|
|
59
|
-
|
|
23
|
+
refreshTokenName: config.refreshTokenName,
|
|
24
|
+
tokenExpiresIn: config.tokenExpiresIn,
|
|
60
25
|
refreshTokenExpiresIn: config.refreshTokenExpiresIn,
|
|
61
26
|
useCookies: config.useCookies,
|
|
62
27
|
useCookiesForRefreshToken: config.useCookiesForRefreshToken,
|
|
@@ -64,8 +29,39 @@ export function jwtGuard(config) {
|
|
|
64
29
|
content: config.content,
|
|
65
30
|
jwks: config.jwks,
|
|
66
31
|
cookie: config.cookie,
|
|
32
|
+
issuer: config.issuer,
|
|
33
|
+
audience: config.audience,
|
|
34
|
+
algorithms: config.algorithms,
|
|
35
|
+
clockTolerance: config.clockTolerance,
|
|
36
|
+
getUserId: config.getUserId,
|
|
37
|
+
verifyPayload: config.verifyPayload,
|
|
67
38
|
};
|
|
68
|
-
|
|
39
|
+
validateGuardOptions(options, 'JWT guard');
|
|
40
|
+
const driver = resolveDriver(options, 'JWT guard');
|
|
41
|
+
return (ctx) => new JwtGuard(ctx, config.provider, { ...options, driver });
|
|
69
42
|
},
|
|
70
43
|
};
|
|
71
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Derive a dedicated signing key from the app key, so the guard never signs
|
|
47
|
+
* with the raw app key used elsewhere by the application, and two JWT guards
|
|
48
|
+
* never accept each other's tokens.
|
|
49
|
+
*/
|
|
50
|
+
function deriveSecretFromAppKey(rawAppKey, guardName) {
|
|
51
|
+
if (!rawAppKey || rawAppKey.length < 32) {
|
|
52
|
+
throw new Error('JWT guard requires the application key (APP_KEY) to be at least 32 characters when no `secret` is configured');
|
|
53
|
+
}
|
|
54
|
+
return Buffer.from(hkdfSync('sha256', rawAppKey, '', `@maximemrf/adonisjs-jwt:${guardName}`, 32)).toString('hex');
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* AdonisJS v6 exposes the app key as `app.appKey` in config/app.ts. AdonisJS
|
|
58
|
+
* v7 apps only pass APP_KEY to config/encryption.ts, and the env loader
|
|
59
|
+
* copies it to process.env.
|
|
60
|
+
*/
|
|
61
|
+
function readAppKey(app) {
|
|
62
|
+
const appKey = app.config.get('app.appKey', undefined);
|
|
63
|
+
if (appKey) {
|
|
64
|
+
return typeof appKey === 'string' ? appKey : appKey.release();
|
|
65
|
+
}
|
|
66
|
+
return process.env.APP_KEY;
|
|
67
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Options } from 'jwks-rsa';
|
|
2
|
+
import type { JwtAsymmetricAlgorithm, JwtJwksAlgorithm } from './types.js';
|
|
3
|
+
import type { JwtDriver } from './drivers/types.js';
|
|
4
|
+
export declare function resolveDriver(options: {
|
|
5
|
+
driver?: JwtDriver;
|
|
6
|
+
secret?: string;
|
|
7
|
+
privateKey?: string;
|
|
8
|
+
publicKey?: string;
|
|
9
|
+
algorithm?: JwtAsymmetricAlgorithm;
|
|
10
|
+
jwks?: Options;
|
|
11
|
+
issuer?: string;
|
|
12
|
+
audience?: string | string[];
|
|
13
|
+
algorithms?: JwtJwksAlgorithm[];
|
|
14
|
+
clockTolerance?: number;
|
|
15
|
+
verifyPayload?: unknown;
|
|
16
|
+
}, contextName?: string): JwtDriver;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { SymmetricDriver } from './drivers/symmetric.js';
|
|
2
|
+
import { AsymmetricDriver } from './drivers/asymmetric.js';
|
|
3
|
+
import { JwksDriver } from './drivers/jwks.js';
|
|
4
|
+
export function resolveDriver(options, contextName = 'JWT guard') {
|
|
5
|
+
if (options.driver) {
|
|
6
|
+
return options.driver;
|
|
7
|
+
}
|
|
8
|
+
const usesAsymmetric = options.privateKey !== undefined &&
|
|
9
|
+
options.publicKey !== undefined &&
|
|
10
|
+
options.algorithm !== undefined;
|
|
11
|
+
if (options.jwks) {
|
|
12
|
+
try {
|
|
13
|
+
return new JwksDriver(options.jwks, {
|
|
14
|
+
issuer: options.issuer,
|
|
15
|
+
audience: options.audience,
|
|
16
|
+
clockTolerance: options.clockTolerance,
|
|
17
|
+
algorithms: options.algorithms,
|
|
18
|
+
verifiesPayload: options.verifyPayload !== undefined,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
throw new Error(`${contextName} JWKS validation failed: ${error.message}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (usesAsymmetric) {
|
|
26
|
+
try {
|
|
27
|
+
return new AsymmetricDriver({
|
|
28
|
+
privateKey: options.privateKey,
|
|
29
|
+
publicKey: options.publicKey,
|
|
30
|
+
algorithm: options.algorithm,
|
|
31
|
+
issuer: options.issuer,
|
|
32
|
+
audience: options.audience,
|
|
33
|
+
clockTolerance: options.clockTolerance,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
throw new Error(`${contextName} asymmetric key validation failed: ${error.message}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return new SymmetricDriver({
|
|
41
|
+
secret: options.secret,
|
|
42
|
+
issuer: options.issuer,
|
|
43
|
+
audience: options.audience,
|
|
44
|
+
clockTolerance: options.clockTolerance,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { StringValue } from 'ms';
|
|
2
|
+
import type { JwtDriver } from './types.js';
|
|
3
|
+
import type { JwtAsymmetricAlgorithm } from '../types.js';
|
|
4
|
+
export declare class AsymmetricDriver implements JwtDriver {
|
|
5
|
+
#private;
|
|
6
|
+
readonly canSign = true;
|
|
7
|
+
constructor(options: {
|
|
8
|
+
privateKey: string;
|
|
9
|
+
publicKey: string;
|
|
10
|
+
algorithm: JwtAsymmetricAlgorithm;
|
|
11
|
+
issuer?: string;
|
|
12
|
+
audience?: string | string[];
|
|
13
|
+
clockTolerance?: number;
|
|
14
|
+
});
|
|
15
|
+
sign(payload: Record<string, any>, options?: {
|
|
16
|
+
expiresIn?: number | StringValue;
|
|
17
|
+
}): string;
|
|
18
|
+
verify(token: string): Record<string, any> | string;
|
|
19
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import jwt from 'jsonwebtoken';
|
|
2
|
+
import { createPrivateKey, createPublicKey } from 'node:crypto';
|
|
3
|
+
export class AsymmetricDriver {
|
|
4
|
+
canSign = true;
|
|
5
|
+
#privateKey;
|
|
6
|
+
#publicKey;
|
|
7
|
+
#algorithm;
|
|
8
|
+
#issuer;
|
|
9
|
+
#audience;
|
|
10
|
+
#clockTolerance;
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.#privateKey = options.privateKey;
|
|
13
|
+
this.#publicKey = options.publicKey;
|
|
14
|
+
this.#algorithm = options.algorithm;
|
|
15
|
+
this.#issuer = options.issuer;
|
|
16
|
+
this.#audience = options.audience;
|
|
17
|
+
this.#clockTolerance = options.clockTolerance;
|
|
18
|
+
this.#assertAsymmetricKeyMatchesAlgorithm();
|
|
19
|
+
}
|
|
20
|
+
#assertAsymmetricKeyMatchesAlgorithm() {
|
|
21
|
+
const algorithm = this.#algorithm;
|
|
22
|
+
const expectedKeyType = algorithm.startsWith('RS')
|
|
23
|
+
? 'rsa'
|
|
24
|
+
: algorithm.startsWith('ES')
|
|
25
|
+
? 'ec'
|
|
26
|
+
: null;
|
|
27
|
+
if (!expectedKeyType) {
|
|
28
|
+
throw new Error(`Unsupported asymmetric algorithm "${algorithm}"`);
|
|
29
|
+
}
|
|
30
|
+
const privateKey = createPrivateKey(this.#privateKey);
|
|
31
|
+
const publicKey = createPublicKey(this.#publicKey);
|
|
32
|
+
if (privateKey.asymmetricKeyType !== expectedKeyType) {
|
|
33
|
+
throw new Error(`privateKey type "${privateKey.asymmetricKeyType}" does not match algorithm "${algorithm}"`);
|
|
34
|
+
}
|
|
35
|
+
if (publicKey.asymmetricKeyType !== expectedKeyType) {
|
|
36
|
+
throw new Error(`publicKey type "${publicKey.asymmetricKeyType}" does not match algorithm "${algorithm}"`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
sign(payload, options) {
|
|
40
|
+
const signOptions = {
|
|
41
|
+
...(options?.expiresIn !== undefined ? { expiresIn: options.expiresIn } : {}),
|
|
42
|
+
algorithm: this.#algorithm,
|
|
43
|
+
...(this.#issuer ? { issuer: this.#issuer } : {}),
|
|
44
|
+
...(this.#audience ? { audience: this.#audience } : {}),
|
|
45
|
+
};
|
|
46
|
+
return jwt.sign(payload, this.#privateKey, signOptions);
|
|
47
|
+
}
|
|
48
|
+
verify(token) {
|
|
49
|
+
const verifyOptions = {
|
|
50
|
+
algorithms: [this.#algorithm],
|
|
51
|
+
...(this.#issuer ? { issuer: this.#issuer } : {}),
|
|
52
|
+
...(this.#audience ? { audience: this.#audience } : {}),
|
|
53
|
+
...(this.#clockTolerance !== undefined ? { clockTolerance: this.#clockTolerance } : {}),
|
|
54
|
+
};
|
|
55
|
+
return jwt.verify(token, this.#publicKey, verifyOptions);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Options } from 'jwks-rsa';
|
|
2
|
+
import type { StringValue } from 'ms';
|
|
3
|
+
import type { JwtDriver } from './types.js';
|
|
4
|
+
import { type JwtJwksAlgorithm } from '../types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Safe jwks-rsa defaults: tokens with an unknown `kid` trigger a JWKS
|
|
7
|
+
* fetch, so fetches must be cached and rate limited to avoid hammering
|
|
8
|
+
* the identity provider. They can be overridden through the `jwks` option.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEFAULT_JWKS_OPTIONS: {
|
|
11
|
+
cache: true;
|
|
12
|
+
rateLimit: true;
|
|
13
|
+
jwksRequestsPerMinute: number;
|
|
14
|
+
timeout: number;
|
|
15
|
+
};
|
|
16
|
+
export declare class JwksDriver implements JwtDriver {
|
|
17
|
+
#private;
|
|
18
|
+
readonly canSign = false;
|
|
19
|
+
constructor(options: Options, driverOptions?: {
|
|
20
|
+
issuer?: string;
|
|
21
|
+
audience?: string | string[];
|
|
22
|
+
algorithms?: JwtJwksAlgorithm[];
|
|
23
|
+
clockTolerance?: number;
|
|
24
|
+
verifiesPayload?: boolean;
|
|
25
|
+
});
|
|
26
|
+
sign(_payload: Record<string, any>, _options?: {
|
|
27
|
+
expiresIn?: number | StringValue;
|
|
28
|
+
}): never;
|
|
29
|
+
verify(token: string): Promise<Record<string, any> | string>;
|
|
30
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import jwt from 'jsonwebtoken';
|
|
2
|
+
import { errors } from '@adonisjs/auth';
|
|
3
|
+
import { JwksManager } from '../jwks.js';
|
|
4
|
+
import { ALLOWED_JWKS_ALGORITHMS } from '../types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Safe jwks-rsa defaults: tokens with an unknown `kid` trigger a JWKS
|
|
7
|
+
* fetch, so fetches must be cached and rate limited to avoid hammering
|
|
8
|
+
* the identity provider. They can be overridden through the `jwks` option.
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_JWKS_OPTIONS = {
|
|
11
|
+
cache: true,
|
|
12
|
+
rateLimit: true,
|
|
13
|
+
jwksRequestsPerMinute: 10,
|
|
14
|
+
timeout: 5000,
|
|
15
|
+
};
|
|
16
|
+
export class JwksDriver {
|
|
17
|
+
canSign = false;
|
|
18
|
+
#jwksManager;
|
|
19
|
+
#issuer;
|
|
20
|
+
#audience;
|
|
21
|
+
#clockTolerance;
|
|
22
|
+
#algorithms;
|
|
23
|
+
constructor(options, driverOptions) {
|
|
24
|
+
if (!driverOptions?.issuer ||
|
|
25
|
+
!(hasAudience(driverOptions.audience) || driverOptions.verifiesPayload)) {
|
|
26
|
+
throw new Error('`issuer` and either `audience` or `verifyPayload` are required, otherwise any token signed by the identity provider is accepted, including tokens issued for other applications');
|
|
27
|
+
}
|
|
28
|
+
const algorithms = driverOptions.algorithms ?? [...ALLOWED_JWKS_ALGORITHMS];
|
|
29
|
+
if (algorithms.length === 0) {
|
|
30
|
+
throw new Error('`algorithms` must not be empty');
|
|
31
|
+
}
|
|
32
|
+
const unsupported = algorithms.filter((alg) => !ALLOWED_JWKS_ALGORITHMS.includes(alg));
|
|
33
|
+
if (unsupported.length > 0) {
|
|
34
|
+
throw new Error(`unsupported algorithm(s): ${unsupported.join(', ')}. Allowed: ${ALLOWED_JWKS_ALGORITHMS.join(', ')}`);
|
|
35
|
+
}
|
|
36
|
+
this.#jwksManager = new JwksManager({ ...DEFAULT_JWKS_OPTIONS, ...options });
|
|
37
|
+
this.#issuer = driverOptions.issuer;
|
|
38
|
+
this.#audience = driverOptions.audience;
|
|
39
|
+
this.#clockTolerance = driverOptions.clockTolerance;
|
|
40
|
+
this.#algorithms = algorithms;
|
|
41
|
+
}
|
|
42
|
+
sign(_payload, _options) {
|
|
43
|
+
throw new errors.E_UNAUTHORIZED_ACCESS("You can't use the auth.generate method with jwks", {
|
|
44
|
+
guardDriverName: 'jwt',
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async verify(token) {
|
|
48
|
+
const decoded = jwt.decode(token, { complete: true });
|
|
49
|
+
if (!decoded || !decoded.header || !decoded.header.kid || !decoded.header.alg) {
|
|
50
|
+
throw new errors.E_UNAUTHORIZED_ACCESS('Unauthorized access', {
|
|
51
|
+
guardDriverName: 'jwt',
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const key = await this.#jwksManager.getSigningKey(decoded.header.kid);
|
|
55
|
+
const verifyOptions = {
|
|
56
|
+
algorithms: [...this.#algorithms],
|
|
57
|
+
...(this.#issuer ? { issuer: this.#issuer } : {}),
|
|
58
|
+
...(this.#audience ? { audience: this.#audience } : {}),
|
|
59
|
+
...(this.#clockTolerance !== undefined ? { clockTolerance: this.#clockTolerance } : {}),
|
|
60
|
+
};
|
|
61
|
+
return jwt.verify(token, key, verifyOptions);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function hasAudience(audience) {
|
|
65
|
+
return Array.isArray(audience)
|
|
66
|
+
? audience.length > 0 && audience.every(Boolean)
|
|
67
|
+
: Boolean(audience);
|
|
68
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { StringValue } from 'ms';
|
|
2
|
+
import type { JwtDriver } from './types.js';
|
|
3
|
+
export declare class SymmetricDriver implements JwtDriver {
|
|
4
|
+
#private;
|
|
5
|
+
readonly canSign = true;
|
|
6
|
+
constructor(options: {
|
|
7
|
+
secret: string;
|
|
8
|
+
issuer?: string;
|
|
9
|
+
audience?: string | string[];
|
|
10
|
+
clockTolerance?: number;
|
|
11
|
+
});
|
|
12
|
+
sign(payload: Record<string, any>, options?: {
|
|
13
|
+
expiresIn?: number | StringValue;
|
|
14
|
+
}): string;
|
|
15
|
+
verify(token: string): Record<string, any> | string;
|
|
16
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import jwt from 'jsonwebtoken';
|
|
2
|
+
import { ALLOWED_SYMMETRIC_ALGORITHMS } from '../types.js';
|
|
3
|
+
export class SymmetricDriver {
|
|
4
|
+
canSign = true;
|
|
5
|
+
#secret;
|
|
6
|
+
#issuer;
|
|
7
|
+
#audience;
|
|
8
|
+
#clockTolerance;
|
|
9
|
+
constructor(options) {
|
|
10
|
+
if (!options.secret) {
|
|
11
|
+
throw new Error('Symmetric JWT driver requires a secret key');
|
|
12
|
+
}
|
|
13
|
+
if (options.secret.length < 32) {
|
|
14
|
+
throw new Error('Symmetric JWT driver requires a secret of at least 32 characters to ensure sufficient entropy');
|
|
15
|
+
}
|
|
16
|
+
this.#secret = options.secret;
|
|
17
|
+
this.#issuer = options.issuer;
|
|
18
|
+
this.#audience = options.audience;
|
|
19
|
+
this.#clockTolerance = options.clockTolerance;
|
|
20
|
+
}
|
|
21
|
+
sign(payload, options) {
|
|
22
|
+
const signOptions = {
|
|
23
|
+
...(options?.expiresIn !== undefined ? { expiresIn: options.expiresIn } : {}),
|
|
24
|
+
...(this.#issuer ? { issuer: this.#issuer } : {}),
|
|
25
|
+
...(this.#audience ? { audience: this.#audience } : {}),
|
|
26
|
+
};
|
|
27
|
+
return jwt.sign(payload, this.#secret, signOptions);
|
|
28
|
+
}
|
|
29
|
+
verify(token) {
|
|
30
|
+
const verifyOptions = {
|
|
31
|
+
algorithms: [...ALLOWED_SYMMETRIC_ALGORITHMS],
|
|
32
|
+
...(this.#issuer ? { issuer: this.#issuer } : {}),
|
|
33
|
+
...(this.#audience ? { audience: this.#audience } : {}),
|
|
34
|
+
...(this.#clockTolerance !== undefined ? { clockTolerance: this.#clockTolerance } : {}),
|
|
35
|
+
};
|
|
36
|
+
return jwt.verify(token, this.#secret, verifyOptions);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { StringValue } from 'ms';
|
|
2
|
+
export interface JwtDriver {
|
|
3
|
+
readonly canSign: boolean;
|
|
4
|
+
sign(payload: Record<string, any>, options?: {
|
|
5
|
+
expiresIn?: number | StringValue;
|
|
6
|
+
}): string;
|
|
7
|
+
verify(token: string): Promise<Record<string, any> | string> | Record<string, any> | string;
|
|
8
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { symbols } from '@adonisjs/auth';
|
|
2
2
|
import type { AuthClientResponse, GuardContract } from '@adonisjs/auth/types';
|
|
3
3
|
import type { HttpContext } from '@adonisjs/core/http';
|
|
4
|
-
import type
|
|
5
|
-
|
|
6
|
-
export declare class JwtGuard<UserProvider extends JwtUserProviderContract<unknown>> implements GuardContract<UserProvider[typeof symbols.PROVIDER_REAL_USER]> {
|
|
4
|
+
import { type JwtUserProviderContract, type JwtGuardOptions, type JwtGenerateResult } from './types.js';
|
|
5
|
+
export declare class JwtGuard<UserProvider extends JwtUserProviderContract<unknown>, WithRefreshToken extends boolean = boolean> implements GuardContract<UserProvider[typeof symbols.PROVIDER_REAL_USER]> {
|
|
7
6
|
#private;
|
|
8
7
|
constructor(ctx: HttpContext, userProvider: UserProvider, option: JwtGuardOptions<UserProvider[typeof symbols.PROVIDER_REAL_USER]>);
|
|
9
8
|
/**
|
|
@@ -34,13 +33,7 @@ export declare class JwtGuard<UserProvider extends JwtUserProviderContract<unkno
|
|
|
34
33
|
/**
|
|
35
34
|
* Generate a JWT token for a given user.
|
|
36
35
|
*/
|
|
37
|
-
generate(user: UserProvider[typeof symbols.PROVIDER_REAL_USER]): Promise<
|
|
38
|
-
type: string;
|
|
39
|
-
token: string;
|
|
40
|
-
expiresIn: number | StringValue | undefined;
|
|
41
|
-
refreshToken: string | undefined;
|
|
42
|
-
refreshTokenExpiresIn: number | StringValue | undefined;
|
|
43
|
-
}>;
|
|
36
|
+
generate(user: UserProvider[typeof symbols.PROVIDER_REAL_USER]): Promise<JwtGenerateResult<WithRefreshToken>>;
|
|
44
37
|
/**
|
|
45
38
|
* Authenticate the current HTTP request and return
|
|
46
39
|
* the user instance if there is a valid JWT token
|
|
@@ -49,13 +42,7 @@ export declare class JwtGuard<UserProvider extends JwtUserProviderContract<unkno
|
|
|
49
42
|
authenticate(): Promise<UserProvider[typeof symbols.PROVIDER_REAL_USER] & {
|
|
50
43
|
currentToken: string;
|
|
51
44
|
}>;
|
|
52
|
-
generateWithRefreshToken(refreshToken?: string): Promise<
|
|
53
|
-
type: string;
|
|
54
|
-
token: string;
|
|
55
|
-
expiresIn: number | StringValue | undefined;
|
|
56
|
-
refreshToken: string | undefined;
|
|
57
|
-
refreshTokenExpiresIn: number | StringValue | undefined;
|
|
58
|
-
} | undefined>;
|
|
45
|
+
generateWithRefreshToken(refreshToken?: string): Promise<JwtGenerateResult<true>>;
|
|
59
46
|
/**
|
|
60
47
|
* Revoke the refresh token
|
|
61
48
|
*/
|