From 4b17af40a0020860ec5c8520c291c0fbe7da5d51 Mon Sep 17 00:00:00 2001 From: D-Pow Date: Tue, 6 Aug 2024 13:04:27 -0400 Subject: [PATCH] Split NodeJS and browser `encodeJwt()` functions apart Webpack crashes since it can't find `node:crypto` import --- config/utils/Crypto.ts | 35 +++++++++++++++++++++++++++++++++++ config/utils/index.ts | 1 + src/utils/Jwt.ts | 18 +----------------- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/config/utils/Crypto.ts b/config/utils/Crypto.ts index da1526f1..ff9992ce 100644 --- a/config/utils/Crypto.ts +++ b/config/utils/Crypto.ts @@ -1,5 +1,6 @@ import { createHash, + createHmac, type BinaryLike, type BinaryToTextEncoding, } from 'node:crypto'; @@ -57,3 +58,37 @@ export function hash(input: BinaryLike, { return cipher.digest(outputFormat as BinaryToTextEncoding); } + + + +/** + * @see [NodeJS docs]{@link https://nodejs.org/api/crypto.html#cryptocreatehmacalgorithm-key-options} + * @see [NodeJS walkthrough]{@link https://stackoverflow.com/questions/67432096/generating-jwt-tokens/67432483#67432483} + */ +export function encodeJwt(text: string, { + alg = 'HS256', + typ = 'JWT', + secret = '', +} = {}) { + function base64UrlEncode(str: string) { + return btoa(str) + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=+/g, ''); + } + + const encodedHeader = base64UrlEncode(JSON.stringify({ alg, typ })); + const encodedPayload = base64UrlEncode(text); + let algorithm = alg + .replace(/^hs/i, 'sha') + .replace(/^\D+/gi, match => match.toLowerCase()); + + const hmacCipher = createHmac(algorithm, secret); + + hmacCipher.update(`${encodedHeader}.${encodedPayload}`); + + const hmacStr = hmacCipher.digest('base64url'); + const jwt = `${encodedHeader}.${encodedPayload}.${hmacStr}`; + + return jwt; +} diff --git a/config/utils/index.ts b/config/utils/index.ts index ca83c5a4..c9793f74 100644 --- a/config/utils/index.ts +++ b/config/utils/index.ts @@ -8,3 +8,4 @@ export * from './Network'; // TS export * from './Certs'; +export * from './Crypto'; diff --git a/src/utils/Jwt.ts b/src/utils/Jwt.ts index 1efa27c9..543618a2 100644 --- a/src/utils/Jwt.ts +++ b/src/utils/Jwt.ts @@ -94,10 +94,8 @@ export function decodeJwt(jwt: string, { /** * Creates a JWT token. - * Works with either NodeJS or modern browsers. * - * @see [NodeJS walkthrough](https://stackoverflow.com/questions/67432096/generating-jwt-tokens/67432483#67432483) - * @see [Browser walkthrough](https://stackoverflow.com/questions/47329132/how-to-get-hmac-with-crypto-web-api/47332317#47332317) + * @see [Browser walkthrough]{@link https://stackoverflow.com/questions/47329132/how-to-get-hmac-with-crypto-web-api/47332317#47332317} */ export async function encodeJwt(text: string, { alg = 'HS256', @@ -117,20 +115,6 @@ export async function encodeJwt(text: string, { .replace(/^hs/i, 'sha') .replace(/^\D+/gi, match => match.toLowerCase()); - try { - const crypto = await import('node:crypto'); - const hmacCipher = crypto.createHmac(algorithm, secret); - - hmacCipher.update(`${encodedHeader}.${encodedPayload}`); - - const hmacStr = hmacCipher.digest('base64url'); - const jwt = `${encodedHeader}.${encodedPayload}.${hmacStr}`; - - return jwt; - } catch (notNodeJs) { - // Attempt to use web crypto API below - } - algorithm = algorithm.replace(/^sha/i, 'SHA-'); const algoInfo = { name: 'HMAC',