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 🙏

© 2026 – Pkg Stats / Ryan Hefner

tokengrip

v0.3.2

Published

Creates and verifies signed tokens using rotated credentials

Readme

tokengrip

Similar to Keygrip, this library helps create and verify digital signatures using a rotating credential system. Unlike Keygrip, however, it produces self-contained JWT-like tokens which contain the signing algorthm, a JSON-encoded payload, and the signature itself.

Tokengrip tokens are not JWT's

Although they are similar to JWT's, the tokens created by Tokengrip are deliberately not compliant with the JWT standard. This standard is massively complex and intended to handle use cases far beyond what is required for simple symmetrically-signed authentication tokens.

Most notably, Tokengrip does not use the same algorithm specifiers, instead using those consumed by Node's crypto.createHmac function. It also does not concern itself with any aysmmetric signing algorithms, built-in expiration times, or any of the other "standard claims" of JWT.

Rationale

Keygrip is nice, but unless you are using it with Cookies to automatically sign, verify, and update HTTP cookies using the signed: true option, it leaves a bit of work for you.

Most trivially, since it returns signatures that do not include the signed data, it is up to you to either join these and split them later, or to provide both to your clients, who must send both with future requests. The Cookies module uses the latter approach by setting the signature as a second cookie with the same name as the signed cookie, except with the suffix .sig. Tokengrip solves this problem the former way.

Less trivially, while Keygrip allows you to change your keys in a rotating fashion, it does not allow you to change signing algorithms in the same way. Once a Keygrip signature is created, there is no way of knowing what algorithm was used to create it besides trying all of the supported algorithms one-by-one. Tokengrip solves this by including the signing algorithm in a token header-- similar to how JWT's do it.

Finally, Cookies has a nice feature which uses Keygrip's #index method to detect when a signature was created with an outdated key, and automatically replacing the signature with one using the newest key every time you fetch a signed cookie. Without this feature, valid signatures would eventually become invalid, no matter how frequently they are used in requests, which obviously defeats the purpose of using Keygrip in the first place.

Tokengrip aims to reproduce this feature within its own API. Its #verify method will perform this check and include a replacement token in its return value, if and only if such a replacement is necessary. This allows you to perform the replacement in whatever way is appropriate. If you are storing your tokens in a cookie, simply overwrite the cookie. If you are leaving it to your clients to store their tokens, you can simply send it as a response header.

API

grip = new TokenGrip([keys],[algorithms])

This creates a new Tokengrip instance with the provided keys and algorithms. Either argument can be provided as a single string or an array of strings. keys defaults to an empty array, and algorithms defaults to 'sha1'.

grip.keys

You can edit this property as you see fit to change the keys in your instance-- mutate it with unshift and pop or replace it entirely by assigning over it.

grip.algorithms

Same as grip.keys, except changing it affects the signing algorithms allowed by the instance.

token=grip.sign(payload)

Creates a new token with the provided payload, which must be a valid argument to JSON.stringify. The token will be signed using the first key and the first algorithm.

{ payload, newToken } = grip.verify(token)

Consumes a token, returning its payload as the payload property of the result. If the token was signed with an outdated key or algorithm, the result will also have a newnewToken property, which will be a new token with the same payload, except re-signed with the newest key and algorithm. This method will throw if verfication fails with all keys, or if the token is simply invalid due to an unsupported algorithm or invalid JSON.

payload = decodePayload(token)

Extracts the decoded payload of a token, without checking the signature. This should only be used if it is helpful to know something about the token before the signature is checked-- an expiration date, for example, so you know if the token is expired before you even bother with verifying it.

Data extracted this way should not be trusted until the signature is checked.

newToken = grip.checkSignature(token)

This is similar to grip#verify, except that it does not decode the payload. Like grip#verify it returns a new token if one is needed. It will return null otherwise.

This method is useful for checking a signature after you've already decoded the payload using decodePayload. grip#verify is simply a combination of both of these operations, since the majority of the time you'll want to be doing both.

Error Handling

Tokengrip uses Nani to create the errors it throws, allowing you to easily identify them using the is function. Three error classes are exported:

  • TokengripError: This is the base class of all of the following errors for namespacing purposes.

  • InvalidStateError: Will be thrown when an attempt is made to sign or verify a token when either the keys or algorithms arrays are empty. If you catch one of these, you should probably re-throw it, as it indicates a mistake on the part of the developer-- for example, popping out too many keys,or failing to provide any to begin with.

  • InvalidTokenError: Will be thrown when a token is provided that is not valid. This can be due to the token having been tampered with, or having been signed by a key or alorithm that is no longer allowed by the Tokengrip instance.

  • InvalidSignatureError: A subtype of InvalidTokenError, indicating specifically that the signature was checked against all of the keys and did not match with any of them. This can happen if the token was tampered with, or if it was originally signed by a key that is no longer in the instance. Unlike a base InvalidTokenError, though, you can rule out a disallowed alogrithm, or that a string was provided that simply isn't a Tokengrip token.

Example

import { Tokengrip, InvalidTokenError } from 'tokengrip';
import { is } from 'Nani';

// Create the instance and store it somewhere.
const grip = new Tokengrip(
	[ 'seeecrat', 'wow another seecrat' ],
	[ 'sha256', 'sha1' ],
);

// ...

// In your login endpoint. You can put whatever data is needed in the body.
const token = grip.sign({ foo: 'bar' });
// Give the token to the client in cookie, header, or response body...

// ...

// In your api middleware:
let payload;
let newToken;

try {
	// Get the token from the request and verify it.
	({ payload, newToken } = grip.verify(token));
} catch (err) {
	if (is(err, InvalidTokenError)) {
		// Should cause a 401 or whatever is appropriate for your API.
		throw MyCustomError();
	} else {
		// Rethrow any other errors.
		throw err;
	}
}

// Do whatever is needed with the payload....

if (newToken) {
	// Give the new token to the client...
}