@payello-module/jwt 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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(atob(bits[0]));
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(atob(bits[1]));
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 of requiredProps) {
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,14 +86,14 @@ export class JWT {
88
86
  }
89
87
  let verify = false;
90
88
  // Preparation of the data to verify the signature.
91
- const data = `${extracted.headerRaw}.${extracted.payloadRaw}`;
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':
95
- verify = extracted.signature === btoa(await HmacSha256.encrypt(data, secretKey)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
93
+ verify = await HmacSha256.verify(data, extracted.signature, secretKey);
96
94
  break;
97
95
  case 'HS512':
98
- verify = extracted.signature === btoa(await HmacSha512.encrypt(data, secretKey)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
96
+ verify = await HmacSha512.verify(data, extracted.signature, secretKey);
99
97
  break;
100
98
  default:
101
99
  throw new JwtError(`Unsupported algorithm`);
@@ -1,8 +1,6 @@
1
1
  import { JwtHeader } from "./JwtHeader";
2
2
  export interface JwtExtract {
3
3
  header: JwtHeader;
4
- headerRaw: string;
5
4
  payload: any;
6
- payloadRaw: string;
7
5
  signature: string;
8
6
  }
@@ -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
+ }
@@ -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,4 @@
1
+ export declare class JwtError extends Error {
2
+ type: "@payello/module-jwt#JwtError";
3
+ constructor(message: string, options?: ErrorOptions);
4
+ }
@@ -0,0 +1,6 @@
1
+ export class JwtError extends Error {
2
+ type = "@payello/module-jwt#JwtError";
3
+ constructor(message, options) {
4
+ super(message, options);
5
+ }
6
+ }
@@ -0,0 +1,10 @@
1
+ import { JWTPayload } from "./JWTPayload";
2
+ import { JwtHeader } from "./JwtHeader";
3
+ export interface JwtExtract {
4
+ header: JwtHeader;
5
+ headerRaw: string;
6
+ payload: JWTPayload;
7
+ payloadRaw: string;
8
+ signature: Uint8Array;
9
+ signatureRaw: string;
10
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export interface JwtExtractOpts {
2
+ requiredProps?: string[];
3
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import { JWTAlgorithm } from "./JWTAlgorithms";
2
+ export interface JwtHeader {
3
+ typ: 'JWT';
4
+ alg: JWTAlgorithm;
5
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import { JWTAlgorithm } from "./JWTAlgorithms";
2
+ export interface JwtSignOpts {
3
+ privateKey: string | ArrayBuffer | Uint8Array;
4
+ algorithm: JWTAlgorithm;
5
+ }
@@ -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.1.6",
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": [