Skip to content

Commit

Permalink
Avoid using default exports (#175)
Browse files Browse the repository at this point in the history
Co-authored-by: Jon Koops <jonkoops@gmail.com>
Co-authored-by: Ewan Harris <ewan.harris@okta.com>
  • Loading branch information
3 people committed Oct 27, 2023
1 parent 946918d commit 820f4d9
Show file tree
Hide file tree
Showing 9 changed files with 59 additions and 55 deletions.
37 changes: 26 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ Run `npm install jwt-decode` or `yarn add jwt-decode` to install the library.
### Usage

```js
import jwt_decode from "jwt-decode";
import { jwtDecode } from "jwt-decode";

var token = "eyJ0eXAiO.../// jwt token";
var decoded = jwt_decode(token);
const token = "eyJ0eXAiO.../// jwt token";
const decoded = jwtDecode(token);

console.log(decoded);

Expand All @@ -40,7 +40,7 @@ console.log(decoded);
*/

// decode header by passing in options (useful for when you need `kid` to verify a JWT):
var decodedHeader = jwt_decode(token, { header: true });
const decodedHeader = jwtDecode(token, { header: true });
console.log(decodedHeader);

/* prints:
Expand Down Expand Up @@ -69,35 +69,50 @@ Not adhering to the format will result in a `InvalidTokenError` with one of the
- `Invalid token specified: invalid base64 for part #` => the part could not be base64 decoded (the message should contain the error the base64 decoder gave)
- `Invalid token specified: invalid json for part #` => the part was correctly base64 decoded, however, the decoded value was not valid JSON (the message should contain the error the JSON parser gave)

#### Use with typescript
#### Use with TypeScript

The return type of the `jwt_decode` function is determined by the `header` property of the object passed as the second argument. If omitted (or set to false), it'll use `JwtPayload`, when true it will use `JwtHeader`.
If needed, you can specify what the expected return type should be by passing a type argument to the `jwt_decode` function.
The return type of the `jwtDecode` function is determined by the `header` property of the object passed as the second argument. If omitted (or set to false), it'll use `JwtPayload`, when true it will use `JwtHeader`.
If needed, you can specify what the expected return type should be by passing a type argument to the `jwtDecode` function.

You can extend both `JwtHeader` and `JwtPayload` to include non-standard claims or properties.

```typescript
import jwtDecode from "jwt-decode";
import { jwtDecode } from "jwt-decode";

const token: string = "eyJhsw5c";
const token = "eyJhsw5c";
const decoded = jwtDecode<JwtPayload>(token); // Returns with the JwtPayload type
```

#### Use as a CommonJS package

```javascript
const jwt_decode = require('jwt-decode');
const { jwtDecode } = require('jwt-decode');
...
```

#### Include with a script tag

Copy the file `jwt-decode.js` from the `build/` folder to your project somewhere, then include it like so:
Copy the file `jwt-decode.js` from the root of the `build/` folder to your project somewhere, then include it like so:

```html
<script src="jwt-decode.js"></script>
```

Once this script has loaded, the `jwt_decode` function will be exposed as a global:

```javascript
const token = "eyJhsw5c";
const decoded = jwt_decode(token);
```

Alternatively, if you are using the [Asynchronous Module Definition (AMD) API](https://github.com/amdjs/amdjs-api/blob/master/AMD.md), you can load the same function as follows:

```javascript
define(["jwt_decode"], (jwtDecode) => {
const token = "eyJhsw5c";
const decoded = jwtDecode(token);
});
```

## Feedback

Expand Down
2 changes: 1 addition & 1 deletion lib/base64_url_decode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function b64DecodeUnicode(str: string) {
);
}

export default function(str: string) {
export function base64UrlDecode(str: string) {
let output = str.replace(/-/g, "+").replace(/_/g, "/");
switch (output.length % 4) {
case 0:
Expand Down
6 changes: 0 additions & 6 deletions lib/index.cjs.ts

This file was deleted.

3 changes: 0 additions & 3 deletions lib/index.standalone.ts

This file was deleted.

11 changes: 5 additions & 6 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import base64_url_decode from "./base64_url_decode";
import { base64UrlDecode } from "./base64_url_decode";

export interface JwtDecodeOptions {
header?: boolean;
Expand Down Expand Up @@ -28,12 +28,12 @@ export class InvalidTokenError extends Error {

InvalidTokenError.prototype.name = "InvalidTokenError";

function jwtDecode<T = JwtHeader>(
export function jwtDecode<T = JwtHeader>(
token: string,
options: JwtDecodeOptions & { header: true }
): T;
function jwtDecode<T = JwtPayload>(token: string, options?: JwtDecodeOptions): T;
function jwtDecode(token: string, options?: JwtDecodeOptions) {
export function jwtDecode<T = JwtPayload>(token: string, options?: JwtDecodeOptions): T;
export function jwtDecode(token: string, options?: JwtDecodeOptions) {
if (typeof token !== "string") {
throw new InvalidTokenError("Invalid token specified: must be a string");
}
Expand All @@ -50,7 +50,7 @@ function jwtDecode(token: string, options?: JwtDecodeOptions) {

let decoded: string;
try {
decoded = base64_url_decode(part);
decoded = base64UrlDecode(part);
} catch (e: any) {
throw new InvalidTokenError(
"Invalid token specified: invalid base64 for part #" +
Expand All @@ -74,4 +74,3 @@ function jwtDecode(token: string, options?: JwtDecodeOptions) {
}
}

export default jwtDecode;
1 change: 1 addition & 0 deletions lib/index.umd.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { jwtDecode as default } from './index';
20 changes: 9 additions & 11 deletions rollup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { defineConfig } from "rollup";
import livereload from "rollup-plugin-livereload";
import serve from "rollup-plugin-serve";

const EXPORT_NAME = "jwt-decode";
const isProduction = process.env.NODE_ENV === "production";
const tsPlugin = typescript({
rootDir: "lib",
Expand All @@ -16,8 +15,10 @@ const plugins = [
isProduction && terser(),
];

const input = "lib/index.ts";

export default defineConfig([{
input: "lib/index.standalone.ts",
input: "lib/index.umd.ts",
output: {
name: "jwt_decode",
file: "build/jwt-decode.js",
Expand All @@ -29,24 +30,21 @@ export default defineConfig([{
]
},
{
input: "lib/index.cjs.ts",
output: [{
name: EXPORT_NAME,
input,
output: {
file: "build/cjs/jwt-decode.js",
format: "cjs",
exports: "auto",
sourcemap: true,
}, ],
},
plugins,
},
{
input: "lib/index.ts",
output: [{
name: EXPORT_NAME,
input,
output: {
file: "build/esm/jwt-decode.js",
format: "esm",
sourcemap: true,
}, ],
},
plugins: [!isProduction &&
serve({
contentBase: ["build", "static"],
Expand Down
2 changes: 1 addition & 1 deletion static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ <h2>decoded:</h2>
<pre><code class="js-error3"></code></pre>

<script type="module">
import jwtDecode from "/jwt-decode.esm.js";
import { jwtDecode } from "/esm/jwt-decode.js";
var token =
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmb28iOiJiYXIiLCJleHAiOjEzOTMyODY4OTMsImlhdCI6MTM5MzI2ODg5M30.4-iaDojEVl0pJQMjrbM1EzUIfAZgsbK_kgnVyVxFSVo";
var decoded = jwtDecode(token);
Expand Down
32 changes: 16 additions & 16 deletions test/tests.ts
Original file line number Diff line number Diff line change
@@ -1,69 +1,69 @@
import jwt_decode, { InvalidTokenError, JwtPayload } from "./../lib/index";
import { jwtDecode, InvalidTokenError, JwtPayload } from "./../lib/index";
import { describe, expect, it } from "@jest/globals";

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

describe("jwt-decode", function () {
it("should return default and custom claims", function () {
var decoded = jwt_decode<JwtPayload & { foo: string }>(token);
var 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 = jwt_decode(token, { header: true });
var 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 =
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiSm9zw6kiLCJpYXQiOjE0MjU2NDQ5NjZ9.1CfFtdGUPs6q8kT3OGQSVlhEMdbuX0HfNSqum0023a0";
var decoded = jwt_decode<JwtPayload & { name: string }>(utf8_token);
var decoded = jwtDecode<JwtPayload & { name: string }>(utf8_token);
expect(decoded.name).toEqual("José");
});

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

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

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

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

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

it("should throw InvalidTokenErrors when token is null", function () {
var bad_token = null;
expect(function () {
jwt_decode(bad_token as any, { header: true });
jwtDecode(bad_token as any, { header: true });
}).toThrow(
new InvalidTokenError("Invalid token specified: must be a string")
);
Expand All @@ -72,28 +72,28 @@ describe("jwt-decode", function () {
it("should throw InvalidTokenErrors when missing part #1", function () {
var bad_token = ".FAKE_TOKEN";
expect(function () {
jwt_decode(bad_token, { header: true });
jwtDecode(bad_token, { 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 () {
jwt_decode(bad_token, { header: true });
jwtDecode(bad_token, { 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 () {
jwt_decode(bad_token, { header: true });
jwtDecode(bad_token, { 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 () {
jwt_decode(bad_token);
jwtDecode(bad_token);
}).toThrow(
new InvalidTokenError("Invalid token specified: missing part #2")
);
Expand All @@ -102,14 +102,14 @@ describe("jwt-decode", function () {
it("should throw InvalidTokenErrors when part #2 is not valid base64", function () {
var bad_token = "FAKE.TOKEN";
expect(function () {
jwt_decode(bad_token);
jwtDecode(bad_token);
}).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 () {
jwt_decode(bad_token);
jwtDecode(bad_token);
}).toThrow(/Invalid token specified: invalid json for part #2/);
});
});

0 comments on commit 820f4d9

Please sign in to comment.