vortez 5.0.0-dev.16 → 5.0.0-dev.18
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/beta/JwtManager/HeaderValidator.d.ts +25 -0
- package/build/beta/JwtManager/HeaderValidator.js +47 -0
- package/build/beta/JwtManager/HeaderValidator.js.map +1 -0
- package/build/beta/JwtManager/Jwt.d.ts +14 -6
- package/build/beta/JwtManager/Jwt.js +15 -7
- package/build/beta/JwtManager/Jwt.js.map +1 -1
- package/build/beta/JwtManager/JwtManager.d.ts +32 -25
- package/build/beta/JwtManager/JwtManager.js +62 -41
- package/build/beta/JwtManager/JwtManager.js.map +1 -1
- package/build/beta/JwtManager/JwtUtils.d.ts +19 -4
- package/build/beta/JwtManager/JwtUtils.js +18 -0
- package/build/beta/JwtManager/JwtUtils.js.map +1 -1
- package/build/beta/JwtManager/KeyEntry.d.ts +52 -0
- package/build/beta/JwtManager/KeyEntry.js +42 -0
- package/build/beta/JwtManager/KeyEntry.js.map +1 -0
- package/build/beta/JwtManager/KeyGenerator.d.ts +5 -0
- package/build/beta/JwtManager/KeyGenerator.js +12 -9
- package/build/beta/JwtManager/KeyGenerator.js.map +1 -1
- package/package.json +1 -1
- package/tests/jwtManager/jwtManager.js +310 -46
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type Jwt from "./Jwt.js";
|
|
2
|
+
export declare class HeaderValidator {
|
|
3
|
+
static ALLOWED_ALGO_PREFIXES: string[];
|
|
4
|
+
static ALLOWED_ALGO_LENGTHS: number[];
|
|
5
|
+
/**
|
|
6
|
+
* Static method to validate the JWT header. It checks if the header is a non-null object and then calls specific validation methods for the algorithm, type, and expiration claims. If any of the validations fail, it throws an error indicating the issue with the header.
|
|
7
|
+
* @param header The JWT header object to validate.
|
|
8
|
+
* @throws Will throw an error if the header is not a non-null object or if any of the specific validations fail.
|
|
9
|
+
*/
|
|
10
|
+
static validate(header: any): asserts header is Jwt.Header;
|
|
11
|
+
/**
|
|
12
|
+
* Static method to validate the `alg` (algorithm) property in the JWT header. It checks if the `alg` property is present, ensures that it follows the expected format (a prefix followed by a length), and verifies that the prefix and length are among the allowed values. If any of these conditions are not met, it throws an error indicating the issue with the `alg` property.
|
|
13
|
+
* @param header The JWT header object to validate, which should contain the `alg` property.
|
|
14
|
+
* @throws Will throw an error if the `alg` property is missing, does not follow the expected format, or has an unsupported prefix or length.
|
|
15
|
+
*/
|
|
16
|
+
static validateAlgorithm(header: any): void;
|
|
17
|
+
/**
|
|
18
|
+
* Static method to validate the `typ` (type) property in the JWT header. It checks if the `typ` property is present, ensures that it is a string, and verifies that its value is "jwt" (case-insensitive). If any of these conditions are not met, it throws an error indicating the issue with the `typ` property.
|
|
19
|
+
* @param header The JWT header object to validate, which should contain the `typ` property.
|
|
20
|
+
* @throws Will throw an error if the `typ` property is missing, is not a string, or does not equal "jwt" (case-insensitive).
|
|
21
|
+
*/
|
|
22
|
+
static validateType(header: any): void;
|
|
23
|
+
}
|
|
24
|
+
export declare namespace HeaderValidator { }
|
|
25
|
+
export default HeaderValidator;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export class HeaderValidator {
|
|
2
|
+
static ALLOWED_ALGO_PREFIXES = ['HS', 'RS', 'PS', 'ES'];
|
|
3
|
+
static ALLOWED_ALGO_LENGTHS = [256, 384, 512];
|
|
4
|
+
/**
|
|
5
|
+
* Static method to validate the JWT header. It checks if the header is a non-null object and then calls specific validation methods for the algorithm, type, and expiration claims. If any of the validations fail, it throws an error indicating the issue with the header.
|
|
6
|
+
* @param header The JWT header object to validate.
|
|
7
|
+
* @throws Will throw an error if the header is not a non-null object or if any of the specific validations fail.
|
|
8
|
+
*/
|
|
9
|
+
static validate(header) {
|
|
10
|
+
if (typeof header !== 'object' || header === null)
|
|
11
|
+
throw new Error('Header must be a non-null object');
|
|
12
|
+
this.validateAlgorithm(header);
|
|
13
|
+
this.validateType(header);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Static method to validate the `alg` (algorithm) property in the JWT header. It checks if the `alg` property is present, ensures that it follows the expected format (a prefix followed by a length), and verifies that the prefix and length are among the allowed values. If any of these conditions are not met, it throws an error indicating the issue with the `alg` property.
|
|
17
|
+
* @param header The JWT header object to validate, which should contain the `alg` property.
|
|
18
|
+
* @throws Will throw an error if the `alg` property is missing, does not follow the expected format, or has an unsupported prefix or length.
|
|
19
|
+
*/
|
|
20
|
+
static validateAlgorithm(header) {
|
|
21
|
+
if (!('alg' in header))
|
|
22
|
+
throw new Error('Header must contain "alg" property');
|
|
23
|
+
const [prefix, lengthStr] = header.alg.match(/([A-Z]+)(\d+)/)?.slice(1) || [];
|
|
24
|
+
if (!prefix || !lengthStr)
|
|
25
|
+
throw new Error('Invalid "alg" format in header');
|
|
26
|
+
const length = parseInt(lengthStr, 10);
|
|
27
|
+
if (!HeaderValidator.ALLOWED_ALGO_PREFIXES.includes(prefix))
|
|
28
|
+
throw new Error(`Unsupported algorithm prefix: ${prefix}`);
|
|
29
|
+
if (!HeaderValidator.ALLOWED_ALGO_LENGTHS.includes(length))
|
|
30
|
+
throw new Error(`Unsupported algorithm length: ${length}`);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Static method to validate the `typ` (type) property in the JWT header. It checks if the `typ` property is present, ensures that it is a string, and verifies that its value is "jwt" (case-insensitive). If any of these conditions are not met, it throws an error indicating the issue with the `typ` property.
|
|
34
|
+
* @param header The JWT header object to validate, which should contain the `typ` property.
|
|
35
|
+
* @throws Will throw an error if the `typ` property is missing, is not a string, or does not equal "jwt" (case-insensitive).
|
|
36
|
+
*/
|
|
37
|
+
static validateType(header) {
|
|
38
|
+
if (!('typ' in header))
|
|
39
|
+
throw new Error('Header must contain "typ" property');
|
|
40
|
+
if (typeof header.typ !== 'string')
|
|
41
|
+
throw new Error('The "typ" property must be a string');
|
|
42
|
+
if (header.typ.toLowerCase() !== 'jwt')
|
|
43
|
+
throw new Error('The "typ" property must be "jwt"');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export default HeaderValidator;
|
|
47
|
+
//# sourceMappingURL=HeaderValidator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HeaderValidator.js","sourceRoot":"","sources":["../../../src/beta/JwtManager/HeaderValidator.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,eAAe;IACjB,MAAM,CAAC,qBAAqB,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,oBAAoB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACrD;;;;OAIG;IACI,MAAM,CAAC,QAAQ,CAAC,MAAW;QAC9B,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACvG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IACD;;;;OAIG;IACI,MAAM,CAAC,iBAAiB,CAAC,MAAW;QACvC,IAAI,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QAC9E,MAAM,CAAE,MAAM,EAAE,SAAS,CAAE,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAChF,IAAI,CAAC,MAAM,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAC7E,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACvC,IAAI,CAAC,eAAe,CAAC,qBAAqB,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,MAAM,EAAE,CAAC,CAAC;QACxH,IAAI,CAAC,eAAe,CAAC,oBAAoB,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,MAAM,EAAE,CAAC,CAAC;IAC3H,CAAC;IACD;;;;OAIG;IACI,MAAM,CAAC,YAAY,CAAC,MAAW;QAClC,IAAI,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QAC9E,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QAC3F,IAAI,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAChG,CAAC;;AAGL,eAAe,eAAe,CAAC"}
|
|
@@ -1,17 +1,24 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import type Algorithm from './algorithm/Algorithm.js';
|
|
2
|
+
import type KeyEntry from './KeyEntry.js';
|
|
2
3
|
export declare class Jwt implements Jwt.Jwt {
|
|
3
4
|
readonly header: Jwt.Header;
|
|
4
5
|
readonly payload: Jwt.Payload;
|
|
5
6
|
readonly signature: string;
|
|
6
|
-
protected readonly
|
|
7
|
-
constructor(header: Jwt.Header, payload: Jwt.Payload, signature: string,
|
|
7
|
+
protected readonly signer: Algorithm;
|
|
8
|
+
constructor(header: Jwt.Header, payload: Jwt.Payload, signature: string, signer: Algorithm);
|
|
8
9
|
/**
|
|
9
10
|
* Checks if the JWT has expired based on the `exp` claim in the payload. If the `exp` claim is not present, it returns `false`, indicating that the token is not considered expired. If the `exp` claim is present, it compares the current time (in seconds since the Unix epoch) with the expiration time specified in the `exp` claim and returns `true` if the token has expired, or `false` otherwise.
|
|
10
11
|
* @returns `true` if the JWT has expired; otherwise, `false`.
|
|
11
12
|
*/
|
|
12
|
-
expired(): boolean;
|
|
13
|
+
get expired(): boolean;
|
|
13
14
|
toJSON(): Jwt.Jwt;
|
|
14
15
|
toString(): string;
|
|
16
|
+
/**
|
|
17
|
+
* Static method to check if a given expiration time (in seconds since the Unix epoch) has passed. It compares the current time with the provided expiration time and returns `true` if the current time is greater than or equal to the expiration time, indicating that the token has expired, or `false` otherwise.
|
|
18
|
+
* @param exp The expiration time to check, represented as a number of seconds since the Unix epoch.
|
|
19
|
+
* @returns `true` if the current time is greater than or equal to the expiration time; otherwise, `false`.
|
|
20
|
+
*/
|
|
21
|
+
static isExpired(exp: number): boolean;
|
|
15
22
|
}
|
|
16
23
|
export declare namespace Jwt {
|
|
17
24
|
interface Jwt {
|
|
@@ -20,12 +27,13 @@ export declare namespace Jwt {
|
|
|
20
27
|
signature: string;
|
|
21
28
|
}
|
|
22
29
|
interface Header {
|
|
23
|
-
alg:
|
|
30
|
+
alg: KeyEntry.AlgorithmName;
|
|
24
31
|
typ: 'jwt';
|
|
25
|
-
|
|
32
|
+
kid?: string;
|
|
26
33
|
[key: string]: any;
|
|
27
34
|
}
|
|
28
35
|
interface Payload {
|
|
36
|
+
exp?: number;
|
|
29
37
|
[key: string]: any;
|
|
30
38
|
}
|
|
31
39
|
}
|
|
@@ -3,22 +3,21 @@ export class Jwt {
|
|
|
3
3
|
header;
|
|
4
4
|
payload;
|
|
5
5
|
signature;
|
|
6
|
-
|
|
7
|
-
constructor(header, payload, signature,
|
|
6
|
+
signer;
|
|
7
|
+
constructor(header, payload, signature, signer) {
|
|
8
8
|
this.header = header;
|
|
9
9
|
this.payload = payload;
|
|
10
10
|
this.signature = signature;
|
|
11
|
-
this.
|
|
11
|
+
this.signer = signer;
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* Checks if the JWT has expired based on the `exp` claim in the payload. If the `exp` claim is not present, it returns `false`, indicating that the token is not considered expired. If the `exp` claim is present, it compares the current time (in seconds since the Unix epoch) with the expiration time specified in the `exp` claim and returns `true` if the token has expired, or `false` otherwise.
|
|
15
15
|
* @returns `true` if the JWT has expired; otherwise, `false`.
|
|
16
16
|
*/
|
|
17
|
-
expired() {
|
|
18
|
-
if (
|
|
17
|
+
get expired() {
|
|
18
|
+
if (this.payload.exp == null)
|
|
19
19
|
return false;
|
|
20
|
-
|
|
21
|
-
return now >= this.header.exp;
|
|
20
|
+
return Jwt.isExpired(this.payload.exp);
|
|
22
21
|
}
|
|
23
22
|
toJSON() {
|
|
24
23
|
return { header: this.header, payload: this.payload, signature: this.signature };
|
|
@@ -28,6 +27,15 @@ export class Jwt {
|
|
|
28
27
|
const encodedPayload = JwtUtils.objectToBase64Url(this.payload);
|
|
29
28
|
return `${encodedHeader}.${encodedPayload}.${this.signature}`;
|
|
30
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Static method to check if a given expiration time (in seconds since the Unix epoch) has passed. It compares the current time with the provided expiration time and returns `true` if the current time is greater than or equal to the expiration time, indicating that the token has expired, or `false` otherwise.
|
|
32
|
+
* @param exp The expiration time to check, represented as a number of seconds since the Unix epoch.
|
|
33
|
+
* @returns `true` if the current time is greater than or equal to the expiration time; otherwise, `false`.
|
|
34
|
+
*/
|
|
35
|
+
static isExpired(exp) {
|
|
36
|
+
const now = JwtUtils.nowInSeconds();
|
|
37
|
+
return now >= exp;
|
|
38
|
+
}
|
|
31
39
|
}
|
|
32
40
|
export default Jwt;
|
|
33
41
|
//# sourceMappingURL=Jwt.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Jwt.js","sourceRoot":"","sources":["../../../src/beta/JwtManager/Jwt.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Jwt.js","sourceRoot":"","sources":["../../../src/beta/JwtManager/Jwt.ts"],"names":[],"mappings":"AAGA,OAAO,QAAQ,MAAM,eAAe,CAAC;AAErC,MAAM,OAAO,GAAG;IAEQ;IACA;IACA;IACG;IAJvB,YACoB,MAAkB,EAClB,OAAoB,EACpB,SAAiB,EACd,MAAiB;QAHpB,WAAM,GAAN,MAAM,CAAY;QAClB,YAAO,GAAP,OAAO,CAAa;QACpB,cAAS,GAAT,SAAS,CAAQ;QACd,WAAM,GAAN,MAAM,CAAW;IACrC,CAAC;IACJ;;;OAGG;IACH,IAAW,OAAO;QACd,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,IAAI;YAAE,OAAO,KAAK,CAAC;QAC3C,OAAO,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3C,CAAC;IAEM,MAAM;QACT,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;IACrF,CAAC;IACM,QAAQ;QACX,MAAM,aAAa,GAAG,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9D,MAAM,cAAc,GAAG,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChE,OAAO,GAAG,aAAa,IAAI,cAAc,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;IAClE,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,SAAS,CAAC,GAAW;QAC/B,MAAM,GAAG,GAAG,QAAQ,CAAC,YAAY,EAAE,CAAC;QACpC,OAAO,GAAG,IAAI,GAAG,CAAC;IACtB,CAAC;CACJ;AAkBD,eAAe,GAAG,CAAC"}
|
|
@@ -1,49 +1,56 @@
|
|
|
1
|
-
import RSA from "./algorithm/RSA.js";
|
|
2
|
-
import HMAC from "./algorithm/HMAC.js";
|
|
3
|
-
import ECDSA from "./algorithm/ECDSA.js";
|
|
4
|
-
import RSAPSS from "./algorithm/RSAPSS.js";
|
|
5
1
|
import _KeyGenerator from "./KeyGenerator.js";
|
|
6
|
-
import _JwtUtils from "./JwtUtils.js";
|
|
7
2
|
import _Algorithm from "./algorithm/Algorithm.js";
|
|
3
|
+
import _JwtUtils from "./JwtUtils.js";
|
|
4
|
+
import _KeyEntry from "./KeyEntry.js";
|
|
8
5
|
import _Jwt from "./Jwt.js";
|
|
9
6
|
export { KeyGenerator } from "./KeyGenerator.js";
|
|
10
7
|
export { Algorithm } from "./algorithm/Algorithm.js";
|
|
11
8
|
export { JwtUtils } from "./JwtUtils.js";
|
|
9
|
+
export { KeyEntry as KIDEntry } from "./KeyEntry.js";
|
|
12
10
|
export { Jwt } from "./Jwt.js";
|
|
13
|
-
export declare const AlgorithmMap: {
|
|
14
|
-
HS: typeof HMAC;
|
|
15
|
-
RS: typeof RSA;
|
|
16
|
-
PS: typeof RSAPSS;
|
|
17
|
-
ES: typeof ECDSA;
|
|
18
|
-
};
|
|
19
11
|
export declare class JwtManager {
|
|
20
|
-
|
|
21
|
-
protected
|
|
22
|
-
constructor(
|
|
12
|
+
protected KIDMap: JwtManager.KIDMap;
|
|
13
|
+
protected defaultEntry: JwtManager.KeyEntry;
|
|
14
|
+
constructor(entry: JwtManager.KeyOption, ...entries: JwtManager.KeyOption[]);
|
|
15
|
+
/**
|
|
16
|
+
* Adds a new key entry to the manager's KID map.
|
|
17
|
+
* The provided entry can be either an instance of `KIDEntry` or an object containing the necessary properties to create a new `KIDEntry`.
|
|
18
|
+
* The method normalizes the entry using the `normalizeEntry` static method and then adds it to the KID map using the entry's `kid` property as the key.
|
|
19
|
+
* @param entry The key entry to be added, which can be either an instance of `KIDEntry` or an object containing the properties needed to create a new `KIDEntry`.
|
|
20
|
+
*/
|
|
21
|
+
addKey(entry: JwtManager.KeyOption): void;
|
|
22
|
+
/**
|
|
23
|
+
* Removes the key associated with the specified key ID (kid) from the manager's KID map.
|
|
24
|
+
* If the specified kid does not exist in the map, this method does nothing.
|
|
25
|
+
* @param kid The key ID of the key to be removed from the manager's KID map.
|
|
26
|
+
*/
|
|
27
|
+
delKey(kid: string): void;
|
|
23
28
|
/**
|
|
24
29
|
* Signs the given payload with the specified header using the configured algorithm and key.
|
|
25
30
|
* @param payload The payload to be signed, represented as a JavaScript object.
|
|
26
|
-
* @param header The header to be included in the JWT, represented as a JavaScript object.
|
|
31
|
+
* @param header The header to be included in the JWT, represented as a JavaScript object.
|
|
32
|
+
* - The `alg` property will be automatically set based on the manager's algorithm.
|
|
27
33
|
* @returns The complete JWT as a string, consisting of the base64url-encoded header, payload, and signature.
|
|
28
34
|
*/
|
|
29
|
-
sign(payload: JwtManager.Jwt.Payload, header
|
|
35
|
+
sign(payload: JwtManager.Jwt.Payload, header?: Partial<JwtManager.Jwt.Header>): string;
|
|
30
36
|
parse(token: string): JwtManager.Jwt;
|
|
31
|
-
toJSON(): object;
|
|
32
|
-
toString(): string;
|
|
33
37
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* @param
|
|
37
|
-
* @returns
|
|
38
|
+
* Normalizes a given key entry option into a `KIDEntry` instance. If the provided entry is already an instance of `KIDEntry`, it is returned as-is.
|
|
39
|
+
* If the entry is provided as an object containing the necessary properties to create a new `KIDEntry`.
|
|
40
|
+
* @param entry The key entry option to be normalized, which can be either an instance of `KIDEntry` or an object containing the properties needed to create a new `KIDEntry`.
|
|
41
|
+
* @returns A `KIDEntry` instance corresponding to the provided entry option.
|
|
38
42
|
*/
|
|
39
|
-
|
|
43
|
+
static normalizeEntry(entry: JwtManager.KeyOption): JwtManager.KeyEntry;
|
|
40
44
|
}
|
|
41
45
|
export declare namespace JwtManager {
|
|
42
46
|
export import Algorithm = _Algorithm;
|
|
43
47
|
export import KeyGenerator = _KeyGenerator;
|
|
44
48
|
export import JwtUtils = _JwtUtils;
|
|
49
|
+
export import KeyEntry = _KeyEntry;
|
|
45
50
|
export import Jwt = _Jwt;
|
|
46
|
-
type
|
|
47
|
-
|
|
51
|
+
type KeyOption = KeyEntry | KeyEntry.KeyEntryOptions;
|
|
52
|
+
interface KIDMap {
|
|
53
|
+
[kid: string]: KeyEntry;
|
|
54
|
+
}
|
|
48
55
|
}
|
|
49
56
|
export default JwtManager;
|
|
@@ -1,73 +1,94 @@
|
|
|
1
|
-
import RSA from "./algorithm/RSA.js";
|
|
2
|
-
import HMAC from "./algorithm/HMAC.js";
|
|
3
|
-
import ECDSA from "./algorithm/ECDSA.js";
|
|
4
|
-
import RSAPSS from "./algorithm/RSAPSS.js";
|
|
5
1
|
import _KeyGenerator from "./KeyGenerator.js";
|
|
6
|
-
import _JwtUtils from "./JwtUtils.js";
|
|
7
2
|
import _Algorithm from "./algorithm/Algorithm.js";
|
|
3
|
+
import _JwtUtils from "./JwtUtils.js";
|
|
4
|
+
import _KeyEntry from "./KeyEntry.js";
|
|
8
5
|
import _Jwt from "./Jwt.js";
|
|
9
6
|
export { KeyGenerator } from "./KeyGenerator.js";
|
|
10
7
|
export { Algorithm } from "./algorithm/Algorithm.js";
|
|
11
8
|
export { JwtUtils } from "./JwtUtils.js";
|
|
9
|
+
export { KeyEntry as KIDEntry } from "./KeyEntry.js";
|
|
12
10
|
export { Jwt } from "./Jwt.js";
|
|
13
|
-
export const AlgorithmMap = {
|
|
14
|
-
HS: HMAC,
|
|
15
|
-
RS: RSA,
|
|
16
|
-
PS: RSAPSS,
|
|
17
|
-
ES: ECDSA
|
|
18
|
-
};
|
|
19
11
|
export class JwtManager {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
constructor(
|
|
23
|
-
this.
|
|
24
|
-
|
|
12
|
+
KIDMap = {};
|
|
13
|
+
defaultEntry;
|
|
14
|
+
constructor(entry, ...entries) {
|
|
15
|
+
this.defaultEntry = JwtManager.normalizeEntry(entry);
|
|
16
|
+
entries = [this.defaultEntry, ...entries];
|
|
17
|
+
for (const e of entries) {
|
|
18
|
+
this.addKey(e);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Adds a new key entry to the manager's KID map.
|
|
23
|
+
* The provided entry can be either an instance of `KIDEntry` or an object containing the necessary properties to create a new `KIDEntry`.
|
|
24
|
+
* The method normalizes the entry using the `normalizeEntry` static method and then adds it to the KID map using the entry's `kid` property as the key.
|
|
25
|
+
* @param entry The key entry to be added, which can be either an instance of `KIDEntry` or an object containing the properties needed to create a new `KIDEntry`.
|
|
26
|
+
*/
|
|
27
|
+
addKey(entry) {
|
|
28
|
+
const kidEntry = JwtManager.normalizeEntry(entry);
|
|
29
|
+
this.KIDMap[kidEntry.kid] = kidEntry;
|
|
25
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Removes the key associated with the specified key ID (kid) from the manager's KID map.
|
|
33
|
+
* If the specified kid does not exist in the map, this method does nothing.
|
|
34
|
+
* @param kid The key ID of the key to be removed from the manager's KID map.
|
|
35
|
+
*/
|
|
36
|
+
delKey(kid) { delete this.KIDMap[kid]; }
|
|
26
37
|
/**
|
|
27
38
|
* Signs the given payload with the specified header using the configured algorithm and key.
|
|
28
39
|
* @param payload The payload to be signed, represented as a JavaScript object.
|
|
29
|
-
* @param header The header to be included in the JWT, represented as a JavaScript object.
|
|
40
|
+
* @param header The header to be included in the JWT, represented as a JavaScript object.
|
|
41
|
+
* - The `alg` property will be automatically set based on the manager's algorithm.
|
|
30
42
|
* @returns The complete JWT as a string, consisting of the base64url-encoded header, payload, and signature.
|
|
31
43
|
*/
|
|
32
|
-
sign(payload, header) {
|
|
33
|
-
|
|
44
|
+
sign(payload, header = {}) {
|
|
45
|
+
const kid = header.kid || this.defaultEntry.kid;
|
|
46
|
+
const entry = this.KIDMap[kid];
|
|
47
|
+
if (!entry)
|
|
48
|
+
throw new Error(`No key found for kid: ${kid}`);
|
|
49
|
+
const { signer, alg } = entry;
|
|
50
|
+
header = { ...header, alg, kid, typ: 'jwt' };
|
|
34
51
|
const encodedHeader = JwtManager.JwtUtils.objectToBase64Url(header);
|
|
35
52
|
const encodedPayload = JwtManager.JwtUtils.objectToBase64Url(payload);
|
|
36
53
|
const content = `${encodedHeader}.${encodedPayload}`;
|
|
37
|
-
const signature =
|
|
54
|
+
const signature = signer.sign(content);
|
|
38
55
|
return `${content}.${signature}`;
|
|
39
56
|
}
|
|
40
57
|
parse(token) {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
const { header, payload, signature, encodedHeader, encodedPayload } = JwtManager.JwtUtils.getJwtParts(token);
|
|
59
|
+
const kid = header.kid || this.defaultEntry.kid;
|
|
60
|
+
const entry = this.KIDMap[kid];
|
|
61
|
+
if (!entry)
|
|
62
|
+
throw new Error(`No key found for kid: ${kid}`);
|
|
63
|
+
const signer = entry.signer;
|
|
64
|
+
const content = `${encodedHeader}.${encodedPayload}`;
|
|
65
|
+
if (!signer.verify(content, signature))
|
|
66
|
+
throw new Error('Invalid signature');
|
|
67
|
+
const jwt = new JwtManager.Jwt(header, payload, signature, signer);
|
|
68
|
+
if (jwt.expired)
|
|
69
|
+
throw new Error('Token has expired');
|
|
70
|
+
return jwt;
|
|
51
71
|
}
|
|
52
|
-
toJSON() { return { algorithm: this.algorithmName }; }
|
|
53
|
-
toString() { return `Jwt manager with algorithm ${this.algorithmName}`; }
|
|
72
|
+
// toJSON(): object { return { algorithm: this.algorithmName }; }
|
|
73
|
+
// toString(): string { return `Jwt manager with algorithm ${this.algorithmName}`; }
|
|
54
74
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* @param
|
|
58
|
-
* @returns
|
|
75
|
+
* Normalizes a given key entry option into a `KIDEntry` instance. If the provided entry is already an instance of `KIDEntry`, it is returned as-is.
|
|
76
|
+
* If the entry is provided as an object containing the necessary properties to create a new `KIDEntry`.
|
|
77
|
+
* @param entry The key entry option to be normalized, which can be either an instance of `KIDEntry` or an object containing the properties needed to create a new `KIDEntry`.
|
|
78
|
+
* @returns A `KIDEntry` instance corresponding to the provided entry option.
|
|
59
79
|
*/
|
|
60
|
-
static
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const alg =
|
|
64
|
-
return new
|
|
80
|
+
static normalizeEntry(entry) {
|
|
81
|
+
if (entry instanceof JwtManager.KeyEntry)
|
|
82
|
+
return entry;
|
|
83
|
+
const { alg: algorithm, key, kid } = entry;
|
|
84
|
+
return new JwtManager.KeyEntry(algorithm, key, kid);
|
|
65
85
|
}
|
|
66
86
|
}
|
|
67
87
|
(function (JwtManager) {
|
|
68
88
|
JwtManager.Algorithm = _Algorithm;
|
|
69
89
|
JwtManager.KeyGenerator = _KeyGenerator;
|
|
70
90
|
JwtManager.JwtUtils = _JwtUtils;
|
|
91
|
+
JwtManager.KeyEntry = _KeyEntry;
|
|
71
92
|
JwtManager.Jwt = _Jwt;
|
|
72
93
|
})(JwtManager || (JwtManager = {}));
|
|
73
94
|
export default JwtManager;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JwtManager.js","sourceRoot":"","sources":["../../../src/beta/JwtManager/JwtManager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"JwtManager.js","sourceRoot":"","sources":["../../../src/beta/JwtManager/JwtManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type Algorithm from "./algorithm/Algorithm.js";
|
|
2
|
-
import type
|
|
2
|
+
import type KeyEntry from "./KeyEntry.js";
|
|
3
|
+
import type Jwt from './Jwt.js';
|
|
3
4
|
export declare class JwtUtils {
|
|
5
|
+
/**
|
|
6
|
+
* Returns the current time in seconds since the Unix epoch. This is commonly used for setting the `iat` (issued at) and `exp` (expiration) claims in JWTs.
|
|
7
|
+
* @returns The current time in seconds since January 1, 1970 (Unix epoch).
|
|
8
|
+
*/
|
|
9
|
+
static nowInSeconds(): number;
|
|
4
10
|
/**
|
|
5
11
|
* Converts a JavaScript object to a base64url-encoded string.
|
|
6
12
|
* @param obj The object to be converted.
|
|
@@ -19,14 +25,23 @@ export declare class JwtUtils {
|
|
|
19
25
|
* @returns The algorithm prefix (e.g., "HS", "RS", "ES", "PS").
|
|
20
26
|
* @throws An error if the algorithm is unsupported.
|
|
21
27
|
*/
|
|
22
|
-
static getAlgPrefix(alg:
|
|
28
|
+
static getAlgPrefix(alg: KeyEntry.AlgorithmName): KeyEntry.AlgPrefix;
|
|
23
29
|
/**
|
|
24
30
|
* Extracts the hash length from the given algorithm name and validates it.
|
|
25
31
|
* @param alg The algorithm name (e.g., "HS256", "RS384", "ES512").
|
|
26
32
|
* @returns The hash length as a string ("256", "384", or "512").
|
|
27
33
|
* @throws An error if the algorithm is unsupported or if the hash length is invalid.
|
|
28
34
|
*/
|
|
29
|
-
static getHashLength(alg:
|
|
35
|
+
static getHashLength(alg: KeyEntry.AlgorithmName): Algorithm.HashLength;
|
|
36
|
+
static getJwtParts(token: string): JwtUtils.JwtParts;
|
|
37
|
+
}
|
|
38
|
+
export declare namespace JwtUtils {
|
|
39
|
+
interface JwtParts {
|
|
40
|
+
encodedHeader: string;
|
|
41
|
+
encodedPayload: string;
|
|
42
|
+
signature: string;
|
|
43
|
+
header: Jwt.Header;
|
|
44
|
+
payload: Jwt.Payload;
|
|
45
|
+
}
|
|
30
46
|
}
|
|
31
|
-
export declare namespace JwtUtils { }
|
|
32
47
|
export default JwtUtils;
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
+
import HeaderValidator from "./HeaderValidator.js";
|
|
1
2
|
export class JwtUtils {
|
|
3
|
+
/**
|
|
4
|
+
* Returns the current time in seconds since the Unix epoch. This is commonly used for setting the `iat` (issued at) and `exp` (expiration) claims in JWTs.
|
|
5
|
+
* @returns The current time in seconds since January 1, 1970 (Unix epoch).
|
|
6
|
+
*/
|
|
7
|
+
static nowInSeconds() {
|
|
8
|
+
return Math.floor(Date.now() / 1000);
|
|
9
|
+
}
|
|
2
10
|
/**
|
|
3
11
|
* Converts a JavaScript object to a base64url-encoded string.
|
|
4
12
|
* @param obj The object to be converted.
|
|
@@ -45,6 +53,16 @@ export class JwtUtils {
|
|
|
45
53
|
}
|
|
46
54
|
return hashLength;
|
|
47
55
|
}
|
|
56
|
+
static getJwtParts(token) {
|
|
57
|
+
const parts = token.split('.');
|
|
58
|
+
if (parts.length !== 3)
|
|
59
|
+
throw new Error('Invalid JWT format');
|
|
60
|
+
const [encodedHeader, encodedPayload, signature] = parts;
|
|
61
|
+
const header = this.base64UrlToObject(encodedHeader);
|
|
62
|
+
HeaderValidator.validate(header);
|
|
63
|
+
const payload = this.base64UrlToObject(encodedPayload);
|
|
64
|
+
return { encodedHeader, encodedPayload, signature, header, payload };
|
|
65
|
+
}
|
|
48
66
|
}
|
|
49
67
|
export default JwtUtils;
|
|
50
68
|
//# sourceMappingURL=JwtUtils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JwtUtils.js","sourceRoot":"","sources":["../../../src/beta/JwtManager/JwtUtils.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,QAAQ;IACjB;;;;OAIG;IACI,MAAM,CAAC,iBAAiB,CAAC,GAAW;QACvC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAChD,OAAO,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACxC,CAAC;IACD;;;;OAIG;IACI,MAAM,CAAC,iBAAiB,CAAI,SAAiB;QAChD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QACnD,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAM,CAAC;IACvC,CAAC;IACD;;;;;OAKG;IACI,MAAM,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"JwtUtils.js","sourceRoot":"","sources":["../../../src/beta/JwtManager/JwtUtils.ts"],"names":[],"mappings":"AAGA,OAAO,eAAe,MAAM,sBAAsB,CAAC;AAEnD,MAAM,OAAO,QAAQ;IACjB;;;OAGG;IACI,MAAM,CAAC,YAAY;QACtB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IACzC,CAAC;IACD;;;;OAIG;IACI,MAAM,CAAC,iBAAiB,CAAC,GAAW;QACvC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAChD,OAAO,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACxC,CAAC;IACD;;;;OAIG;IACI,MAAM,CAAC,iBAAiB,CAAI,SAAiB;QAChD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QACnD,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAM,CAAC;IACvC,CAAC;IACD;;;;;OAKG;IACI,MAAM,CAAC,YAAY,CAAC,GAA2B;QAClD,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/B,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,EAAE,CAAC,CAAC;QACrD,CAAC;QACD,OAAO,MAA4B,CAAC;IACxC,CAAC;IACD;;;;;OAKG;IACI,MAAM,CAAC,aAAa,CAAC,GAA2B;QACnD,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAChC,IAAI,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,EAAE,CAAC,CAAC;QACrD,CAAC;QACD,OAAO,UAAkC,CAAC;IAC9C,CAAC;IACM,MAAM,CAAC,WAAW,CAAC,KAAa;QACnC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;QAE9D,MAAM,CAAE,aAAa,EAAE,cAAc,EAAE,SAAS,CAAE,GAAG,KAAK,CAAC;QAC3D,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAa,aAAa,CAAC,CAAC;QACjE,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAEjC,MAAM,OAAO,GAAG,IAAI,CAAC,iBAAiB,CAAc,cAAc,CAAC,CAAC;QAEpE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IACzE,CAAC;CACJ;AAUD,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type JwtManager from "./JwtManager.js";
|
|
2
|
+
import Algorithm from "./algorithm/Algorithm.js";
|
|
3
|
+
import RSAPSS from "./algorithm/RSAPSS.js";
|
|
4
|
+
import ECDSA from "./algorithm/ECDSA.js";
|
|
5
|
+
import HMAC from "./algorithm/HMAC.js";
|
|
6
|
+
import RSA from "./algorithm/RSA.js";
|
|
7
|
+
export declare class KeyEntry {
|
|
8
|
+
readonly alg: KeyEntry.AlgorithmName;
|
|
9
|
+
protected static AlgorithmMap: {
|
|
10
|
+
HS: typeof HMAC;
|
|
11
|
+
RS: typeof RSA;
|
|
12
|
+
ES: typeof ECDSA;
|
|
13
|
+
PS: typeof RSAPSS;
|
|
14
|
+
};
|
|
15
|
+
readonly kid: string;
|
|
16
|
+
readonly signer: Algorithm;
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new KIDEntry instance with the specified algorithm, key, and optional key ID (kid).
|
|
19
|
+
* If the kid is not provided, it generates a random one based on the algorithm name and a random string.
|
|
20
|
+
* The signer is created using the `createSigner` static method, which determines the appropriate signing algorithm based on the provided algorithm name and key.
|
|
21
|
+
* @param alg The name of the signing algorithm (e.g., "HS256", "RS384", "ES512").
|
|
22
|
+
* @param key The key or key options to be used for signing, which can be an instance of `Algorithm` or an object containing key options.
|
|
23
|
+
* @param kid An optional key ID to uniquely identify the key. If not provided, a random one will be generated.
|
|
24
|
+
*/
|
|
25
|
+
constructor(alg: KeyEntry.AlgorithmName, key: KeyEntry.Signer, kid?: string);
|
|
26
|
+
/**
|
|
27
|
+
* Creates an instance of the appropriate signing algorithm based on the provided algorithm name and key.
|
|
28
|
+
* It extracts the algorithm prefix and hash length from the algorithm name,
|
|
29
|
+
* looks up the corresponding algorithm class in the `AlgorithmMap`,and returns a new instance of that class initialized
|
|
30
|
+
* with the specified hash length and key.
|
|
31
|
+
* @param alg The algorithm name (e.g., "HS256", "RS384", "ES512").
|
|
32
|
+
* @param key The key or key options to be used for signing, which can be an instance of `Algorithm` or an object containing key options.
|
|
33
|
+
* @returns An instance of the appropriate signing algorithm initialized with the specified hash length and key.
|
|
34
|
+
*/
|
|
35
|
+
static createSigner(alg: KeyEntry.AlgorithmName, key: Algorithm.Key | Algorithm.KeyOptions): Algorithm;
|
|
36
|
+
}
|
|
37
|
+
export declare namespace KeyEntry {
|
|
38
|
+
type AlgPrefix = 'HS' | 'RS' | 'ES' | 'PS';
|
|
39
|
+
type AlgorithmName = `${AlgPrefix}${JwtManager.Algorithm.HashLength}`;
|
|
40
|
+
type Signer = Algorithm | Algorithm.Key | Algorithm.KeyOptions;
|
|
41
|
+
interface KeyEntryOptions {
|
|
42
|
+
key: Signer;
|
|
43
|
+
alg: AlgorithmName;
|
|
44
|
+
kid?: string;
|
|
45
|
+
}
|
|
46
|
+
interface KeyEntry extends KeyEntryOptions {
|
|
47
|
+
kid: string;
|
|
48
|
+
signer: Algorithm;
|
|
49
|
+
alg: AlgorithmName;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export default KeyEntry;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import Algorithm from "./algorithm/Algorithm.js";
|
|
2
|
+
import RSAPSS from "./algorithm/RSAPSS.js";
|
|
3
|
+
import ECDSA from "./algorithm/ECDSA.js";
|
|
4
|
+
import HMAC from "./algorithm/HMAC.js";
|
|
5
|
+
import RSA from "./algorithm/RSA.js";
|
|
6
|
+
import JwtUtils from "./JwtUtils.js";
|
|
7
|
+
export class KeyEntry {
|
|
8
|
+
alg;
|
|
9
|
+
static AlgorithmMap = { HS: HMAC, RS: RSA, ES: ECDSA, PS: RSAPSS };
|
|
10
|
+
kid;
|
|
11
|
+
signer;
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new KIDEntry instance with the specified algorithm, key, and optional key ID (kid).
|
|
14
|
+
* If the kid is not provided, it generates a random one based on the algorithm name and a random string.
|
|
15
|
+
* The signer is created using the `createSigner` static method, which determines the appropriate signing algorithm based on the provided algorithm name and key.
|
|
16
|
+
* @param alg The name of the signing algorithm (e.g., "HS256", "RS384", "ES512").
|
|
17
|
+
* @param key The key or key options to be used for signing, which can be an instance of `Algorithm` or an object containing key options.
|
|
18
|
+
* @param kid An optional key ID to uniquely identify the key. If not provided, a random one will be generated.
|
|
19
|
+
*/
|
|
20
|
+
constructor(alg, key, kid) {
|
|
21
|
+
this.alg = alg;
|
|
22
|
+
this.kid = kid || `${alg}-${Math.random().toString(36).substring(2, 8)}`;
|
|
23
|
+
this.signer = key instanceof Algorithm ? key : KeyEntry.createSigner(alg, key);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Creates an instance of the appropriate signing algorithm based on the provided algorithm name and key.
|
|
27
|
+
* It extracts the algorithm prefix and hash length from the algorithm name,
|
|
28
|
+
* looks up the corresponding algorithm class in the `AlgorithmMap`,and returns a new instance of that class initialized
|
|
29
|
+
* with the specified hash length and key.
|
|
30
|
+
* @param alg The algorithm name (e.g., "HS256", "RS384", "ES512").
|
|
31
|
+
* @param key The key or key options to be used for signing, which can be an instance of `Algorithm` or an object containing key options.
|
|
32
|
+
* @returns An instance of the appropriate signing algorithm initialized with the specified hash length and key.
|
|
33
|
+
*/
|
|
34
|
+
static createSigner(alg, key) {
|
|
35
|
+
const prefix = JwtUtils.getAlgPrefix(alg);
|
|
36
|
+
const hashLength = JwtUtils.getHashLength(alg);
|
|
37
|
+
const algorithm = this.AlgorithmMap[prefix];
|
|
38
|
+
return new algorithm(hashLength, key);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export default KeyEntry;
|
|
42
|
+
//# sourceMappingURL=KeyEntry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KeyEntry.js","sourceRoot":"","sources":["../../../src/beta/JwtManager/KeyEntry.ts"],"names":[],"mappings":"AAEA,OAAO,SAAS,MAAM,0BAA0B,CAAC;AACjD,OAAO,MAAM,MAAM,uBAAuB,CAAC;AAC3C,OAAO,KAAK,MAAM,sBAAsB,CAAC;AACzC,OAAO,IAAI,MAAM,qBAAqB,CAAC;AACvC,OAAO,GAAG,MAAM,oBAAoB,CAAC;AAErC,OAAO,QAAQ,MAAM,eAAe,CAAC;AAErC,MAAM,OAAO,QAAQ;IAeG;IAdV,MAAM,CAAC,YAAY,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;IAE7D,GAAG,CAAS;IACZ,MAAM,CAAY;IAElC;;;;;;;OAOG;IACH,YACoB,GAA2B,EAC3C,GAAoB,EAAE,GAAY;QADlB,QAAG,GAAH,GAAG,CAAwB;QAG3C,IAAI,CAAC,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;QACzE,IAAI,CAAC,MAAM,GAAG,GAAG,YAAY,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACnF,CAAC;IACD;;;;;;;;OAQG;IACI,MAAM,CAAC,YAAY,CAAC,GAA2B,EAAE,GAAyC;QAC7F,MAAM,MAAM,GAAG,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QAC1C,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QAE/C,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAC5C,OAAO,IAAI,SAAS,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IAC1C,CAAC;;AAmBL,eAAe,QAAQ,CAAC"}
|
|
@@ -23,6 +23,11 @@ export declare class KeyGenerator {
|
|
|
23
23
|
* If no modulus length is provided, it defaults to 2048 bits.
|
|
24
24
|
* @param modulusLength The length of the RSA modulus in bits (e.g., 2048, 4096). Defaults to 2048 if not specified.
|
|
25
25
|
* @returns An object containing the generated private key ("key") and public key ("pub") as strings, both in PEM format.
|
|
26
|
+
*
|
|
27
|
+
* NOTE: Standard JWT libraries (RFC 7518) use the same RSA key pair for both RSASSA-PKCS1-v1_5 (RS*)
|
|
28
|
+
* and RSASSA-PSS (PS*) algorithms. The difference is in signature padding/salt, not in key generation.
|
|
29
|
+
* The code below was an initial attempt to generate 'rsa-pss' keys separately, but proved unnecessary.
|
|
30
|
+
* Kept as a historical reference of that misunderstanding.
|
|
26
31
|
*/
|
|
27
32
|
protected static RSAPSS(modulusLength?: number): KeyGenerator.KeyObject;
|
|
28
33
|
/**
|
|
@@ -60,18 +60,21 @@ export class KeyGenerator {
|
|
|
60
60
|
* If no modulus length is provided, it defaults to 2048 bits.
|
|
61
61
|
* @param modulusLength The length of the RSA modulus in bits (e.g., 2048, 4096). Defaults to 2048 if not specified.
|
|
62
62
|
* @returns An object containing the generated private key ("key") and public key ("pub") as strings, both in PEM format.
|
|
63
|
+
*
|
|
64
|
+
* NOTE: Standard JWT libraries (RFC 7518) use the same RSA key pair for both RSASSA-PKCS1-v1_5 (RS*)
|
|
65
|
+
* and RSASSA-PSS (PS*) algorithms. The difference is in signature padding/salt, not in key generation.
|
|
66
|
+
* The code below was an initial attempt to generate 'rsa-pss' keys separately, but proved unnecessary.
|
|
67
|
+
* Kept as a historical reference of that misunderstanding.
|
|
63
68
|
*/
|
|
64
69
|
static RSAPSS(modulusLength = 2048) {
|
|
65
70
|
return this.RSA(modulusLength);
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
});
|
|
74
|
-
return { key: privateKey, pub: publicKey };
|
|
71
|
+
// Initial implementation attempt (unreachable code - left as historical reference):
|
|
72
|
+
// const { privateKey, publicKey } = generateKeyPairSync('rsa-pss', {
|
|
73
|
+
// modulusLength: modulusLength,
|
|
74
|
+
// publicKeyEncoding: { type: 'spki', format: 'pem' },
|
|
75
|
+
// privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
|
|
76
|
+
// });
|
|
77
|
+
// return { key: privateKey, pub: publicKey };
|
|
75
78
|
}
|
|
76
79
|
/**
|
|
77
80
|
* Generates an Elliptic Curve (EC) key pair using the specified named curve.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KeyGenerator.js","sourceRoot":"","sources":["../../../src/beta/JwtManager/KeyGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAG1D,MAAM,OAAO,YAAY;IACrB;;;;;;;;;OASG;IACI,MAAM,CAAC,QAAQ,CAAC,IAAuB,EAAE,UAAgC,EAAE;QAC9E,IAAI,UAAyB,CAAC;QAC9B,IAAI,SAAwB,CAAC;QAE7B,QAAO,IAAI,EAAE,CAAC;YACV,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACZ,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;gBAC9E,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC;gBAAC,MAAM;YAC3C,CAAC;YACD,KAAK,KAAK,CAAC,CAAC,CAAC;gBACT,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;gBAC7D,UAAU,GAAG,GAAG,CAAC;gBAAC,SAAS,GAAG,GAAG,CAAC;gBAAC,MAAM;YAC7C,CAAC;YACD,KAAK,SAAS,CAAC,CAAC,CAAC;gBACb,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;gBAChE,UAAU,GAAG,GAAG,CAAC;gBAAC,SAAS,GAAG,GAAG,CAAC;gBAAC,MAAM;YAC7C,CAAC;YACD,KAAK,IAAI,CAAC,CAAC,CAAC;gBACR,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,YAAY,CAAC,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;gBACzD,UAAU,GAAG,GAAG,CAAC;gBAAC,SAAS,GAAG,GAAG,CAAC;gBAAC,MAAM;YAC7C,CAAC;YACD,OAAO,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;IAC/C,CAAC;IACD;;;;;OAKG;IACO,MAAM,CAAC,GAAG,CAAC,gBAAwB,IAAI;QAC7C,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,mBAAmB,CAAC,KAAK,EAAE;YACzD,aAAa,EAAE,aAAa;YAC5B,iBAAiB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE;YAClD,kBAAkB,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE;SACvD,CAAC,CAAC;QACH,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;IAC/C,CAAC;IACD
|
|
1
|
+
{"version":3,"file":"KeyGenerator.js","sourceRoot":"","sources":["../../../src/beta/JwtManager/KeyGenerator.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
import JwtManager from "../../build/beta/JwtManager/JwtManager.js";
|
|
3
2
|
import { strict as assert } from 'assert';
|
|
4
3
|
|
|
5
|
-
|
|
4
|
+
import { Beta, Logger } from "../../build/Vortez.js";
|
|
5
|
+
|
|
6
|
+
const JwtManager = Beta.JwtManager;
|
|
7
|
+
const logger = new Logger({prefix: 'JWT'});
|
|
8
|
+
|
|
9
|
+
/** @type { Beta.JwtManager.Algorithm.HashLength[] } */
|
|
6
10
|
const MASKS = ['256', '384', '512'];
|
|
7
11
|
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
+
const RSA_KEY = JwtManager.KeyGenerator.generate('rsa');
|
|
13
|
+
const PSS_KEY = JwtManager.KeyGenerator.generate('rsa-pss');
|
|
14
|
+
const EC_KEY = JwtManager.KeyGenerator.generate('ec');
|
|
15
|
+
const SECRET = JwtManager.KeyGenerator.generate('secret');
|
|
12
16
|
|
|
13
17
|
const payload = {
|
|
14
18
|
uuid: '00000000-0000-0000-0000-000000000000',
|
|
@@ -19,60 +23,320 @@ const payload = {
|
|
|
19
23
|
const header = {
|
|
20
24
|
exp: Math.floor((Date.now() + 10000) / 1000)
|
|
21
25
|
};
|
|
26
|
+
|
|
27
|
+
let testsPassed = 0;
|
|
28
|
+
let testsFailed = 0;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Logs test results.
|
|
32
|
+
* @param { string } testName The test name.
|
|
33
|
+
* @param { boolean } passed Whether the test passed.
|
|
34
|
+
* @param { unknown | null } error Optional error object for failures.
|
|
35
|
+
*/
|
|
36
|
+
function logTestResult(testName, passed, error = null) {
|
|
37
|
+
if (passed) {
|
|
38
|
+
logger.log(`&C2✓ ${testName}`);
|
|
39
|
+
testsPassed++;
|
|
40
|
+
} else {
|
|
41
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
42
|
+
logger.error(`&C1✗ ${testName}${error ? ': ' + message : ''}`);
|
|
43
|
+
testsFailed++;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
22
47
|
/**
|
|
23
|
-
*
|
|
24
|
-
* @param { JwtManager.AlgorithmName } algorithm The
|
|
25
|
-
* @param {
|
|
26
|
-
* @param { string | null } pubKey The public key to be used for verifying JWTs (required for asymmetric algorithms).
|
|
48
|
+
* Verifies signing and parsing for a given algorithm and key.
|
|
49
|
+
* @param { Beta.JwtManager.KeyEntry.AlgorithmName } algorithm The algorithm under test.
|
|
50
|
+
* @param { Beta.JwtManager.KeyEntry.Signer } key The signing key.
|
|
27
51
|
*/
|
|
28
|
-
|
|
29
|
-
const jwt = new JwtManager(algorithm, {
|
|
30
|
-
key: key,
|
|
31
|
-
pub: pubKey || null
|
|
32
|
-
});
|
|
33
|
-
console.log('--------------------------')
|
|
34
|
-
const token = jwt.sign(payload, header);
|
|
35
|
-
console.log('jwt:', token);
|
|
36
|
-
/* if (algorithm.startsWith('HS')) console.log('secret:', key);
|
|
37
|
-
else {
|
|
38
|
-
console.log('Private Key:', key);
|
|
39
|
-
if (pubKey) console.log('Public Key:', pubKey);
|
|
40
|
-
} */
|
|
52
|
+
function testValidSignVerify(algorithm, key) {
|
|
41
53
|
try {
|
|
54
|
+
const jwt = new JwtManager({
|
|
55
|
+
alg: algorithm,
|
|
56
|
+
key: key,
|
|
57
|
+
kid: 'key-v1'
|
|
58
|
+
});
|
|
59
|
+
const token = jwt.sign(payload, header);
|
|
42
60
|
const parsed = jwt.parse(token);
|
|
43
|
-
|
|
61
|
+
|
|
44
62
|
assert.deepEqual(parsed.payload, payload);
|
|
45
|
-
|
|
63
|
+
assert.equal(parsed.header.alg, algorithm);
|
|
64
|
+
logTestResult(`${algorithm} - Valid token sign/verify`, true);
|
|
46
65
|
} catch (error) {
|
|
47
|
-
|
|
66
|
+
logTestResult(`${algorithm} - Valid token sign/verify`, false, error);
|
|
48
67
|
}
|
|
49
68
|
};
|
|
50
69
|
|
|
51
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Ensures a tampered payload is rejected.
|
|
72
|
+
* @param { Beta.JwtManager.KeyEntry.AlgorithmName } algorithm The algorithm under test.
|
|
73
|
+
* @param { Beta.JwtManager.KeyEntry.Signer } key The signing key.
|
|
74
|
+
*/
|
|
75
|
+
function testTamperedPayload(algorithm, key) {
|
|
52
76
|
try {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
77
|
+
const jwt = new JwtManager({
|
|
78
|
+
alg: algorithm,
|
|
79
|
+
key: key,
|
|
80
|
+
});
|
|
81
|
+
const token = jwt.sign(payload, header);
|
|
82
|
+
|
|
83
|
+
const parts = token.split('.');
|
|
84
|
+
const encodedPayload = JwtManager.JwtUtils.objectToBase64Url({ ...payload, name: 'Hacker' });
|
|
85
|
+
const tamperedToken = `${parts[0]}.${encodedPayload}.${parts[2]}`;
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
jwt.parse(tamperedToken);
|
|
89
|
+
logTestResult(`${algorithm} - Reject tampered payload`, false, new Error('Should have thrown'));
|
|
90
|
+
} catch (error) {
|
|
91
|
+
if (`${error}`.includes('Invalid signature')) {
|
|
92
|
+
logTestResult(`${algorithm} - Reject tampered payload`, true);
|
|
93
|
+
} else {
|
|
94
|
+
logTestResult(`${algorithm} - Reject tampered payload`, false, error);
|
|
95
|
+
}
|
|
69
96
|
}
|
|
97
|
+
} catch (error) {
|
|
98
|
+
logTestResult(`${algorithm} - Reject tampered payload`, false, error);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
70
101
|
|
|
71
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Ensures an expired token is rejected.
|
|
104
|
+
* @param { Beta.JwtManager.KeyEntry.AlgorithmName } algorithm The algorithm under test.
|
|
105
|
+
* @param { Beta.JwtManager.KeyEntry.Signer } key The signing key.
|
|
106
|
+
*/
|
|
107
|
+
function testExpiredToken(algorithm, key) {
|
|
108
|
+
try {
|
|
109
|
+
const jwt = new JwtManager({
|
|
110
|
+
alg: algorithm,
|
|
111
|
+
key: key,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const expiredPayload = { ...payload, exp: Math.floor(Date.now() / 1000) - 1 };
|
|
115
|
+
const token = jwt.sign(expiredPayload, header);
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
jwt.parse(token);
|
|
119
|
+
logTestResult(`${algorithm} - Reject expired token`, false, new Error('Should have thrown'));
|
|
120
|
+
} catch (error) {
|
|
121
|
+
const message = `${error}`;
|
|
122
|
+
if (message.includes('expired')) {
|
|
123
|
+
logTestResult(`${algorithm} - Reject expired token`, true);
|
|
124
|
+
} else {
|
|
125
|
+
logTestResult(`${algorithm} - Reject expired token`, false, message);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
72
128
|
} catch (error) {
|
|
73
|
-
|
|
129
|
+
logTestResult(`${algorithm} - Reject expired token`, false, error);
|
|
74
130
|
}
|
|
75
131
|
};
|
|
76
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Ensures a token cannot be verified with an unrelated key set.
|
|
135
|
+
* @param { Beta.JwtManager.KeyEntry.AlgorithmName } algorithm The algorithm under test.
|
|
136
|
+
* @param { Beta.JwtManager.KeyEntry.Signer } key The signing key.
|
|
137
|
+
*/
|
|
138
|
+
function testMissingKey(algorithm, key) {
|
|
139
|
+
try {
|
|
140
|
+
const jwt = new JwtManager({
|
|
141
|
+
alg: algorithm,
|
|
142
|
+
key: key,
|
|
143
|
+
});
|
|
144
|
+
const token = jwt.sign(payload, header);
|
|
145
|
+
|
|
146
|
+
const replacementKey = algorithm.startsWith('HS')
|
|
147
|
+
? JwtManager.KeyGenerator.generate('secret')
|
|
148
|
+
: algorithm.startsWith('RS')
|
|
149
|
+
? JwtManager.KeyGenerator.generate('rsa')
|
|
150
|
+
: algorithm.startsWith('PS')
|
|
151
|
+
? JwtManager.KeyGenerator.generate('rsa-pss')
|
|
152
|
+
: JwtManager.KeyGenerator.generate('ec');
|
|
153
|
+
|
|
154
|
+
const newJwt = new JwtManager({
|
|
155
|
+
alg: algorithm,
|
|
156
|
+
key: replacementKey,
|
|
157
|
+
kid: `${algorithm}-replacement`
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
newJwt.parse(token);
|
|
162
|
+
logTestResult(`${algorithm} - Reject token with unknown KID`, false, new Error('Should have thrown'));
|
|
163
|
+
} catch (error) {
|
|
164
|
+
const message = `${error}`;
|
|
165
|
+
if (message.includes('No key found')) {
|
|
166
|
+
logTestResult(`${algorithm} - Reject token with unknown KID`, true);
|
|
167
|
+
} else {
|
|
168
|
+
logTestResult(`${algorithm} - Reject token with unknown KID`, false, error);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} catch (error) {
|
|
172
|
+
logTestResult(`${algorithm} - Reject token with unknown KID`, false, error);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Ensures key rotation works.
|
|
178
|
+
* @param { Beta.JwtManager.KeyEntry.AlgorithmName } algorithm The algorithm under test.
|
|
179
|
+
* @param { Beta.JwtManager.KeyEntry.Signer } key The signing key.
|
|
180
|
+
*/
|
|
181
|
+
function testKeyRotation(algorithm, key) {
|
|
182
|
+
try {
|
|
183
|
+
const jwt = new JwtManager({
|
|
184
|
+
alg: algorithm,
|
|
185
|
+
key: key,
|
|
186
|
+
kid: 'key-v1',
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const newKeyObj = algorithm.startsWith('HS')
|
|
190
|
+
? JwtManager.KeyGenerator.generate('secret')
|
|
191
|
+
: algorithm.startsWith('RS')
|
|
192
|
+
? JwtManager.KeyGenerator.generate('rsa')
|
|
193
|
+
: algorithm.startsWith('PS')
|
|
194
|
+
? JwtManager.KeyGenerator.generate('rsa-pss')
|
|
195
|
+
: JwtManager.KeyGenerator.generate('ec');
|
|
196
|
+
|
|
197
|
+
jwt.addKey({
|
|
198
|
+
alg: algorithm,
|
|
199
|
+
key: newKeyObj,
|
|
200
|
+
kid: 'key-v2'
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const token = jwt.sign(payload, { ...header, kid: 'key-v2' });
|
|
204
|
+
|
|
205
|
+
const parsed = jwt.parse(token);
|
|
206
|
+
assert.deepEqual(parsed.payload, payload);
|
|
207
|
+
assert.equal(parsed.header.kid, 'key-v2');
|
|
208
|
+
|
|
209
|
+
logTestResult(`${algorithm} - Key rotation`, true);
|
|
210
|
+
} catch (error) {
|
|
211
|
+
logTestResult(`${algorithm} - Key rotation`, false, error);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Ensures a public-key-only manager can verify but cannot sign.
|
|
217
|
+
* @param { Beta.JwtManager.KeyEntry.AlgorithmName } algorithm
|
|
218
|
+
* @param { { pub: string } } keyObj Object containing the public key.
|
|
219
|
+
*/
|
|
220
|
+
function testReadOnlyPublicKey(algorithm, keyObj) {
|
|
221
|
+
if (algorithm.startsWith('HS')) return;
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
const sharedKid = `${algorithm}-public-only`;
|
|
225
|
+
const verifier = new JwtManager({
|
|
226
|
+
alg: algorithm,
|
|
227
|
+
key: { pub: keyObj.pub },
|
|
228
|
+
kid: sharedKid
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
verifier.sign(payload);
|
|
233
|
+
logTestResult(`${algorithm} - Public key only: Sign blocked`, false, new Error('Should not be able to sign'));
|
|
234
|
+
} catch (error) {
|
|
235
|
+
if (`${error}`.includes('No key provided')) {
|
|
236
|
+
logTestResult(`${algorithm} - Public key only: Sign blocked`, true);
|
|
237
|
+
} else {
|
|
238
|
+
logTestResult(`${algorithm} - Public key only: Sign blocked`, false, error);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const signer = new JwtManager({
|
|
243
|
+
alg: algorithm,
|
|
244
|
+
key: keyObj,
|
|
245
|
+
kid: sharedKid
|
|
246
|
+
});
|
|
247
|
+
const token = signer.sign(payload, { ...header, kid: sharedKid });
|
|
248
|
+
|
|
249
|
+
const parsed = verifier.parse(token);
|
|
250
|
+
assert.deepEqual(parsed.payload, payload);
|
|
251
|
+
logTestResult(`${algorithm} - Public key only: Verify works`, true);
|
|
252
|
+
|
|
253
|
+
} catch (error) {
|
|
254
|
+
logTestResult(`${algorithm} - Public key only`, false, error);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Prevents RS256 to HS256 downgrade attacks.
|
|
259
|
+
*/
|
|
260
|
+
function testAlgorithmConfusionAttack() {
|
|
261
|
+
const algRS = 'RS256';
|
|
262
|
+
const algHS = 'HS256';
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
const manager = new JwtManager({
|
|
266
|
+
alg: algRS,
|
|
267
|
+
key: RSA_KEY,
|
|
268
|
+
kid: 'secure-key'
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const attackerManager = new JwtManager({
|
|
272
|
+
alg: algHS,
|
|
273
|
+
key: RSA_KEY.pub,
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
const maliciousToken = attackerManager.sign({ ...payload, name: 'Attacker' });
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
manager.parse(maliciousToken);
|
|
280
|
+
logTestResult(`Security - RS256 to HS256 Attack`, false, new Error('VULNERABLE: Accepted HS256 signed with RSA Public Key'));
|
|
281
|
+
} catch (error) {
|
|
282
|
+
logTestResult(`Security - RS256 to HS256 Attack blocked`, true);
|
|
283
|
+
}
|
|
284
|
+
} catch (error) {
|
|
285
|
+
logTestResult(`Security - RS256 to HS256 Attack error`, false, error);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Runs all tests for the JwtManager across different algorithms and key types.
|
|
291
|
+
* Logs the results and exits with a non-zero code if any tests failed.
|
|
292
|
+
*/
|
|
293
|
+
const runTests = () => {
|
|
294
|
+
logger.log('\n&C6=== JWT Manager Test Suite ===\n');
|
|
295
|
+
|
|
296
|
+
for (let index = 0; index < MASKS.length; index++) {
|
|
297
|
+
|
|
298
|
+
/** @type {Beta.JwtManager.KeyEntry.AlgorithmName} */
|
|
299
|
+
const algorithm = `HS${MASKS[index]}`;
|
|
300
|
+
/** @type {Beta.JwtManager.KeyEntry.AlgorithmName} */
|
|
301
|
+
const algorithmRS = `RS${MASKS[index]}`;
|
|
302
|
+
/** @type {Beta.JwtManager.KeyEntry.AlgorithmName} */
|
|
303
|
+
const algorithmPS = `PS${MASKS[index]}`;
|
|
304
|
+
/** @type {Beta.JwtManager.KeyEntry.AlgorithmName} */
|
|
305
|
+
const algorithmES = `ES${MASKS[index]}`;
|
|
306
|
+
|
|
307
|
+
testValidSignVerify(algorithm, SECRET);
|
|
308
|
+
testTamperedPayload(algorithm, SECRET);
|
|
309
|
+
testExpiredToken(algorithm, SECRET);
|
|
310
|
+
testMissingKey(algorithm, SECRET);
|
|
311
|
+
testKeyRotation(algorithm, SECRET);
|
|
312
|
+
|
|
313
|
+
testValidSignVerify(algorithmRS, RSA_KEY);
|
|
314
|
+
testTamperedPayload(algorithmRS, RSA_KEY);
|
|
315
|
+
testExpiredToken(algorithmRS, RSA_KEY);
|
|
316
|
+
testMissingKey(algorithmRS, RSA_KEY);
|
|
317
|
+
testReadOnlyPublicKey(algorithmRS, RSA_KEY);
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
testValidSignVerify(algorithmPS, PSS_KEY);
|
|
321
|
+
testTamperedPayload(algorithmPS, PSS_KEY);
|
|
322
|
+
testExpiredToken(algorithmPS, PSS_KEY);
|
|
323
|
+
testReadOnlyPublicKey(algorithmPS, PSS_KEY);
|
|
324
|
+
|
|
325
|
+
testValidSignVerify(algorithmES, EC_KEY);
|
|
326
|
+
testTamperedPayload(algorithmES, EC_KEY);
|
|
327
|
+
testExpiredToken(algorithmES, EC_KEY);
|
|
328
|
+
testReadOnlyPublicKey(algorithmES, EC_KEY);
|
|
329
|
+
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
testAlgorithmConfusionAttack();
|
|
333
|
+
|
|
334
|
+
logger.log(`\n&C6=== Results ===`);
|
|
335
|
+
logger.log(`&C2✓ Passed: ${testsPassed}`);
|
|
336
|
+
logger.log(`&C1✗ Failed: ${testsFailed}`);
|
|
337
|
+
logger.log(`&C3Total: ${testsPassed + testsFailed}\n`);
|
|
338
|
+
|
|
339
|
+
process.exit(testsFailed > 0 ? 1 : 0);
|
|
340
|
+
};
|
|
77
341
|
|
|
78
|
-
runTests();
|
|
342
|
+
runTests();
|