Build a JWT with your own header and payload and sign it with HMAC. Use it to test an endpoint, or to see what a set of claims looks like once it is encoded. The tokens are for testing; production tokens belong on your server.
A JSON Web Token (RFC 7519) carries claims such as a user ID and an expiry time between two parties. The signature lets the receiver check that the claims were not changed in transit. However, the payload is only Base64url-encoded, so anyone holding the token can read it.
Algorithm and token type
Claims such as sub and exp
HMAC over header.payload
The jsrsasign library signs the token in this tab, so the secret is never sent to a server. Sign production tokens on your backend. A secret typed into a browser can end up in clipboard history or a screen share, and any service holding an HS256 secret can mint tokens.
const { createHmac } = require("node:crypto");
// HS256 token with no dependencies
function signHS256(payload, secret) {
const b64url = (obj) => Buffer.from(JSON.stringify(obj)).toString("base64url");
const header = b64url({ alg: "HS256", typ: "JWT" });
const body = b64url(payload);
const signature = createHmac("sha256", secret).update(`${header}.${body}`).digest("base64url");
return `${header}.${body}.${signature}`;
}
const now = Math.floor(Date.now() / 1000);
const token = signHS256(
{ sub: "1234567890", name: "John Doe", iat: now, exp: now + 3600 },
"your-256-bit-secret"
);
console.log(token);
// With the jsonwebtoken package (npm i jsonwebtoken):
// const jwt = require("jsonwebtoken");
// jwt.sign({ sub: "1234567890", name: "John Doe" }, "your-256-bit-secret", { expiresIn: "1h" }); The hand-rolled version shows how the format works. In production, use jsonwebtoken or jose, which also handle RS256, key rotation and claim validation.
<?php
// composer require firebase/php-jwt
require 'vendor/autoload.php';
use Firebase\JWT\JWT;
$payload = [
'sub' => '1234567890',
'name' => 'John Doe',
'iat' => time(),
'exp' => time() + 3600,
];
$token = JWT::encode($payload, 'your-256-bit-secret', 'HS256');
echo $token, PHP_EOL; Keep the secret in an environment variable, not in the source file.
# pip install PyJWT
import time
import jwt
payload = {
"sub": "1234567890",
"name": "John Doe",
"iat": int(time.time()),
"exp": int(time.time()) + 3600,
}
token = jwt.encode(payload, "your-256-bit-secret", algorithm="HS256")
print(token) Install PyJWT, not the older unrelated package named jwt; both import as jwt and the wrong one has no encode() with this signature.
A JWT is three Base64url strings joined by dots: header, payload, signature. With HS256 the signature is an HMAC-SHA256 over header.payload with a shared secret, which means any service that can verify a token can also mint one. That works for a single backend. Once several services or a third party need to verify tokens, switch to RS256 or ES256, where the issuer signs with a private key and everyone else holds only the public one.
The payload is encoded, not encrypted. Anyone holding the token can read every claim, so keep passwords, personal data and internal IDs out of it. Use the standard claim names from RFC 7519 (iss, sub, aud, exp, iat, jti) rather than inventing your own. Keep exp short, for example 15 minutes, and pair it with a refresh token, so a leaked token stops working soon after it leaks.
This tool follows the specifications below. Read them when you need the exact normative wording, for example to settle an edge case between two implementations.
HS256 (HMAC with SHA-256) by default, the most widely used symmetric algorithm. The same secret signs and verifies the token. Changing alg in the header to HS384 or HS512 signs with that algorithm instead.
Usually sub (subject), iat (issued at), exp (expiry) and any application-specific claims such as roles. exp is a Unix timestamp in seconds, for example 1767225600 for 1 January 2026 at 00:00 UTC.
Use it for testing and debugging. For production, generate tokens on your server with a maintained library such as jose or PyJWT, and keep the secret out of the browser.
Check four things: the secret matches, the exp claim is in the future, the API expects HS256 and not RS256, and the token was not altered after signing. Paste it into the JWT Decoder with the secret to confirm the signature.
No. The jsrsasign library signs the token in your browser, and the page sends nothing.
The JWT decoder reads a token back and verifies the signature. The HMAC generator computes that signature on its own if you want to check the math.