@payello-module/jwt 0.1.5 → 0.2.0
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/dist/JWT.js +4 -6
- package/dist/JwtExtract.d.ts +0 -2
- package/dist/src/JWT.d.ts +46 -0
- package/dist/src/JWT.js +171 -0
- package/dist/src/JWTAlgorithms.d.ts +93 -0
- package/dist/src/JWTAlgorithms.js +81 -0
- package/dist/src/JWTKeyPair.d.ts +28 -0
- package/dist/src/JWTKeyPair.js +1 -0
- package/dist/src/JWTPayload.d.ts +67 -0
- package/dist/src/JWTPayload.js +1 -0
- package/dist/src/JwtError.d.ts +4 -0
- package/dist/src/JwtError.js +6 -0
- package/dist/src/JwtExtract.d.ts +10 -0
- package/dist/src/JwtExtract.js +1 -0
- package/dist/src/JwtExtractOpts.d.ts +3 -0
- package/dist/src/JwtExtractOpts.js +1 -0
- package/dist/src/JwtHeader.d.ts +5 -0
- package/dist/src/JwtHeader.js +1 -0
- package/dist/src/JwtSignOpts.d.ts +5 -0
- package/dist/src/JwtSignOpts.js +1 -0
- package/dist/src/base64_encode_buffer.d.ts +4 -0
- package/dist/src/base64_encode_buffer.js +22 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.js +10 -0
- package/dist/test/test.d.ts +1 -0
- package/dist/test/test.js +46 -0
- package/package.json +2 -1
package/dist/JWT.js
CHANGED
|
@@ -49,17 +49,17 @@ export class JWT {
|
|
|
49
49
|
if (bits.length !== 3) {
|
|
50
50
|
throw new JwtError(`Invalid number of parts in JWT string. Expected 3 but got ${bits.length}`);
|
|
51
51
|
}
|
|
52
|
-
const header = JSON.parse(
|
|
52
|
+
const header = JSON.parse(bits[0]);
|
|
53
53
|
if (!header || !header.typ || header.typ !== "JWT") {
|
|
54
54
|
throw new JwtError("Header invalid or type is not JWT");
|
|
55
55
|
}
|
|
56
|
-
const payload = JSON.parse(
|
|
56
|
+
const payload = JSON.parse(bits[1]);
|
|
57
57
|
if (!payload || !payload.jti) {
|
|
58
58
|
throw new JwtError("Payload invalid or missing jti value");
|
|
59
59
|
}
|
|
60
60
|
// Validates the present of required properties in the payload.
|
|
61
61
|
const requiredProps = opts.requiredProps || ["jti", "iss", "iat"];
|
|
62
|
-
for (const prop
|
|
62
|
+
for (const prop in requiredProps) {
|
|
63
63
|
if (!payload[prop]) {
|
|
64
64
|
throw new JwtError(`Payload missing ${prop} value`);
|
|
65
65
|
}
|
|
@@ -67,9 +67,7 @@ export class JWT {
|
|
|
67
67
|
// Returns an object containing the extracted components of the JWT.
|
|
68
68
|
return {
|
|
69
69
|
header: header,
|
|
70
|
-
headerRaw: bits[0],
|
|
71
70
|
payload: payload,
|
|
72
|
-
payloadRaw: bits[1],
|
|
73
71
|
signature: bits[2]
|
|
74
72
|
};
|
|
75
73
|
}
|
|
@@ -88,7 +86,7 @@ export class JWT {
|
|
|
88
86
|
}
|
|
89
87
|
let verify = false;
|
|
90
88
|
// Preparation of the data to verify the signature.
|
|
91
|
-
const data = `${extracted.
|
|
89
|
+
const data = `${btoa(JSON.stringify(extracted.header))}.${btoa(JSON.stringify(extracted.payload))}`;
|
|
92
90
|
// Verification of the signature based on the algorithm specified in the header.
|
|
93
91
|
switch (extracted.header.alg) {
|
|
94
92
|
case 'HS256':
|
package/dist/JwtExtract.d.ts
CHANGED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { JwtHeader } from "./JwtHeader";
|
|
2
|
+
import { JwtExtract } from "./JwtExtract";
|
|
3
|
+
import { JwtExtractOpts } from "./JwtExtractOpts";
|
|
4
|
+
import { JWTAlgorithm } from "./JWTAlgorithms";
|
|
5
|
+
import { JWTPayload } from "./JWTPayload";
|
|
6
|
+
import { JWTKeyPair } from "./JWTKeyPair";
|
|
7
|
+
/**
|
|
8
|
+
* Class representing JSON Web Tokens (JWT) functionalities, including signing, extracting components,
|
|
9
|
+
* and verifying the signature of the token.
|
|
10
|
+
*/
|
|
11
|
+
export declare class JWT {
|
|
12
|
+
private static algExportFormat;
|
|
13
|
+
/**
|
|
14
|
+
* Generates a new key pair for the given algorithm
|
|
15
|
+
* @param alg JWT algorithm to generate for
|
|
16
|
+
* @returns
|
|
17
|
+
*/
|
|
18
|
+
static generateKeys(alg?: JWTAlgorithm): Promise<JWTKeyPair>;
|
|
19
|
+
/**
|
|
20
|
+
* Signs the given payload and returns a JWT string.
|
|
21
|
+
* @param payload - The payload of the JWT which is the content that you want to protect.
|
|
22
|
+
* @param alg - The algorithm for signing the JWT
|
|
23
|
+
* @param key - The key for signing the JWT
|
|
24
|
+
* @returns A promise that resolves to the signed JWT string.
|
|
25
|
+
*/
|
|
26
|
+
static sign(payload: JWTPayload, alg: JWTAlgorithm, key: string | BufferSource): Promise<string>;
|
|
27
|
+
/**
|
|
28
|
+
* Extracts and returns the header, payload, and signature components from a JWT string.
|
|
29
|
+
* @param input - The JWT string to be parsed.
|
|
30
|
+
* @param opts - Optional parameters, including the requirements for the presence of required properties in the payload.
|
|
31
|
+
* @returns A promise that resolves to an object containing the separated components of the JWT (header, payload, signature).
|
|
32
|
+
*/
|
|
33
|
+
static extract(input: string, opts?: JwtExtractOpts): Promise<JwtExtract>;
|
|
34
|
+
/**
|
|
35
|
+
* Verifies the given JWT string using the secret key fetched by the given issuer.
|
|
36
|
+
* @param token - The JWT to be verified.
|
|
37
|
+
* @param getVerifyKey - A function to retrieve the verification key based on the header & payload (BufferSource or base64-encoded string)
|
|
38
|
+
* @param throwErrors - If true then will throw JwtError if something fails or the token is not valid
|
|
39
|
+
* @returns A promise that resolves to a boolean indicating whether the JWT has been verified successfully or not.
|
|
40
|
+
* @throws `JwtError | Error`
|
|
41
|
+
*/
|
|
42
|
+
static verifySignature(token: string, getVerifyKey: (header: JwtHeader, payload: JWTPayload) => Promise<BufferSource | string> | BufferSource | string, throwErrors?: boolean): Promise<{
|
|
43
|
+
verified: boolean;
|
|
44
|
+
extracted: JwtExtract | null;
|
|
45
|
+
}>;
|
|
46
|
+
}
|
package/dist/src/JWT.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { JwtError } from "./JwtError";
|
|
2
|
+
import { base64_decode_buffer, base64_decode_urlsafe, base64_encode_buffer, base64_encode_urlsafe } from "./base64_encode_buffer";
|
|
3
|
+
import { JWTAlgorithms } from "./JWTAlgorithms";
|
|
4
|
+
/**
|
|
5
|
+
* Class representing JSON Web Tokens (JWT) functionalities, including signing, extracting components,
|
|
6
|
+
* and verifying the signature of the token.
|
|
7
|
+
*/
|
|
8
|
+
export class JWT {
|
|
9
|
+
static algExportFormat(alg, use) {
|
|
10
|
+
const algName = JWTAlgorithms[alg].name;
|
|
11
|
+
if (["RSASSA-PKCS1-v1_5", "ECDSA", "RSA-PSS"].indexOf(algName) > -1) {
|
|
12
|
+
return use == "sign" ? "pkcs8" : "spki";
|
|
13
|
+
}
|
|
14
|
+
return "raw";
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Generates a new key pair for the given algorithm
|
|
18
|
+
* @param alg JWT algorithm to generate for
|
|
19
|
+
* @returns
|
|
20
|
+
*/
|
|
21
|
+
static async generateKeys(alg = "HS256") {
|
|
22
|
+
if (alg == "HS256" || alg == "HS384" || alg == "HS512") {
|
|
23
|
+
const cryptoKey = await crypto.subtle.generateKey(JWTAlgorithms[alg], true, ["sign", "verify"]);
|
|
24
|
+
const privateKey = await crypto.subtle.exportKey("raw", cryptoKey);
|
|
25
|
+
const key = { raw: privateKey, base64: base64_encode_buffer(privateKey) };
|
|
26
|
+
return { sign: key, verify: key };
|
|
27
|
+
}
|
|
28
|
+
else if (alg == "RS256" || alg == "RS384" || alg == "RS512" || alg == "ES256" || alg == "ES384" || alg == "ES512" || alg == "PS256" || alg == "PS384" || alg == "PS512") {
|
|
29
|
+
const cryptoKey = await crypto.subtle.generateKey(JWTAlgorithms[alg], true, ["sign", "verify"]);
|
|
30
|
+
const privateKey = await crypto.subtle.exportKey(this.algExportFormat(alg, "sign"), cryptoKey.privateKey);
|
|
31
|
+
const publicKey = await crypto.subtle.exportKey(this.algExportFormat(alg, "verify"), cryptoKey.publicKey);
|
|
32
|
+
return { sign: { raw: privateKey, base64: base64_encode_buffer(privateKey) }, verify: { raw: publicKey, base64: base64_encode_buffer(publicKey) } };
|
|
33
|
+
}
|
|
34
|
+
throw new JwtError("Unknown algorithm: " + alg);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Signs the given payload and returns a JWT string.
|
|
38
|
+
* @param payload - The payload of the JWT which is the content that you want to protect.
|
|
39
|
+
* @param alg - The algorithm for signing the JWT
|
|
40
|
+
* @param key - The key for signing the JWT
|
|
41
|
+
* @returns A promise that resolves to the signed JWT string.
|
|
42
|
+
*/
|
|
43
|
+
static async sign(payload, alg, key) {
|
|
44
|
+
if (typeof JWTAlgorithms[alg] == "undefined")
|
|
45
|
+
throw new JwtError("Unknown algorithm");
|
|
46
|
+
const _header = {
|
|
47
|
+
typ: 'JWT',
|
|
48
|
+
alg: alg
|
|
49
|
+
};
|
|
50
|
+
const body = base64_encode_urlsafe(JSON.stringify(_header)) +
|
|
51
|
+
"." +
|
|
52
|
+
base64_encode_urlsafe(JSON.stringify(payload));
|
|
53
|
+
const signingKey = typeof key == "string" ? new Uint8Array(base64_decode_buffer(key)) : key;
|
|
54
|
+
const importedKey = await crypto.subtle.importKey(this.algExportFormat(alg, "sign"), signingKey, JWTAlgorithms[alg], false, ['sign']);
|
|
55
|
+
const signed = await crypto.subtle.sign(JWTAlgorithms[alg], importedKey, new TextEncoder().encode(body));
|
|
56
|
+
// Returns the final JWT token as a concatenation of header, payload, and signature
|
|
57
|
+
return body + "." + base64_encode_buffer(signed);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Extracts and returns the header, payload, and signature components from a JWT string.
|
|
61
|
+
* @param input - The JWT string to be parsed.
|
|
62
|
+
* @param opts - Optional parameters, including the requirements for the presence of required properties in the payload.
|
|
63
|
+
* @returns A promise that resolves to an object containing the separated components of the JWT (header, payload, signature).
|
|
64
|
+
*/
|
|
65
|
+
static async extract(input, opts = {}) {
|
|
66
|
+
const bits = input.split(".");
|
|
67
|
+
// Ensures that the JWT string has three parts: header, payload, and signature.
|
|
68
|
+
if (bits.length !== 3) {
|
|
69
|
+
throw new JwtError(`Invalid number of parts in JWT string. Expected 3 but got ${bits.length}`);
|
|
70
|
+
}
|
|
71
|
+
const header = JSON.parse(base64_decode_urlsafe(bits[0]));
|
|
72
|
+
if (!header || !header.typ || header.typ !== "JWT") {
|
|
73
|
+
throw new JwtError("Header invalid or type is not JWT");
|
|
74
|
+
}
|
|
75
|
+
const payload = JSON.parse(base64_decode_urlsafe(bits[1]));
|
|
76
|
+
if (!payload || typeof payload !== "object") {
|
|
77
|
+
throw new JwtError("Payload invalid");
|
|
78
|
+
}
|
|
79
|
+
// Validates the present of required properties in the payload.
|
|
80
|
+
const requiredProps = opts.requiredProps || [];
|
|
81
|
+
for (const prop of requiredProps) {
|
|
82
|
+
if (!payload[prop]) {
|
|
83
|
+
throw new JwtError(`Payload missing ${prop} value`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const signature = base64_decode_buffer(bits[2]);
|
|
87
|
+
// Returns an object containing the extracted components of the JWT.
|
|
88
|
+
return {
|
|
89
|
+
header: header,
|
|
90
|
+
headerRaw: bits[0],
|
|
91
|
+
payload: payload,
|
|
92
|
+
payloadRaw: bits[1],
|
|
93
|
+
signature: signature,
|
|
94
|
+
signatureRaw: bits[2]
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Verifies the given JWT string using the secret key fetched by the given issuer.
|
|
99
|
+
* @param token - The JWT to be verified.
|
|
100
|
+
* @param getVerifyKey - A function to retrieve the verification key based on the header & payload (BufferSource or base64-encoded string)
|
|
101
|
+
* @param throwErrors - If true then will throw JwtError if something fails or the token is not valid
|
|
102
|
+
* @returns A promise that resolves to a boolean indicating whether the JWT has been verified successfully or not.
|
|
103
|
+
* @throws `JwtError | Error`
|
|
104
|
+
*/
|
|
105
|
+
static async verifySignature(token, getVerifyKey, throwErrors) {
|
|
106
|
+
const extracted = await this.extract(token).catch(e => {
|
|
107
|
+
if (throwErrors)
|
|
108
|
+
throw e;
|
|
109
|
+
});
|
|
110
|
+
if (!extracted) {
|
|
111
|
+
return { verified: false, extracted: null };
|
|
112
|
+
}
|
|
113
|
+
// Fetches the secret key based on the issuer in the payload.
|
|
114
|
+
let verifyKey = await getVerifyKey(extracted.header, extracted.payload);
|
|
115
|
+
if (!verifyKey || (typeof verifyKey !== "string" && typeof verifyKey !== "object")) {
|
|
116
|
+
if (throwErrors)
|
|
117
|
+
throw new JwtError(`Verify key not found`);
|
|
118
|
+
return { verified: false, extracted: null };
|
|
119
|
+
}
|
|
120
|
+
if (typeof verifyKey == "string") {
|
|
121
|
+
try {
|
|
122
|
+
verifyKey = base64_decode_buffer(verifyKey);
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
if (throwErrors)
|
|
126
|
+
throw e;
|
|
127
|
+
return { verified: false, extracted: null };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
let verified = false;
|
|
131
|
+
// Preparation of the data to verify the signature.
|
|
132
|
+
const body = `${extracted.headerRaw}.${extracted.payloadRaw}`;
|
|
133
|
+
try {
|
|
134
|
+
// Verification of the signature based on the algorithm specified in the header.
|
|
135
|
+
switch (extracted.header.alg) {
|
|
136
|
+
case 'HS256':
|
|
137
|
+
case 'HS384':
|
|
138
|
+
case 'HS512':
|
|
139
|
+
const importedSignKey = await crypto.subtle.importKey(this.algExportFormat(extracted.header.alg, "verify"), verifyKey, JWTAlgorithms[extracted.header.alg], false, ['sign']);
|
|
140
|
+
const signed = await crypto.subtle.sign(JWTAlgorithms[extracted.header.alg], importedSignKey, new TextEncoder().encode(body));
|
|
141
|
+
verified = extracted.signatureRaw == base64_encode_buffer(signed);
|
|
142
|
+
break;
|
|
143
|
+
case 'ES256':
|
|
144
|
+
case 'ES384':
|
|
145
|
+
case 'ES512':
|
|
146
|
+
case 'PS256':
|
|
147
|
+
case 'PS384':
|
|
148
|
+
case 'PS512':
|
|
149
|
+
case 'RS256':
|
|
150
|
+
case 'RS384':
|
|
151
|
+
case 'RS512':
|
|
152
|
+
const importedVerifyKey = await crypto.subtle.importKey(this.algExportFormat(extracted.header.alg, "verify"), verifyKey, JWTAlgorithms[extracted.header.alg], false, ['verify']);
|
|
153
|
+
verified = await crypto.subtle.verify(JWTAlgorithms[extracted.header.alg], importedVerifyKey, extracted.signature, new TextEncoder().encode(body));
|
|
154
|
+
break;
|
|
155
|
+
default:
|
|
156
|
+
if (throwErrors)
|
|
157
|
+
throw new JwtError(`Unsupported algorithm`);
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch (e) {
|
|
162
|
+
if (throwErrors)
|
|
163
|
+
throw e;
|
|
164
|
+
}
|
|
165
|
+
if (verified === true)
|
|
166
|
+
return { verified: true, extracted: extracted };
|
|
167
|
+
if (throwErrors)
|
|
168
|
+
throw new JwtError(`Signature not verified`);
|
|
169
|
+
return { verified: false, extracted: null };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
export declare const JWTAlgorithms: {
|
|
2
|
+
readonly HS256: {
|
|
3
|
+
readonly name: "HMAC";
|
|
4
|
+
readonly hash: {
|
|
5
|
+
readonly name: "SHA-256";
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
readonly HS384: {
|
|
9
|
+
readonly name: "HMAC";
|
|
10
|
+
readonly hash: {
|
|
11
|
+
readonly name: "SHA-384";
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
readonly HS512: {
|
|
15
|
+
readonly name: "HMAC";
|
|
16
|
+
readonly hash: {
|
|
17
|
+
readonly name: "SHA-512";
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
readonly RS256: {
|
|
21
|
+
readonly name: "RSASSA-PKCS1-v1_5";
|
|
22
|
+
readonly modulusLength: 2048;
|
|
23
|
+
readonly publicExponent: Uint8Array;
|
|
24
|
+
readonly hash: {
|
|
25
|
+
readonly name: "SHA-256";
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
readonly RS384: {
|
|
29
|
+
readonly name: "RSASSA-PKCS1-v1_5";
|
|
30
|
+
readonly modulusLength: 3072;
|
|
31
|
+
readonly publicExponent: Uint8Array;
|
|
32
|
+
readonly hash: {
|
|
33
|
+
readonly name: "SHA-384";
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
readonly RS512: {
|
|
37
|
+
readonly name: "RSASSA-PKCS1-v1_5";
|
|
38
|
+
readonly modulusLength: 4096;
|
|
39
|
+
readonly publicExponent: Uint8Array;
|
|
40
|
+
readonly hash: {
|
|
41
|
+
readonly name: "SHA-512";
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
readonly ES256: {
|
|
45
|
+
readonly name: "ECDSA";
|
|
46
|
+
readonly namedCurve: "P-256";
|
|
47
|
+
readonly hash: {
|
|
48
|
+
readonly name: "SHA-256";
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
readonly ES384: {
|
|
52
|
+
readonly name: "ECDSA";
|
|
53
|
+
readonly namedCurve: "P-384";
|
|
54
|
+
readonly hash: {
|
|
55
|
+
readonly name: "SHA-384";
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
readonly ES512: {
|
|
59
|
+
readonly name: "ECDSA";
|
|
60
|
+
readonly namedCurve: "P-521";
|
|
61
|
+
readonly hash: {
|
|
62
|
+
readonly name: "SHA-512";
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
readonly PS256: {
|
|
66
|
+
readonly name: "RSA-PSS";
|
|
67
|
+
readonly modulusLength: 2048;
|
|
68
|
+
readonly publicExponent: Uint8Array;
|
|
69
|
+
readonly saltLength: 32;
|
|
70
|
+
readonly hash: {
|
|
71
|
+
readonly name: "SHA-256";
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
readonly PS384: {
|
|
75
|
+
readonly name: "RSA-PSS";
|
|
76
|
+
readonly modulusLength: 3072;
|
|
77
|
+
readonly publicExponent: Uint8Array;
|
|
78
|
+
readonly saltLength: 48;
|
|
79
|
+
readonly hash: {
|
|
80
|
+
readonly name: "SHA-384";
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
readonly PS512: {
|
|
84
|
+
readonly name: "RSA-PSS";
|
|
85
|
+
readonly modulusLength: 4096;
|
|
86
|
+
readonly publicExponent: Uint8Array;
|
|
87
|
+
readonly saltLength: 64;
|
|
88
|
+
readonly hash: {
|
|
89
|
+
readonly name: "SHA-512";
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
export type JWTAlgorithm = keyof typeof JWTAlgorithms;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// { [key: string]: AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | AesKeyAlgorithm | RsaPssParams | EcdsaParams | RsaHashedKeyGenParams | EcKeyGenParams; }
|
|
2
|
+
export const JWTAlgorithms = {
|
|
3
|
+
// HMAC using SHA-256
|
|
4
|
+
HS256: {
|
|
5
|
+
name: "HMAC",
|
|
6
|
+
hash: { name: "SHA-256" }
|
|
7
|
+
},
|
|
8
|
+
// HMAC using SHA-384
|
|
9
|
+
HS384: {
|
|
10
|
+
name: "HMAC",
|
|
11
|
+
hash: { name: "SHA-384" }
|
|
12
|
+
},
|
|
13
|
+
// HMAC using SHA-512
|
|
14
|
+
HS512: {
|
|
15
|
+
name: "HMAC",
|
|
16
|
+
hash: { name: "SHA-512" }
|
|
17
|
+
},
|
|
18
|
+
// RSASSA-PKCS1-v1_5 using SHA-256
|
|
19
|
+
RS256: {
|
|
20
|
+
name: "RSASSA-PKCS1-v1_5",
|
|
21
|
+
modulusLength: 2048,
|
|
22
|
+
publicExponent: new Uint8Array([1, 0, 1]),
|
|
23
|
+
hash: { name: "SHA-256" }
|
|
24
|
+
},
|
|
25
|
+
// RSASSA-PKCS1-v1_5 using SHA-384
|
|
26
|
+
RS384: {
|
|
27
|
+
name: "RSASSA-PKCS1-v1_5",
|
|
28
|
+
modulusLength: 3072,
|
|
29
|
+
publicExponent: new Uint8Array([1, 0, 1]),
|
|
30
|
+
hash: { name: "SHA-384" }
|
|
31
|
+
},
|
|
32
|
+
// RSASSA-PKCS1-v1_5 using SHA-512
|
|
33
|
+
RS512: {
|
|
34
|
+
name: "RSASSA-PKCS1-v1_5",
|
|
35
|
+
modulusLength: 4096,
|
|
36
|
+
publicExponent: new Uint8Array([1, 0, 1]),
|
|
37
|
+
hash: { name: "SHA-512" }
|
|
38
|
+
},
|
|
39
|
+
// ECDSA using P-256 and SHA-256
|
|
40
|
+
ES256: {
|
|
41
|
+
name: "ECDSA",
|
|
42
|
+
namedCurve: "P-256",
|
|
43
|
+
hash: { name: "SHA-256" }
|
|
44
|
+
},
|
|
45
|
+
// ECDSA using P-384 and SHA-384
|
|
46
|
+
ES384: {
|
|
47
|
+
name: "ECDSA",
|
|
48
|
+
namedCurve: "P-384",
|
|
49
|
+
hash: { name: "SHA-384" }
|
|
50
|
+
},
|
|
51
|
+
// ECDSA using P-521 and SHA-512
|
|
52
|
+
ES512: {
|
|
53
|
+
name: "ECDSA",
|
|
54
|
+
namedCurve: "P-521",
|
|
55
|
+
hash: { name: "SHA-512" }
|
|
56
|
+
},
|
|
57
|
+
// RSASSA-PSS using SHA-256 and MGF1 with SHA-256
|
|
58
|
+
PS256: {
|
|
59
|
+
name: "RSA-PSS",
|
|
60
|
+
modulusLength: 2048,
|
|
61
|
+
publicExponent: new Uint8Array([1, 0, 1]),
|
|
62
|
+
saltLength: 32,
|
|
63
|
+
hash: { name: "SHA-256" }
|
|
64
|
+
},
|
|
65
|
+
// RSASSA-PSS using SHA-384 and MGF1 with SHA-384
|
|
66
|
+
PS384: {
|
|
67
|
+
name: "RSA-PSS",
|
|
68
|
+
modulusLength: 3072,
|
|
69
|
+
publicExponent: new Uint8Array([1, 0, 1]),
|
|
70
|
+
saltLength: 48,
|
|
71
|
+
hash: { name: "SHA-384" }
|
|
72
|
+
},
|
|
73
|
+
// RSASSA-PSS using SHA-512 and MGF1 with SHA-512
|
|
74
|
+
PS512: {
|
|
75
|
+
name: "RSA-PSS",
|
|
76
|
+
modulusLength: 4096,
|
|
77
|
+
publicExponent: new Uint8Array([1, 0, 1]),
|
|
78
|
+
saltLength: 64,
|
|
79
|
+
hash: { name: "SHA-512" }
|
|
80
|
+
}
|
|
81
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface JWTKeyPair {
|
|
2
|
+
/**
|
|
3
|
+
* The key which should be used for signing (creating) JWTs, such as the server
|
|
4
|
+
*/
|
|
5
|
+
sign: {
|
|
6
|
+
/**
|
|
7
|
+
* Raw bytes of the key
|
|
8
|
+
*/
|
|
9
|
+
raw: ArrayBuffer;
|
|
10
|
+
/**
|
|
11
|
+
* Base64-encoded key
|
|
12
|
+
*/
|
|
13
|
+
base64: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The key which should be used for verifying the JWT, such as the client
|
|
17
|
+
*/
|
|
18
|
+
verify: {
|
|
19
|
+
/**
|
|
20
|
+
* Raw bytes of the key
|
|
21
|
+
*/
|
|
22
|
+
raw: ArrayBuffer;
|
|
23
|
+
/**
|
|
24
|
+
* Base64-encoded key
|
|
25
|
+
*/
|
|
26
|
+
base64: string;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export interface JWTPayload {
|
|
2
|
+
/**
|
|
3
|
+
* "iss" (Issuer) Claim [RFC7519, Section 4.1.1]
|
|
4
|
+
* @description - The "iss" (issuer) claim identifies the principal that issued the JWT.
|
|
5
|
+
* - The processing of this claim is generally application specific.
|
|
6
|
+
* - The "iss" value is a case-sensitive string containing a StringOrURI value.
|
|
7
|
+
* - Use of this claim is OPTIONAL.
|
|
8
|
+
*/
|
|
9
|
+
iss?: string;
|
|
10
|
+
/**
|
|
11
|
+
* "sub" (Subject) Claim [RFC7519, Section 4.1.1]
|
|
12
|
+
* @description - The "sub" (subject) claim identifies the principal that is the subject of the JWT.
|
|
13
|
+
* - The claims in a JWT are normally statements about the subject.
|
|
14
|
+
* - The subject value MUST either be scoped to be locally unique in the context of the issuer or be globally unique.
|
|
15
|
+
* - The processing of this claim is generally application specific.
|
|
16
|
+
* - The "sub" value is a case-sensitive string containing a StringOrURI value.
|
|
17
|
+
* - Use of this claim is OPTIONAL.
|
|
18
|
+
*/
|
|
19
|
+
sub?: string;
|
|
20
|
+
/**
|
|
21
|
+
* "aud" (Audience) Claim [RFC7519, Section 4.1.1]
|
|
22
|
+
* @description - The "aud" (audience) claim identifies the recipients that the JWT is intended for.
|
|
23
|
+
* - Each principal intended to process the JWT MUST identify itself with a value in the audience claim.
|
|
24
|
+
* - If the principal processing the claim does not identify itself with a value in the "aud" claim when this claim is present, then the JWT MUST be rejected.
|
|
25
|
+
* - In the general case, the "aud" value is an array of case- sensitive strings, each containing a StringOrURI value.
|
|
26
|
+
* - In the special case when the JWT has one audience, the "aud" value MAY be a single case-sensitive string containing a StringOrURI value.
|
|
27
|
+
* - The interpretation of audience values is generally application specific.
|
|
28
|
+
* - Use of this claim is OPTIONAL.
|
|
29
|
+
*/
|
|
30
|
+
aud?: string | string[];
|
|
31
|
+
/**
|
|
32
|
+
* "exp" (Expiration Time) Claim [RFC7519, Section 4.1.1]
|
|
33
|
+
* @description - The "exp" (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing.
|
|
34
|
+
* - The processing of the "exp" claim requires that the current date/time MUST be before the expiration date/time listed in the "exp" claim.
|
|
35
|
+
* - Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock skew.
|
|
36
|
+
* - Its value MUST be a number containing a NumericDate value.
|
|
37
|
+
* - Use of this claim is OPTIONAL.
|
|
38
|
+
*/
|
|
39
|
+
exp?: number;
|
|
40
|
+
/**
|
|
41
|
+
* "nbf" (Not Before) Claim [RFC7519, Section 4.1.1]
|
|
42
|
+
* @description - The "nbf" (not before) claim identifies the time before which the JWT MUST NOT be accepted for processing.
|
|
43
|
+
* - The processing of the "nbf" claim requires that the current date/time MUST be after or equal to the not-before date/time listed in the "nbf" claim.
|
|
44
|
+
* - Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock skew.
|
|
45
|
+
* - Its value MUST be a number containing a NumericDate value.
|
|
46
|
+
* - Use of this claim is OPTIONAL.
|
|
47
|
+
*/
|
|
48
|
+
nbf?: number;
|
|
49
|
+
/**
|
|
50
|
+
* "iat" (Issued At) Claim [RFC7519, Section 4.1.1]
|
|
51
|
+
* @description - The "iat" (issued at) claim identifies the time at which the JWT was issued.
|
|
52
|
+
* - This claim can be used to determine the age of the JWT.
|
|
53
|
+
* - Its value MUST be a number containing a NumericDate value.
|
|
54
|
+
* - Use of this claim is OPTIONAL.
|
|
55
|
+
*/
|
|
56
|
+
iat?: number;
|
|
57
|
+
/**
|
|
58
|
+
* "jti" (JWT ID) Claim [RFC7519, Section 4.1.1]
|
|
59
|
+
* @description - The "jti" (JWT ID) claim provides a unique identifier for the JWT.
|
|
60
|
+
* - The identifier value MUST be assigned in a manner that ensures that there is a negligible probability that the same value will be accidentally assigned to a different data object; if the application uses multiple issuers, collisions MUST be prevented among values produced by different issuers as well.
|
|
61
|
+
* - The "jti" claim can be used to prevent the JWT from being replayed.
|
|
62
|
+
* - The "jti" value is a case- sensitive string.
|
|
63
|
+
* - Use of this claim is OPTIONAL.
|
|
64
|
+
*/
|
|
65
|
+
jti?: string;
|
|
66
|
+
[key: string]: any;
|
|
67
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function base64_encode_urlsafe(str: string): string;
|
|
2
|
+
export declare function base64_decode_urlsafe(str: string): string;
|
|
3
|
+
export declare function base64_encode_buffer(buffer: Uint8Array | ArrayBuffer | string): string;
|
|
4
|
+
export declare function base64_decode_buffer(str: string): Uint8Array;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function base64_encode_urlsafe(str) {
|
|
2
|
+
return btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
3
|
+
}
|
|
4
|
+
export function base64_decode_urlsafe(str) {
|
|
5
|
+
return atob(str.replace(/-/g, '+').replace(/_/g, '/'));
|
|
6
|
+
}
|
|
7
|
+
export function base64_encode_buffer(buffer) {
|
|
8
|
+
let uint8array;
|
|
9
|
+
if (buffer instanceof Uint8Array) {
|
|
10
|
+
uint8array = buffer;
|
|
11
|
+
}
|
|
12
|
+
else if (buffer instanceof ArrayBuffer) {
|
|
13
|
+
uint8array = new Uint8Array(buffer);
|
|
14
|
+
}
|
|
15
|
+
else if (typeof buffer == "string") {
|
|
16
|
+
uint8array = new Uint8Array(buffer.split('').map(x => x.charCodeAt(0)));
|
|
17
|
+
}
|
|
18
|
+
return base64_encode_urlsafe(String.fromCharCode(...uint8array));
|
|
19
|
+
}
|
|
20
|
+
export function base64_decode_buffer(str) {
|
|
21
|
+
return new Uint8Array(base64_decode_urlsafe(str).split('').map(c => c.charCodeAt(0)));
|
|
22
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './JwtSignOpts';
|
|
2
|
+
export * from './JWTPayload';
|
|
3
|
+
export * from './JWTKeyPair';
|
|
4
|
+
export * from './JwtHeader';
|
|
5
|
+
export * from './JwtExtractOpts';
|
|
6
|
+
export * from './JwtExtract';
|
|
7
|
+
export * from './JwtError';
|
|
8
|
+
export * from './JWTAlgorithms';
|
|
9
|
+
export * from './JWT';
|
|
10
|
+
export * from './base64_encode_buffer';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './JwtSignOpts';
|
|
2
|
+
export * from './JWTPayload';
|
|
3
|
+
export * from './JWTKeyPair';
|
|
4
|
+
export * from './JwtHeader';
|
|
5
|
+
export * from './JwtExtractOpts';
|
|
6
|
+
export * from './JwtExtract';
|
|
7
|
+
export * from './JwtError';
|
|
8
|
+
export * from './JWTAlgorithms';
|
|
9
|
+
export * from './JWT';
|
|
10
|
+
export * from './base64_encode_buffer';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { JWT } from "../src/JWT";
|
|
2
|
+
import { JWTAlgorithms } from "../src/JWTAlgorithms";
|
|
3
|
+
/**
|
|
4
|
+
* Ensures that verification works for all algorithms
|
|
5
|
+
*/
|
|
6
|
+
async function Test1() {
|
|
7
|
+
for (let key of Object.keys(JWTAlgorithms)) {
|
|
8
|
+
console.log("ALG: ", key);
|
|
9
|
+
const keys = await JWT.generateKeys(key);
|
|
10
|
+
console.log("KEYS: ", keys);
|
|
11
|
+
const signed = await JWT.sign({ iss: "Hello" }, key, keys.sign.base64);
|
|
12
|
+
console.log("SIGNED: ", signed);
|
|
13
|
+
const verify = await JWT.verifySignature(signed, () => { return keys.verify.base64; });
|
|
14
|
+
console.log("VERIFIED: ", verify);
|
|
15
|
+
if (!verify.verified) {
|
|
16
|
+
throw new Error("Not verified!");
|
|
17
|
+
}
|
|
18
|
+
console.log("----");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Ensures that signature manipulation fails for all algorithms
|
|
23
|
+
*/
|
|
24
|
+
async function Test2() {
|
|
25
|
+
for (let key of Object.keys(JWTAlgorithms)) {
|
|
26
|
+
console.log("ALG: ", key);
|
|
27
|
+
const keys = await JWT.generateKeys(key);
|
|
28
|
+
console.log("KEYS: ", keys);
|
|
29
|
+
const signed = await JWT.sign({ iss: "Hello" }, key, keys.sign.base64);
|
|
30
|
+
console.log("SIGNED: ", signed);
|
|
31
|
+
const signed2 = await JWT.sign({ iss: "Hello there" }, key, keys.sign.base64);
|
|
32
|
+
console.log("SIGNED2: ", signed);
|
|
33
|
+
const token = signed.split('.').map(x => (val, index) => {
|
|
34
|
+
if (index == 2)
|
|
35
|
+
return signed2.split('.')[index];
|
|
36
|
+
return val;
|
|
37
|
+
}).join('.');
|
|
38
|
+
const verify = await JWT.verifySignature(token, () => { return keys.verify.base64; });
|
|
39
|
+
console.log("VERIFIED: ", verify);
|
|
40
|
+
if (verify.verified) {
|
|
41
|
+
throw new Error("Somehow verified (shouldn't be)");
|
|
42
|
+
}
|
|
43
|
+
console.log("----");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
Test1();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payello-module/jwt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"author": "Payello <devsupport@payello.com> (https://payello.com/)",
|
|
5
5
|
"displayName": "@payello-module/jwt",
|
|
6
6
|
"description": "JSON Web Token Module",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"prepare": "npm run build"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
|
+
"payellodev": "^0.20240413.1813",
|
|
15
16
|
"typescript": "^5.3.3"
|
|
16
17
|
},
|
|
17
18
|
"files": [
|