Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use modern JavaScript syntax #187

Merged
merged 1 commit into from
Aug 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/base64_url_decode.ts → lib/base64-url-decode.ts
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took the liberty of changing the name of this file so the separators match the other files in the project.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
function b64DecodeUnicode(str: string) {
return decodeURIComponent(
atob(str).replace(/(.)/g, function(m, p) {
atob(str).replace(/(.)/g, (m, p) => {
let code = p.charCodeAt(0).toString(16).toUpperCase();
if (code.length < 2) {
code = "0" + code;
Expand Down
27 changes: 8 additions & 19 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { base64UrlDecode } from "./base64_url_decode";
import { base64UrlDecode } from "./base64-url-decode";

export interface JwtDecodeOptions {
header?: boolean;
Expand All @@ -20,11 +20,7 @@ export interface JwtPayload {
jti?: string;
}

export class InvalidTokenError extends Error {
constructor(message: string) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this constructor only passes the same arguments as the class it extends, I've removed it so that it will fall back to the parent constructor.

super(message);
}
}
export class InvalidTokenError extends Error {}

InvalidTokenError.prototype.name = "InvalidTokenError";

Expand All @@ -38,13 +34,14 @@ export function jwtDecode(token: string, options?: JwtDecodeOptions) {
throw new InvalidTokenError("Invalid token specified: must be a string");
}

options = options || {};
const pos = options.header === true ? 0 : 1;
options ||= {};

const pos = options.header === true ? 0 : 1;
const part = token.split(".")[pos];

if (typeof part !== "string") {
throw new InvalidTokenError(
"Invalid token specified: missing part #" + (pos + 1)
`Invalid token specified: missing part #${(pos + 1)}`
);
}

Expand All @@ -53,23 +50,15 @@ export function jwtDecode(token: string, options?: JwtDecodeOptions) {
decoded = base64UrlDecode(part);
} catch (e: any) {
throw new InvalidTokenError(
"Invalid token specified: invalid base64 for part #" +
(pos + 1) +
" (" +
e.message +
")"
`Invalid token specified: invalid base64 for part #${(pos + 1)} (${e.message})`
);
}

try {
return JSON.parse(decoded);
} catch (e: any) {
throw new InvalidTokenError(
"Invalid token specified: invalid json for part #" +
(pos + 1) +
" (" +
e.message +
")"
`Invalid token specified: invalid json for part #${(pos + 1)} (${e.message})`
);
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/index.umd.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { jwtDecode as default } from './index';
export { jwtDecode as default } from "./index";
108 changes: 54 additions & 54 deletions test/tests.ts
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've also changed the names of the variables here from snake case to camel case, so it matches the rest of the project.

Original file line number Diff line number Diff line change
@@ -1,115 +1,115 @@
import { jwtDecode, InvalidTokenError, JwtPayload } from "./../lib/index";
import { describe, expect, it } from "@jest/globals";

var token =
const token =
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmb28iOiJiYXIiLCJleHAiOjEzOTMyODY4OTMsImlhdCI6MTM5MzI2ODg5M30.4-iaDojEVl0pJQMjrbM1EzUIfAZgsbK_kgnVyVxFSVo";

describe("jwt-decode", function () {
it("should return default and custom claims", function () {
var decoded = jwtDecode<JwtPayload & { foo: string }>(token);
describe("jwt-decode", () => {
it("should return default and custom claims", () => {
const decoded = jwtDecode<JwtPayload & { foo: string }>(token);
expect(decoded.exp).toEqual(1393286893);
expect(decoded.iat).toEqual(1393268893);
expect(decoded.foo).toEqual("bar");
});

it("should return header information", function () {
var decoded = jwtDecode(token, { header: true });
it("should return header information", () => {
const decoded = jwtDecode(token, { header: true });
expect(decoded.typ).toEqual("JWT");
expect(decoded.alg).toEqual("HS256");
});

it("should work with utf8 tokens", function () {
var utf8_token =
it("should work with utf8 tokens", () => {
const utf8Token =
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiSm9zw6kiLCJpYXQiOjE0MjU2NDQ5NjZ9.1CfFtdGUPs6q8kT3OGQSVlhEMdbuX0HfNSqum0023a0";
var decoded = jwtDecode<JwtPayload & { name: string }>(utf8_token);
const decoded = jwtDecode<JwtPayload & { name: string }>(utf8Token);
expect(decoded.name).toEqual("José");
});

it("should work with binary tokens", function () {
var binary_token =
it("should work with binary tokens", () => {
const binaryToken =
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiSm9z6SIsImlhdCI6MTQyNTY0NDk2Nn0.cpnplCBxiw7Xqz5thkqs4Mo_dymvztnI0CI4BN0d1t8";
var decoded = jwtDecode<JwtPayload & { name: string }>(binary_token);
const decoded = jwtDecode<JwtPayload & { name: string }>(binaryToken);
expect(decoded.name).toEqual("José");
});

it("should work with double padding", function () {
var utf8_token =
it("should work with double padding", () => {
const utf8Token =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ikpvc8OpIiwiaWF0IjoxNTE2MjM5MDIyfQ.7A3F5SUH2gbBSYVon5mas_Y-KCrWojorKQg7UKGVEIA";
var decoded = jwtDecode<JwtPayload & { name: string }>(utf8_token);
const decoded = jwtDecode<JwtPayload & { name: string }>(utf8Token);
expect(decoded.name).toEqual("José");
});

it("should work with single padding", function () {
var utf8_token =
it("should work with single padding", () => {
const utf8Token =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ikpvc8OpZSIsImlhdCI6MTUxNjIzOTAyMn0.tbjJzDAylkKSV0_YGR5xBJBlFK01C82nZPLIcA3JX1g";
var decoded = jwtDecode<JwtPayload & { name: string }>(utf8_token);
const decoded = jwtDecode<JwtPayload & { name: string }>(utf8Token);
expect(decoded.name).toEqual("Josée");
});

it("should throw InvalidTokenError on nonstring", function () {
var bad_token = null;
expect(function () {
jwtDecode(bad_token as any);
it("should throw InvalidTokenError on nonstring", () => {
const badToken = null;
expect(() => {
jwtDecode(badToken as any);
}).toThrow(InvalidTokenError);
});

it("should throw InvalidTokenError on string that is not a token", function () {
var bad_token = "fubar";
expect(function () {
jwtDecode(bad_token);
it("should throw InvalidTokenError on string that is not a token", () => {
const badToken = "fubar";
expect(() => {
jwtDecode(badToken);
}).toThrow(InvalidTokenError);
});

it("should throw InvalidTokenErrors when token is null", function () {
var bad_token = null;
expect(function () {
jwtDecode(bad_token as any, { header: true });
it("should throw InvalidTokenErrors when token is null", () => {
const badToken = null;
expect(() => {
jwtDecode(badToken as any, { header: true });
}).toThrow(
new InvalidTokenError("Invalid token specified: must be a string")
);
});

it("should throw InvalidTokenErrors when missing part #1", function () {
var bad_token = ".FAKE_TOKEN";
expect(function () {
jwtDecode(bad_token, { header: true });
it("should throw InvalidTokenErrors when missing part #1", () => {
const badToken = ".FAKE_TOKEN";
expect(() => {
jwtDecode(badToken, { header: true });
}).toThrow(/Invalid token specified: invalid json for part #1/);
});

it("should throw InvalidTokenErrors when part #1 is not valid base64", function () {
var bad_token = "TOKEN";
expect(function () {
jwtDecode(bad_token, { header: true });
it("should throw InvalidTokenErrors when part #1 is not valid base64", () => {
const badToken = "TOKEN";
expect(() => {
jwtDecode(badToken, { header: true });
}).toThrow(/Invalid token specified: invalid base64 for part #1/);
});

it("should throw InvalidTokenErrors when part #1 is not valid JSON", function () {
var bad_token = "FAKE.TOKEN";
expect(function () {
jwtDecode(bad_token, { header: true });
it("should throw InvalidTokenErrors when part #1 is not valid JSON", () => {
const badToken = "FAKE.TOKEN";
expect(() => {
jwtDecode(badToken, { header: true });
}).toThrow(/Invalid token specified: invalid json for part #1/);
});

it("should throw InvalidTokenErrors when missing part #2", function () {
var bad_token = "FAKE_TOKEN";
expect(function () {
jwtDecode(bad_token);
it("should throw InvalidTokenErrors when missing part #2", () => {
const badToken = "FAKE_TOKEN";
expect(() => {
jwtDecode(badToken);
}).toThrow(
new InvalidTokenError("Invalid token specified: missing part #2")
);
});

it("should throw InvalidTokenErrors when part #2 is not valid base64", function () {
var bad_token = "FAKE.TOKEN";
expect(function () {
jwtDecode(bad_token);
it("should throw InvalidTokenErrors when part #2 is not valid base64", () => {
const badToken = "FAKE.TOKEN";
expect(() => {
jwtDecode(badToken);
}).toThrow(/Invalid token specified: invalid base64 for part #2/);
});

it("should throw InvalidTokenErrors when part #2 is not valid JSON", function () {
var bad_token = "FAKE.TOKEN2";
expect(function () {
jwtDecode(bad_token);
it("should throw InvalidTokenErrors when part #2 is not valid JSON", () => {
const badToken = "FAKE.TOKEN2";
expect(() => {
jwtDecode(badToken);
}).toThrow(/Invalid token specified: invalid json for part #2/);
});
});