aboutsummaryrefslogtreecommitdiff
path: root/test/js/third_party/jsonwebtoken/verify.test.js
diff options
context:
space:
mode:
Diffstat (limited to 'test/js/third_party/jsonwebtoken/verify.test.js')
-rw-r--r--test/js/third_party/jsonwebtoken/verify.test.js318
1 files changed, 318 insertions, 0 deletions
diff --git a/test/js/third_party/jsonwebtoken/verify.test.js b/test/js/third_party/jsonwebtoken/verify.test.js
new file mode 100644
index 000000000..c7583892f
--- /dev/null
+++ b/test/js/third_party/jsonwebtoken/verify.test.js
@@ -0,0 +1,318 @@
+import jwt from "jsonwebtoken";
+import { expect, describe, it, afterEach } from "bun:test";
+import jws from "jws";
+import sinon from "sinon";
+import fs from "fs";
+import path from "path";
+
+describe("verify", function () {
+ const pub = fs.readFileSync(path.join(__dirname, "pub.pem"));
+ const priv = fs.readFileSync(path.join(__dirname, "priv.pem"));
+
+ it("should first assume JSON claim set", function (done) {
+ const header = { alg: "RS256" };
+ const payload = { iat: Math.floor(Date.now() / 1000) };
+
+ const signed = jws.sign({
+ header: header,
+ payload: payload,
+ secret: priv,
+ encoding: "utf8",
+ });
+
+ jwt.verify(signed, pub, { typ: "JWT" }, function (err, p) {
+ if (err) return done(err);
+ expect(err).toBeNull();
+ expect(p).toEqual(payload);
+ done();
+ });
+ });
+
+ it("should not be able to verify unsigned token", function () {
+ const header = { alg: "none" };
+ const payload = { iat: Math.floor(Date.now() / 1000) };
+
+ const signed = jws.sign({
+ header: header,
+ payload: payload,
+ secret: "secret",
+ encoding: "utf8",
+ });
+
+ expect(function () {
+ jwt.verify(signed, "secret", { typ: "JWT" });
+ }).toThrow(/jwt signature is required/);
+ });
+
+ it("should not be able to verify unsigned token", function () {
+ const header = { alg: "none" };
+ const payload = { iat: Math.floor(Date.now() / 1000) };
+
+ const signed = jws.sign({
+ header: header,
+ payload: payload,
+ secret: "secret",
+ encoding: "utf8",
+ });
+
+ expect(function () {
+ jwt.verify(signed, undefined, { typ: "JWT" });
+ }).toThrow(/please specify "none" in "algorithms" to verify unsigned tokens/);
+ });
+
+ it("should be able to verify unsigned token when none is specified", function (done) {
+ const header = { alg: "none" };
+ const payload = { iat: Math.floor(Date.now() / 1000) };
+
+ const signed = jws.sign({
+ header: header,
+ payload: payload,
+ secret: "secret",
+ encoding: "utf8",
+ });
+
+ jwt.verify(signed, null, { typ: "JWT", algorithms: ["none"] }, function (err, p) {
+ if (err) return done(err);
+ expect(err).toBeNull();
+ expect(p).toEqual(payload);
+ done();
+ });
+ });
+
+ it("should not mutate options", function (done) {
+ const header = { alg: "HS256" };
+ const payload = { iat: Math.floor(Date.now() / 1000) };
+ const options = { typ: "JWT" };
+ const signed = jws.sign({
+ header: header,
+ payload: payload,
+ secret: "secret",
+ encoding: "utf8",
+ });
+
+ jwt.verify(signed, "secret", options, function (err) {
+ if (err) return done(err);
+ expect(err).toBeNull();
+ expect(Object.keys(options).length).toEqual(1);
+ done();
+ });
+ });
+
+ describe("secret or token as callback", function () {
+ const token =
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE0MzcwMTg1ODIsImV4cCI6MTQzNzAxODU5Mn0.3aR3vocmgRpG05rsI9MpR6z2T_BGtMQaPq2YR6QaroU";
+ const key = "key";
+
+ const payload = { foo: "bar", iat: 1437018582, exp: 1437018592 };
+ const options = { algorithms: ["HS256"], ignoreExpiration: true };
+
+ it("without callback", function (done) {
+ jwt.verify(token, key, options, function (err, p) {
+ if (err) return done(err);
+ expect(err).toBeNull();
+ expect(p).toEqual(payload);
+ done();
+ });
+ });
+
+ it("simple callback", function (done) {
+ const keyFunc = function (header, callback) {
+ expect(header).toEqual({ alg: "HS256", typ: "JWT" });
+
+ callback(undefined, key);
+ };
+
+ jwt.verify(token, keyFunc, options, function (err, p) {
+ if (err) return done(err);
+ expect(err).toBeNull();
+ expect(p).toEqual(payload);
+ done();
+ });
+ });
+
+ it("should error if called synchronously", function (done) {
+ const keyFunc = function (header, callback) {
+ callback(undefined, key);
+ };
+
+ expect(function () {
+ jwt.verify(token, keyFunc, options);
+ }).toThrow(/verify must be called asynchronous if secret or public key is provided as a callback/);
+
+ done();
+ });
+
+ it("simple error", function (done) {
+ const keyFunc = function (header, callback) {
+ callback(new Error("key not found"));
+ };
+
+ jwt.verify(token, keyFunc, options, function (err, p) {
+ expect(err).toBeDefined();
+ expect(err.name).toBe("JsonWebTokenError");
+ expect(err.message).toMatch(/error in secret or public key callback/);
+ expect(p).toBeUndefined();
+ done();
+ });
+ });
+
+ it("delayed callback", function (done) {
+ const keyFunc = function (header, callback) {
+ setTimeout(function () {
+ callback(undefined, key);
+ }, 25);
+ };
+
+ jwt.verify(token, keyFunc, options, function (err, p) {
+ if (err) return done(err);
+ expect(err).toBeNull();
+ expect(p).toEqual(payload);
+ done();
+ });
+ });
+
+ it("delayed error", function (done) {
+ const keyFunc = function (header, callback) {
+ setTimeout(function () {
+ callback(new Error("key not found"));
+ }, 25);
+ };
+
+ jwt.verify(token, keyFunc, options, function (err, p) {
+ expect(err).toBeDefined();
+ expect(err.name).toBe("JsonWebTokenError");
+ expect(err.message).toMatch(/error in secret or public key callback/);
+ expect(p).toBeUndefined();
+ done();
+ });
+ });
+ });
+
+ describe("expiration", function () {
+ // { foo: 'bar', iat: 1437018582, exp: 1437018592 }
+ const token =
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE0MzcwMTg1ODIsImV4cCI6MTQzNzAxODU5Mn0.3aR3vocmgRpG05rsI9MpR6z2T_BGtMQaPq2YR6QaroU";
+ const key = "key";
+
+ let clock;
+ afterEach(function () {
+ try {
+ clock.restore();
+ } catch (e) {}
+ });
+
+ it("should error on expired token", function (done) {
+ clock = sinon.useFakeTimers(1437018650000); // iat + 58s, exp + 48s
+ const options = { algorithms: ["HS256"] };
+
+ jwt.verify(token, key, options, function (err, p) {
+ expect(err).toBeDefined();
+ expect(err.name).toBe("TokenExpiredError");
+ expect(err.message).toBe("jwt expired");
+ expect(err?.expiredAt?.constructor?.name).toBe("Date");
+ expect(Number(err.expiredAt)).toBe(1437018592000);
+ expect(p).toBeUndefined();
+ done();
+ });
+ });
+
+ it("should not error on expired token within clockTolerance interval", function (done) {
+ clock = sinon.useFakeTimers(1437018594000); // iat + 12s, exp + 2s
+ const options = { algorithms: ["HS256"], clockTolerance: 5 };
+
+ jwt.verify(token, key, options, function (err, p) {
+ expect(err).toBeNull();
+ expect(p.foo).toBe("bar");
+ done();
+ });
+ });
+
+ describe("option: clockTimestamp", function () {
+ const clockTimestamp = 1000000000;
+ it("should verify unexpired token relative to user-provided clockTimestamp", function (done) {
+ const token = jwt.sign({ foo: "bar", iat: clockTimestamp, exp: clockTimestamp + 1 }, key);
+ jwt.verify(token, key, { clockTimestamp: clockTimestamp }, function (err) {
+ expect(err).toBeNull();
+ done();
+ });
+ });
+ it("should error on expired token relative to user-provided clockTimestamp", function (done) {
+ const token = jwt.sign({ foo: "bar", iat: clockTimestamp, exp: clockTimestamp + 1 }, key);
+ jwt.verify(token, key, { clockTimestamp: clockTimestamp + 1 }, function (err, p) {
+ expect(err).toBeDefined();
+ expect(err.name).toBe("TokenExpiredError");
+ expect(err.message).toBe("jwt expired");
+ expect(err?.expiredAt?.constructor?.name).toBe("Date");
+ expect(Number(err.expiredAt)).toBe((clockTimestamp + 1) * 1000);
+ expect(p).toBeUndefined();
+ done();
+ });
+ });
+ it("should verify clockTimestamp is a number", function (done) {
+ const token = jwt.sign({ foo: "bar", iat: clockTimestamp, exp: clockTimestamp + 1 }, key);
+ jwt.verify(token, key, { clockTimestamp: "notANumber" }, function (err, p) {
+ expect(err).toBeDefined();
+ expect(err.name).toBe("JsonWebTokenError");
+ expect(err.message).toBe("clockTimestamp must be a number");
+ expect(p).toBeUndefined();
+ done();
+ });
+ });
+ });
+
+ describe("option: maxAge and clockTimestamp", function () {
+ // { foo: 'bar', iat: 1437018582, exp: 1437018800 } exp = iat + 218s
+ const token =
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE0MzcwMTg1ODIsImV4cCI6MTQzNzAxODgwMH0.AVOsNC7TiT-XVSpCpkwB1240izzCIJ33Lp07gjnXVpA";
+ it("cannot be more permissive than expiration", function (done) {
+ const clockTimestamp = 1437018900; // iat + 318s (exp: iat + 218s)
+ const options = { algorithms: ["HS256"], clockTimestamp: clockTimestamp, maxAge: "1000y" };
+
+ jwt.verify(token, key, options, function (err, p) {
+ // maxAge not exceded, but still expired
+ expect(err).toBeDefined();
+ expect(err.name).toBe("TokenExpiredError");
+ expect(err.message).toBe("jwt expired");
+ expect(err?.expiredAt?.constructor?.name).toBe("Date");
+ expect(Number(err.expiredAt)).toBe(1437018800000);
+ expect(p).toBeUndefined();
+ done();
+ });
+ });
+ });
+ });
+
+ describe.todo("when verifying a token with an unsupported public key type", function () {
+ it("should throw an error", function () {
+ const token =
+ "eyJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE2Njk5OTAwMDN9.YdjFWJtPg_9nccMnTfQyesWQ0UX-GsWrfCGit_HqjeIkNjoV6dkAJ8AtbnVEhA4oxwqSXx6ilMOfHEjmMlPtyyyVKkWKQHcIWYnqPbNSEv8a7Men8KhJTIWb4sf5YbhgSCpNvU_VIZjLO1Z0PzzgmEikp0vYbxZFAbCAlZCvUlcIc-kdjIRCnDJe0BBrYRxNLEJtYsf7D1yFIFIqw8-VP87yZdExA4eHsTaE84SgnL24ZK5h5UooDx-IRNd_rrMyio8kNy63grVxCWOtkXZ26iZk6v-HMsnBqxvUwR6-8wfaWrcpADkyUO1q3SNsoTdwtflbvfwgjo3uve0IvIzHMw";
+ const key = fs.readFileSync(path.join(__dirname, "dsa-public.pem"));
+
+ expect(function () {
+ jwt.verify(token, key);
+ }).toThrow('Unknown key type "dsa".');
+ });
+ });
+
+ describe("when verifying a token with an incorrect public key type", function () {
+ it("should throw a validation error if key validation is enabled", function () {
+ const token =
+ "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXkiOiJsb2FkIiwiaWF0IjoxNjcwMjMwNDE2fQ.7TYP8SB_9Tw1fNIfuG60b4tvoLPpDAVBQpV1oepnuKwjUz8GOw4fRLzclo0Q2YAXisJ3zIYMEFsHpYrflfoZJQ";
+ const key = fs.readFileSync(path.join(__dirname, "rsa-public.pem"));
+
+ expect(function () {
+ jwt.verify(token, key, { algorithms: ["ES256"] });
+ }).toThrow('"alg" parameter for "rsa" key type must be one of: RS256, PS256, RS384, PS384, RS512, PS512.');
+ });
+
+ it("should throw an unknown error if key validation is disabled", function () {
+ const token =
+ "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXkiOiJsb2FkIiwiaWF0IjoxNjcwMjMwNDE2fQ.7TYP8SB_9Tw1fNIfuG60b4tvoLPpDAVBQpV1oepnuKwjUz8GOw4fRLzclo0Q2YAXisJ3zIYMEFsHpYrflfoZJQ";
+ const key = fs.readFileSync(path.join(__dirname, "rsa-public.pem"));
+
+ expect(function () {
+ jwt.verify(token, key, { algorithms: ["ES256"], allowInvalidAsymmetricKeyTypes: true });
+ }).not.toThrow('"alg" parameter for "rsa" key type must be one of: RS256, PS256, RS384, PS384, RS512, PS512.');
+ });
+ });
+});