Files
bun.sh/test/js/third_party/jsonwebtoken/test-utils.js
2024-04-25 17:57:22 -07:00

181 lines
5.2 KiB
JavaScript

"use strict";
import jwt from "jsonwebtoken";
function expect(value) {
return {
toEqual: expected => {
if (typeof value === "object") {
if (typeof expected === "object") {
for (const propertyName in expected) {
expect(value[propertyName]).toEqual(expected[propertyName]);
}
return;
}
throw new Error(`Expected ${value} to strictly equal ${expected}`);
}
if (value !== expected) {
throw new Error(`Expected ${value} to equal ${expected}`);
}
},
toStrictEqual: expected => {
if (typeof value === "object") {
if (typeof expected === "object") {
for (const propertyName in expected) {
expect(value[propertyName]).toStrictEqual(expected[propertyName]);
}
return;
}
throw new Error(`Expected ${value} to strictly equal ${expected}`);
}
if (value !== expected) {
throw new Error(`Expected ${value} to strictly equal ${expected}`);
}
},
};
}
function expectError(actual, expected) {
if (!actual && !expected) {
return;
}
if (actual && !expected) {
throw new Error(`Expected no error, but got ${actual}`);
}
if (!actual && expected) {
throw new Error(`Expected error ${expected}, but got no error`);
}
if (actual.message !== expected.message) {
throw new Error(`Expected ${actual.message} to equal ${expected.message}`);
}
if (actual.name !== expected.name) {
throw new Error(`Expected ${actual.name} to equal ${expected.name}`);
}
if (actual.code !== expected.code) {
throw new Error(`Expected ${actual.code} to equal ${expected.code}`);
}
}
/**
* Correctly report errors that occur in an asynchronous callback
* @param {function(err): void} done The mocha callback
* @param {function(): void} testFunction The assertions function
*/
function asyncCheck(done, testFunction) {
try {
testFunction();
done();
} catch (err) {
done(err);
}
}
/**
* Base64-url encode a string
* @param str {string} The string to encode
* @returns {string} The encoded string
*/
function base64UrlEncode(str) {
return Buffer.from(str).toString("base64").replace(/[=]/g, "").replace(/\+/g, "-").replace(/\//g, "_");
}
/**
* Verify a JWT, ensuring that the asynchronous and synchronous calls to `verify` have the same result
* @param {string} jwtString The JWT as a string
* @param {string} secretOrPrivateKey The shared secret or private key
* @param {object} options Verify options
* @param {function(err, token):void} callback
*/
function verifyJWTHelper(jwtString, secretOrPrivateKey, options, callback) {
jwt.verify(jwtString, secretOrPrivateKey, options, (err, asyncVerifiedToken) => {
let error;
let syncVerified;
try {
syncVerified = jwt.verify(jwtString, secretOrPrivateKey, options);
} catch (err) {
error = err;
}
try {
expectError(error, err);
if (error) {
callback(err);
} else {
expect(syncVerified).toStrictEqual(asyncVerifiedToken);
callback(null, syncVerified);
}
} catch (err) {
callback(err);
}
});
}
/**
* Sign a payload to create a JWT, ensuring that the asynchronous and synchronous calls to `sign` have the same result
* @param {object} payload The JWT payload
* @param {string} secretOrPrivateKey The shared secret or private key
* @param {object} options Sign options
* @param {function(err, token):void} callback
*/
function signJWTHelper(payload, secretOrPrivateKey, options, callback) {
// make sure they are created with the same timestamp
// https://github.com/auth0/node-jsonwebtoken/blob/bc28861f1fa981ed9c009e29c044a19760a0b128/sign.js#L185
const timestamp = Math.floor(Date.now() / 1000);
if (typeof payload === "object" && !Buffer.isBuffer(payload) && !payload.iat) {
payload = { ...payload, iat: timestamp };
}
jwt.sign(payload, secretOrPrivateKey, options, (err, asyncSigned) => {
let error;
let syncSigned;
try {
syncSigned = jwt.sign(payload, secretOrPrivateKey, options);
} catch (err) {
error = err;
}
try {
expectError(error, err);
if (error) {
callback(err);
} else {
expect(syncSigned).toEqual(asyncSigned);
callback(null, syncSigned);
}
} catch (err) {
callback(err);
}
});
}
// Same as above but won't automatically set the iat field. When we implement fake timers,
// we can delete this function and use the one above with a fake timer.
function signJWTHelperWithoutAddingTimestamp(payload, secretOrPrivateKey, options, callback) {
jwt.sign(payload, secretOrPrivateKey, options, (err, asyncSigned) => {
let error;
let syncSigned;
try {
syncSigned = jwt.sign(payload, secretOrPrivateKey, options);
} catch (err) {
error = err;
}
try {
expectError(error, err);
if (error) {
callback(err);
} else {
expect(syncSigned).toEqual(asyncSigned);
callback(null, syncSigned);
}
} catch (err) {
callback(err);
}
});
}
export { asyncCheck, base64UrlEncode, signJWTHelper, verifyJWTHelper };
export default {
asyncCheck,
base64UrlEncode,
signJWTHelper,
signJWTHelperWithoutAddingTimestamp,
verifyJWTHelper,
};