mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 10:58:56 +00:00
181 lines
5.2 KiB
JavaScript
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,
|
|
};
|