JWT Generator
Mint a signed test token in one glance. Edit the claims, pick any algorithm, or deliberately break the signature to test that your server rejects it.
alg and typ are set for you. Add kid or other members here.
Token
Supply a secret to mint a token.
Testing your rejection path
The token still parses, but every correct verifier must reject it. Use it to prove your server actually checks the signature.
Generate an alg:none token
An alg:none token carries no signature at all. Historically, libraries that trusted the header's alg would accept these as valid — the attacker rewrites the payload, sets "alg":"none", and drops the signature. Your server should reject it outright. Generate one here to prove that it does.
Choosing an algorithm
The first decision is symmetric or asymmetric, and it is really a question about who needs to verify.
| Family | Key | Use it when |
|---|---|---|
HS256/384/512 | One shared secret | The same service issues and verifies. Simple and fast — but everyone who can verify can also forge. |
RS256/384/512 | RSA key pair | Others verify your tokens. They get the public key; only you can sign. The default for OIDC providers. |
PS256/384/512 | RSA key pair | Same as RS, with a modern probabilistic padding scheme (RSA-PSS). Prefer it over RS for new systems. |
ES256/384/512 | Elliptic-curve key pair | Same trust model as RS, with far smaller keys and signatures. Good for bandwidth-sensitive systems. |
EdDSA | Ed25519 key pair | Fast, small, and hard to implement badly. The best default when your whole stack supports it. |
The rule that matters more than the choice: whichever you pick, pin it on the verifying side. A server that reads the algorithm out of the token it is verifying is exploitable regardless of which algorithm it was supposed to be using. See the algorithm-confusion explainer.
Signing a JWT in code
Node.js (jsonwebtoken)
import jwt from 'jsonwebtoken';
const token = jwt.sign(
{ sub: '1234567890', name: 'Test User' },
process.env.JWT_SECRET, // never a literal
{ algorithm: 'HS256', expiresIn: '1h' }
);Python (PyJWT)
import jwt, os, datetime
token = jwt.encode(
{
"sub": "1234567890",
"name": "Test User",
"exp": datetime.datetime.now(datetime.UTC) + datetime.timedelta(hours=1),
},
os.environ["JWT_SECRET"],
algorithm="HS256",
)Go (golang-jwt)
import (
"os"
"time"
"github.com/golang-jwt/jwt/v5"
)
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": "1234567890",
"exp": time.Now().Add(time.Hour).Unix(),
})
signed, err := token.SignedString([]byte(os.Getenv("JWT_SECRET")))Java (jjwt)
import io.jsonwebtoken.Jwts;
import java.util.Date;
String token = Jwts.builder()
.subject("1234567890")
.expiration(new Date(System.currentTimeMillis() + 3_600_000))
.signWith(key) // a javax.crypto.SecretKey, not a String
.compact();Testing that your server rejects bad tokens
Verifying that the happy path works is the easy half. The half that actually protects you is proving the unhappy paths fail. Every one of these should be rejected, and each is one toggle away on this page:
- A tampered signature. Flip the toggle above. Your server must return 401, not 200. If it returns 200, it isn't verifying at all.
- An
alg:nonetoken. Behind the expander above. A 200 here is a critical vulnerability — anyone can mint a token for any user. - An expired token. Set
expto a past timestamp. Confirm your server checks expiry, and confirm the clock skew you allow is small. - A token with the wrong
aud. A token minted for another service must not be accepted by yours, however impeccable its signature. - A token signed by the wrong key. Generate a second key pair and sign with it. Rejection should be immediate.
Turn each of these into a test case in your suite. They are cheap to write and they catch the failures that matter.
A note on the secrets this page generates
The default HMAC secret is 256 bits from crypto.getRandomValues(), which is a cryptographically secure source. It is fine for testing. For a production secret, generate it where it will live — in your secret manager, or via openssl rand -base64 32 — and never let it pass through a browser, a chat message or a commit. The secret key generator page explains what a good HMAC secret needs to look like.
Common questions
Are the tokens and keys generated here safe to use in production?
The tokens, yes, if you signed them with a key you control and trust. The generated key pairs, no. They are produced by your browser's WebCrypto in a tab you found on the internet — perfectly good randomness, but a key's security depends on where it has been, and this one has been in a web page. Use browser-generated key pairs for testing only; generate production keys with openssl or your KMS.
Why does this tool let me create an invalid token?
Because you need one. The only way to know your server rejects a tampered token is to send it a tampered token. Any dev can produce one in three lines of code, so refusing to help doesn't prevent anything — it just makes the tool useless for the test you actually need to run. We generate them clearly labelled, with an explanation of what they're for.
What is an alg:none token?
A token that declares it has no signature. Historically, JWT libraries that read the algorithm from the token header would accept these as valid — meaning anyone could forge any payload. Your server must reject them outright. Generate one here and fire it at your endpoint; if you get a 200 back, you have found a critical vulnerability.
How long should my test token last?
For a test, as short as you can stand — long enough to make the request, no longer. In production, access tokens should be minutes, not days: a token cannot be revoked once issued, so its lifetime is exactly the window an attacker gets from a stolen copy. Use a refresh token for longevity, and keep the access token brief.
The rest of the workbench
JWT Decoder
Read a token's header, payload and claims — including expired ones. Nothing is uploaded.
JWT Validator
Verify a signature against a secret, PEM, JWK or JWKS. Signature and expiry are judged separately.
JWT Secret Key Generator
Generate a cryptographically random HMAC secret at 256, 384 or 512 bits.