npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

jwt-km

v1.0.5

Published

A simple and intuitive JSON Web Token (JWT) package.

Downloads

11

Readme

jwt-km

jwt-km is a simple and intuitive JSON Web Token (JWT) package for NodeJS.

To install jwt-km, run the following command:

npm install jwt-km

Features

  • Single file implementation
    • The entire package is implemented inside a single 200 line file, jwt.ts.
  • Zero external dependencies
    • jwt-km does not attempt to hide complexity inside external dependencies.
    • The only dependency is Node's crypto module.
  • Zero bloat, zero outdated code, zero useless code
    • jwt-km strips away all the useless fluff present in every other package
    • jwt-km implements JWTs in a simple and modern fashion
    • jwt-km is very, very, very fast
  • Extremely easy to use
    • Simple and modern implementation
      • Everything is encapsulated in the JWT class.
      • There are only 7 methods to remember
    • Zero error handling
      • Once the secret key is correctly setup, all the error handling is built into the methods.
    • Fully synchronous
      • No working with Promises or callbacks.
    • ESM support (import, export, etc.)
      • Stop using outdated packages written for CommonJS
      • Stop using garbage packages with documentation that still uses var
    • Full TypeScript support
  • Detailed documentation with examples

Usage

The jwt-km package is structured around the JWT class, which represents a single JSON Web Token.

Along with some other exported objects, a helper function unixTime() is provided, which calculates the current unix timestamp using Date.now().

import JWT from "jwt-km";
import { unixTime } from "jwt-km";

Note: In the section below, liao.gg refers to the issuer's name or domain, which in this case is me. When using the package in your own project, please use your own domain name or some other identifier.


To create a token, create a new instance of the JWT class: It is strongly recommended that you provide the issuer and expiration timestamp parameter in the constructor.

// Supply the issuer (iss) and token expiration timestamp (exp)
const jwt = new JWT("liao.gg", unixTime() + 60 * 60); // Expires in 1 hour, or 60 * 60 seconds

// Optionally, you can create an empty token by providing no arguments. (Not recommended)
const emptyJwt = new JWT();

To add claims, use the .addClaim() method:

emptyJwt.addClaim("iss", "liao.gg");
emptyJwt.addClaim("exp", unixTime() + 60 * 60);

// You can also chain together .addClaim() calls
const alsoJwt = new JWT("liao.gg", unixTime() + 60 * 60)
    .addClaim("username", "kevin")
    .addClaim("permissions", "admin");

To get the value of a claim, use the getClaim() method:

assert(alsoJwt.getClaim("username") === "kevin");
assert(alsoJwt.getClaim("nonexistant") === undefined);

Finally, to generate the token in string form, use the getToken() method along with a secret key:

// In this example, we will hardcode the secret
const secret = "DD4E8D770CF2CCDBEC698D005D049DB432805D223F8441B709746EB33A8F8711";

const jwt = new JWT("liao.gg", unixTime() + 60 * 60);
jwt.addClaim("username", "kevin");
const token = jwt.getToken(secret);
// token === eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
//          .eyJpc3MiOiJsaWFvLmdnIiwiZXhwIjoxNzA0NzY0Mzc2LCJpYXQiOjE3MDQ3NjA3NzYsInVzZXJuYW1lIjoia2V2aW4ifQ
//          .Wvxdsp-ftlv9R97BELDBwwVN0H7rbN8P1JH9dBVzES8

The token will be in base64url form.

Note: Please read the next section for more information on the algorithm used to generate the signature, the format of the secret key, and best practices.


To get a new JWT object back from the token, use the JWT.fromToken() static method:

const secret = "DD4E8D770CF2CCDBEC698D005D049DB432805D223F8441B709746EB33A8F8711";

const token = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJpc3MiOiJsaWFvLmdnIiwiZXhwIjoxNzA0NzY0Mzc2LCJpYXQiOjE3MDQ3NjA3NzYsInVzZXJuYW1lIjoia2V2aW4ifQ
.Wvxdsp-ftlv9R97BELDBwwVN0H7rbN8P1JH9dBVzES8`.split("\n").join(""); // Token from above

const jwt = JWT.fromToken(token, secret);
assert(jwt !== null);
assert(jwt.getClaim("username") === "kevin");

const newToken = jwt.addClaim("username", "admin").getToken(secret);

To get the Header and Payload object back from a token, use the JWT.unwrap() static method:

const secret = "DD4E8D770CF2CCDBEC698D005D049DB432805D223F8441B709746EB33A8F8711";

const token = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJpc3MiOiJsaWFvLmdnIiwiZXhwIjoxNzA0NzY0Mzc2LCJpYXQiOjE3MDQ3NjA3NzYsInVzZXJuYW1lIjoia2V2aW4ifQ
.Wvxdsp-ftlv9R97BELDBwwVN0H7rbN8P1JH9dBVzES8`.split("\n").join(""); // Token from above

const unwrapped = JWT.unwrap(token, secret);
assert(unwrapped !== null);
const [ header, payload ] = unwrapped;
assert(payload.username === "kevin");
assert(payload.nonexistant === undefined);

// If the token is malformed or is not consistent with the secret, null will be returned
const badTokenUnwrapped = JWT.unwrap(token.substring(1), secret);
assert(badTokenUnwrapped === null);

