Generate JWT for Testing

Jun 8, 2021 min read

JSON Web Tokens have become increasingly popular in recent years because of their powerful capabilities to exchange information between servers and services in a verifiable way. (Note: I did not say secure or encrypted :) ) Often JSON Web Tokens or JWTs (pronounced “jot”) are issued by an authorization server, sometimes outside of our application or organization. This is great, we should leave the JWT signing to a group that specializes in and understands the potential risks associated with using JWTs. However, during the development and testing phases of software building relying on a third party to provide a JWT with all of the relevant we need for a specific scenario can be difficult and time-consuming.

In this article we’ll look at generating JSON Web Tokens (JWT) for testing purposes. The key problem that I’m trying to solve with this solution is how can I modify the claims (user/ use case specific information stored in header/payload of JWT) to match my current development task. For example, I might need to test an API as a specific type of user, i.e. manager vs. admin, is most scenarios that information is available in a claim in the payload of the JWT. I could create a new user account, provide all the necessary permissions to get a token that looks like what I need OR I could just create a local JWT.

A couple important security callouts/ considerations:

  • I’m not “disabling” security during development, I’m simply assuming that the user is already authenticated with a has provided a valid JWT that I can verify.
  • I’m not creating a “backdoor” for someone to access the application, generating a JWT in this way should, and will only, be used for development and testing. It will fail when promoted across environments.

To create a new JWT I’m using a NodeJS CLI (command line interface) to generate a token with the claims I want using a signing key(s) that I can access. 🚨 Again, this should not be a solution for generating JWTs in any environment outside of development! 🚨 For this solution we’re going to be using the jwtgen CLI provided by Vandium Software. Since jwtgen is an NPM package we can use npx to execute our commands without “installing” the dependency in our project.

With jwtgen we can pass in claims, expiration times, signing algorithms, and more. I’ve found this package to have the features I need without a lot of bloat. A simple example request to jwtgen might look like:

npx jwtgen -a HS256 -s "my-secret" -c "iss=user123" -e 3600

This will generate a simple JWT with the additional claim of iss: user123 (Issuer) that represents the entity issuing the token.

For my use cases I needed a slightly more complex solution. I needed to generate a JWT that contained a nested JSON object and I wanted to use a secret to sign the JWT that came from my projects .env file.

First the nested claim object, jwtgen allows us to pass a JSON string to the CLI using the --claims flag. That looked like this:

npx jwtgen -a HS256 -s "my-secret" --claims '{"group":{"test":"testing"}}' -e 3600

Secondly, I wanted to use a secret from my projects .env file. Wanting something with the least amount of dependencies possible, I used Bash to parse a value from the .env file:

$(grep JWT_SECRET .env | cut -d '=' -f2)

This will grep for my environment variable, JWT_SECRET, and return the value after the = sign.

The entire command now looks like:

npx jwtgen -a HS256 -s $(grep JWT_SECRET .env | cut -d '=' -f2) --claims '{"group":{"test":"testing"}}' -e 3600

To go a step further I use the pbcopy copy command to pipe the results from jwtgen directly to my clipboard to pasting into various testing tools.

npx jwtgen -a HS256 -s $(grep JWT_SECRET .env | cut -d '=' -f2) --claims '{"group":{"test":"testing"}}' -e 3600 | pbcopy

Having control over the information being passed between servers and services during development and testing is critical to understanding how an application will behave in different scenarios. This is just one tool of many that I use to test applications and APIs.