In the world of modern web applications, secure communication between clients and servers is paramount. One of the most popular and effective methods for achieving this is through JSON Web Tokens (JWTs). We're excited to announce that Bruno now offers native support for the jsonwebtoken library, allowing you to seamlessly integrate JWT generation, signing, and verification directly within your API testing workflows.
A JSON Web Token (JWT, pronounced "jot") is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed.
A JWT typically consists of three parts, separated by dots (.):
The beauty of JWTs lies in their self-contained nature. All the necessary information about the user is embedded within the token itself, reducing the need for constant database lookups and improving API performance.
JWTs are crucial for several reasons, especially in the context of API development and testing:
At the core of JWT security are the processes of signing and verification:
This is the process of creating a JWT. You provide a payload (the data you want to transmit), a secret key (known only to the issuer), and optional header and signing options. The jsonwebtoken library then uses the specified algorithm to generate a unique signature, combining all three parts into the final token.
Example Scenario: When a user logs in, the server generates a JWT containing their user ID and role, signs it with a secret, and sends it back to the client. The client then includes this JWT in subsequent API requests.
This is the process of checking if a received JWT is valid. You provide the received token and the same secret key used for signing. The jsonwebtoken library recomputes the signature based on the header and payload and compares it with the signature present in the token. If they match, and the token hasn't expired or been tampered with, the token is considered valid, and its payload can be safely decoded and used.
Example Scenario: When an API receives a request with a JWT in the `Authorization` header, it verifies the token using its secret key. If valid, the API can trust the claims within the token to identify the user and determine their permissions.
jsonwebtoken in Bruno for Seamless API TestingBruno's integration with jsonwebtoken allows you to create robust testing workflows that involve generating, signing, and verifying JWTs directly within your requests and post-scripts. Here's how you can set it up and use it:
jsonwebtoken ModuleSince Bruno runs a sandboxed environment, you need to install jsonwebtoken within your project's `node_modules` and then whitelist it. First, navigate to your Bruno collection directory in your terminal and install the package:
npm init -y # If you don't have a package.json
npm install jsonwebtoken express # Express is often useful for local servers or mocking
To allow Bruno to use external modules like jsonwebtoken, you need to add it to your collection's whitelist. This is done in your bruno.json file. Locate your bruno.json file at the root of your Bruno collection and add the following:
{
"scripts": {
"moduleWhitelist": ["jsonwebtoken"], // Add "jsonwebtoken" here
"filesystemAccess": {
"allow": true
}
},
}
Note: Make sure jsonwebtoken is present under both dependencies and scripts.moduleWhitelist in your bruno.json and package.json for Bruno to properly recognize and load it. If you have a separate package.json in your collection root, Bruno will respect its dependencies.
You can generate a JWT in a pre-request script and store it in an environment variable for subsequent requests. This is perfect for obtaining an `access_token` after a login request.
First, set up your environment variables in Bruno. You'll need `JWT_SECRET`, `USER_ID`, and `USER_ROLE`.
const jwt = require("jsonwebtoken");
// Read env vars
const secret = bru.getEnvVar("JWT_SECRET");
const userId = bru.getEnvVar("USER_ID");
const userRole = bru.getEnvVar("USER_ROLE");
// Safety check
if (!secret) {
throw new Error("JWT_SECRET environment variable is missing.");
}
// Build payload
const payload = {
sub: userId,
role: userRole,
issuedBy: "Bruno-Demo" // Optional: identifier for the issuer
};
// Build signing options
const options = {
algorithm: "HS256", // Choose your signing algorithm
expiresIn: "15m", // Token expiration time
audience: "bruno-demo",// Audience for the token
issuer: "bruno-demo" // Issuer of the token
};
// Sign token
const token = jwt.sign(payload, secret, options);
// Save for later usage in environment (e.g., in a subsequent request)
bru.setEnvVar("access_token", token, { persist: false });
// Attach header for the current request
req.setHeader("Authorization", `Bearer ${token}`);
// For debugging: see the generated token in the console
console.log("Generated JWT:", token);
Important: This script will generate a JWT, set it as an environment variable `access_token`, and also include it as an `Authorization` header for the current request. The `persist: false` option for `setEnvVar` means the token will only be available for the current Bruno run and not saved permanently across sessions, which is often desired for sensitive tokens.
After your API request returns, you can use a post-response script to verify the token received or to validate a token that you've previously stored. This is excellent for testing your API's authentication and authorization mechanisms.
const jwt = require("jsonwebtoken");
// Get token from env (assuming it was set in a pre-request script or from response)
const token = bru.getEnvVar("access_token");
const secret = bru.getEnvVar("JWT_SECRET");
// Safety check
if (!secret) {
throw new Error("JWT_SECRET environment variable is missing.");
}
if (!token) {
throw new Error("access_token environment variable is missing for verification.");
}
// Verify token
let decoded;
try {
decoded = jwt.verify(token, secret);
console.log("Verified Token Payload:", decoded);
// Save specific claims for later usage in subsequent requests or tests
if (decoded.sub) {
bru.setVar("decoded_user_id", decoded.sub);
}
if (decoded.role) {
bru.setVar("decoded_user_role", decoded.role);
}
} catch (err) {
// If verification fails, the test will fail and show the error
console.error("JWT Verification Error:", err.message);
}
This post-response script retrieves the `access_token` and `JWT_SECRET` from your environment, attempts to verify the token, logs the decoded payload, and then runs several tests to ensure the token is valid and contains the expected claims. If verification fails, the test will gracefully report the error.
After token generation, you will see the value of the access token is set to automatically fetch from the JWT token, and you can use it in subsequent request auth headers like below.
We’ve published the full working collection on GitHub. You can either clone this or simply click the Fetch in Bruno button below!
The new `jsonwebtoken`support in Bruno significantly enhances your ability to test and interact with secure APIs. By directly integrating JWT generation and verification into your pre-request and post-response scripts, you can create more realistic, efficient, and robust testing workflows, ensuring your APIs are both functional and secure
Happy Testing with Bruno! 🚀