const badSecretUnwrapped = JWT.unwrap(token, secret.split("").reverse().join(""));
assert(badSecretUnwrapped === null);

Note: It is recommended that you just use the JWT.fromToken() method instead.


To quickly check if a token is expired, use the JWT.expired() static method:

const secret = "DD4E8D770CF2CCDBEC698D005D049DB432805D223F8441B709746EB33A8F8711";

const expiredJwt = new JWT("liao.gg", unixTime() - 60); // Expired a minute ago
const expiredToken = expiredJwt.getToken(secret);
assert(JWT.expired(expiredToken, secret));

const unexpiredJwt = new JWT("liao.gg", unixTime() + 7 * 24 * 60 * 60); // Expires in 1 week
const unexpiredToken = unexpiredJwt.getToken(secret);
assert(JWT.expired(unexpiredToken, secret) === false);

// If the token fails to verify, or the "exp" field is invalid, the token will be considered expired.
assert(JWT.expired(unexpiredToken.substring(1), secret));

To quickly check if a token is valid, (consistent with your secret), use the JWT.verify() static method:

const secret = "DD4E8D770CF2CCDBEC698D005D049DB432805D223F8441B709746EB33A8F8711";

const issuedByUs = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJpc3MiOiJsaWFvLmdnIiwiZXhwIjoxNzA0NzY0Mzc2LCJpYXQiOjE3MDQ3NjA3NzYsInVzZXJuYW1lIjoia2V2aW4ifQ
.Wvxdsp-ftlv9R97BELDBwwVN0H7rbN8P1JH9dBVzES8`.split("\n").join(""); // Token from above
assert(JWT.verify(issuedByUs, secret));

const notIssuedByUs = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJpc3MiOiJsaWFvLmdnIiwiZXhwIjoxNzA0NzY2NjgzLCJpYXQiOjE3MDQ3NjMwODMsInVzZXJuYW1lIjoia2V2aW4ifQ
.sK59XyXA6iyvGiPUEokAQMqh0joa82fA_j20iFdOwX4`.split("\n").join(""); // Signed with different secret
assert(JWT.verify(notIssuedByUs, secret) === false);

Note: In most cases, you won't need to use this, as JWT.fromToken(), JWT.unwrap(), and JWT.expired() all call JWT.verify() internally anyways.

Token Signature

Algorithm

jwt-km uses the HS256 algorithm to create the signature for the token. This is the default, and it is the only algorithm which is supported as of right now. However, for all intents and purposes, HS256 is secure enough for virtually every use case as long as your key is secure enough.

If you really feel the need to use another signature generation algorithm, feel free to contribute to the project by opening a pull request (more information below), or to simply copy and paste the jwt.js or jwt.ts file and modify it for use in your own project.

Secret Key

The secret key should be in the form of a hex string representing at least 256 bits. This means that the hex string which you provide should be at least 64 hexes long.

const goodSecret = `0D9C1749A81DC9B132CE922A2F6CA6F6C873EB65BFF127004468F50E5090429B`;

The maximum length of the secret key is 512 bits, or 128 hexes long.

In actuality, you can provide a secret longer than this, but because the maximum key size for the HS256 algorithm is 512 bits, any key that is longer will be hashed and its hash will be used instead. Since we are using HS256, this actually results in a less secure key being used, as the hashed output of the key will be 256 bits, or only 64 hexes long.

const maximallySecureSecret     = `0B667742CC78EFFCEB2050131334EEF9A9442603998BF744B62F1C3665EDBE31F7FF052FF64AAFCCD552CCDA25885567646DFE7220BBC15EE937122089825739`;
const longerButLessSecureSecret = `02AA059373520AB46F1BADBE47D5183264FBD923A45952FDDDBE682983A1A88E61997E30F51C9A0ABB050E5D1FC65F4611631C15F872057CD6425B6BCE4DD9B56`;

To generate a secret key, run the snippet below as a script file, in AN EMPTY TAB in your browser's console, or in the Node REPL:

const keyset = "1234567890ABCDEF".split("");
const keyLength = 64; // Replace with higher number if so desired, up to 128.
let secret = "";
for(let i = 0; i < keyLength; i++) {
    secret += keyset[Math.floor(Math.random() * 16)];
}
console.log(secret);

DO NOT use any website or any program which you do not know the source code of to generate your secret key.


It is not recommended that you hard code your secret key into your project files. Instead, use a .env file and store the secret there:

# Inside file .env
SECRET=69BAD09D17B2BA0E8FB5A6B93965F35ED381EC9663F94A1F853A5ACEBE6B75AB
// Inside project files
const secret = process.env.SECRET || (() => { throw new Error("No secret provided.") })();

Note: If possible, we recommend using the --env-file=.env option native to Node to load env files in NodeJS v20 and above.

Contributing

Before contributing, please consider if your proposed change / addition aligns with jwt-km's principle of simplicity, minimalism, and ease of use. If you're not sure, open a ticket to discuss your idea with us.

To contribute, fork this repository, make your changes, and open a pull request when you're ready. We welcome all relevant changes and strive to provide the best experience for our users.

Project Structure

jwt.ts - The file implementing the JWT class

test.ts - Relevant unit tests

Learn More

JSON Web Tokens on Wikipedia