Build a signed JSON Web Token (JWT)
- Dashboard
- Documentation
- API
What is JWT Builder?
JWT Builder is an online tool that produces a signed JSON Web Token (JWT) from a JSON payload and an HMAC secret. It is for developers who need to quickly generate a test token to check an API's behaviour, simulate an authenticated session in Postman, or reproduce a bug related to a token's expiry or scope.
Unlike our JWT decoder, which just reads an existing
token, JWT Builder builds a complete token: it builds the header, encodes the
payload, computes the HMAC signature and assembles everything in the compact header.payload.signature
format expected by every JWT library on the market.
Why generate a JWT?
A JWT builder is not meant to issue production tokens. It is first and foremost a development and testing tool. Here are the most common scenarios:
- Integration tests: produce predictable JWTs to drive E2E tests that hit protected endpoints without depending on the real identity provider.
- API mocks: temporarily replace a call to the IdP with a JWT signed locally with the same test secret.
- Local development: log in to your own backend by manually generating a token containing the desired claims, without having to go through the whole OAuth2 flow.
- Demos: illustrate an authentication journey or a workflow based on permissions without having a real IdP at hand.
- Bug reproduction: forge an expired token, a token with an incorrect
aud, a token without some claims, to test the error paths of your API. - Debugging a custom parser: inject specially crafted JWTs to test a homemade parser.
Composition of a JWT
A signed JWT is composed of three segments separated by a dot, each encoded in
Base64URL (a variant of Base64 without padding and with -/_ instead
of +//):
- Header: a JSON object that describes the signature algorithm and the token type,
for example
{"alg":"HS256","typ":"JWT"}. JWT Builder generates it automatically from the algorithm you pick. - Payload: an arbitrary JSON object that contains the token's claims (subject, permissions, expiry dates). This is the part you provide.
- Signature: an HMAC-SHA computed on the concatenation
base64url(header) + "." + base64url(payload)with your secret key. It is what guarantees the token's integrity.
Use cases in detail
- API mock for E2E tests: your Cypress or Playwright suite has to call an API
that requires an
Authorization: Bearer .... Rather than orchestrating a full login on every test, sign a JWT on the fly with the shared secret and inject it into the header. - SSO demo: present a federated authentication journey without depending on an online IdP.
- Test fixture: generate a deterministic JWT from a known payload to serve as a stable fixture in unit tests.
- Diagnosing a custom parser: test a homemade JWT parser with deliberately minimal or deliberately odd tokens (missing claims, unexpected types).
- Learning: concretely understand how a JWT is built, by modifying the payload and watching the signature change.
Supported algorithms: HS256, HS384, HS512
Our tool supports the three standard HMAC algorithms of the JWT specification (RFC 7519):
- HS256: HMAC with SHA-256. 32-byte signature. The most used in practice, a good trade-off between speed and cryptographic strength. Recommended by default.
- HS384: HMAC with SHA-384. 48-byte signature. Suited to contexts that require a higher security margin.
- HS512: HMAC with SHA-512. 64-byte signature. The most robust, at the cost of a slightly bigger token.
HMAC or RSA?
The HS* algorithms are symmetric: the same key is used to sign and to verify. It is simple and fast, but it means that any service able to verify a token is also able to issue one. If you need to separate these two roles (a single issuer, multiple consumers), turn to the RS256/RS384/RS512 algorithms (RSA, asymmetric), which you can verify with our JWT verifier.
Security: protecting your secret
The security of an HMAC-signed JWT relies entirely on the secret's confidentiality. A few basic rules:
- Use a long, random secret. RFC 7518 recommends at least the size
of the algorithm's output (32 bytes for HS256, 48 for HS384, 64 for HS512). A
human password like
azerty123is trivially brute-forceable offline. - Never sign a JWT on the client side. The secret would end up in the JavaScript code shipped to the browser, exposed to every user. Signing must always stay on the server side.
- Store the secret in an environment variable (e.g.
JWT_SECRET), never in a Git repository. Consider using a vault like HashiCorp Vault, AWS Secrets Manager or Symfony Secrets depending on your stack. - Rotate the secret regularly (key rotation), especially after any incident or when someone with access to the configuration leaves.
- JWT Builder is meant for testing and learning. For production, use your framework's JWT library (lcobucci/jwt, firebase/php-jwt, jose-php).
Claims best practices
The payload is a free-form JSON object, but RFC 7519 defines a set of registered claims that JWT libraries know how to interpret. Including the right claims makes your token portable and avoids subtle bugs:
- iss (issuer): identifier of the issuer, for example
"https://api.example.com". - sub (subject): identifier of the person or entity concerned, often the user ID.
- aud (audience): who the token is intended for, to prevent reusing a token on another API.
- exp (expiration time): Unix timestamp after which the token is no longer valid. Always include, even for a test token: a token without expiry is a bad habit hard to correct later.
- nbf (not before): timestamp before which the token is not yet valid. Useful to pre-issue a token that activates later.
- iat (issued at): issuance timestamp, useful to log and to revoke.
- jti (JWT ID): unique identifier of the token, essential for idempotence and to implement a revocation list.
Typical payload example
{
"iss": "https://api.example.com",
"sub": "user-12345",
"aud": "mobile-app",
"iat": 1714723200,
"exp": 1714726800,
"jti": "9f2d6b1e-2c4a-4f8a-9c3a-87a2b8a4b7e1",
"scope": "read:profile write:profile"
}
How to use the tool
- Enter the payload as valid JSON. It is an object, so it starts with
{and ends with}. - Provide the HMAC secret. Choose a long random string for production tokens.
- Pick the algorithm: HS256 by default, HS384 or HS512 depending on your needs.
- Click create. The signed JWT appears, ready to paste into an
Authorization: Bearer ...header. - You can then verify the token with the same secret to confirm round-trip consistency, or decode it to read its content.
Frequently asked questions
Which algorithm to pick: HS256, HS384, HS512?
For almost every case, HS256 is the right choice. It offers a security level perfectly sufficient for authentication tokens, with a compact signature (32 bytes) and fast computation. HS384 and HS512 are only justified in specific regulatory contexts or if you handle very long-lived tokens. The higher signature size weighs down every HTTP request.
How do I generate an RSA pair to sign my JWTs?
With OpenSSL in two commands: openssl genrsa -out private.pem 2048 for the private
key, then openssl rsa -in private.pem -pubout -out public.pem to extract
the public key. For new services, 3072-bit or 4096-bit keys are
recommended today. The private key stays with the issuer; the public key is freely distributed
to the services that need to verify tokens.
What is the recommended expiry duration?
For an access token: 5 to 15 minutes. For a refresh token: a few days to
a few weeks, but with a server-side revocation mechanism. The longer-lived the token,
the wider the exploitation window in case of leakage. For test JWTs, you
can pick a more generous duration, but avoid exp set several years out:
they end up leaking into Git repositories.
Can I sign with a very short secret key?
Technically yes, but it is strongly discouraged. An HMAC secret under
16 bytes is weakly resistant to offline brute-force attacks, and tools available in the
wild crack a weak-secret HS256 JWT in seconds. RFC 7518 recommends at least the size
of the algorithm's output: 32 bytes for HS256,
48 for HS384, 64 for HS512. Generate your secrets with
openssl rand -base64 64.
Why is my payload rejected?
The payload must be a valid JSON object. Common mistakes: single quotes instead of
double, extra comma before }, unquoted value for a string. Validate
your JSON first with our JSON formatter.
Can the generated JWT be decrypted by someone else?
A signed JWT is not encrypted: the payload is just Base64URL-encoded. Anyone can read it. If the payload contains sensitive data, use the JWE format (JSON Web Encryption) which adds encryption. Our tool produces a JWS (signed only).
Example request
curl -X POST https://cdrn.fr/api/v1/tools/jwt-builder/execute \
-H "Content-Type: application/json" \
-d '{"payload":"...","secret":"...","algorithm":"HS256"}'
Input schema
| Field | Type | Required | Default |
|---|---|---|---|
payload |
text | ✓ | – |
secret |
text | ✓ | – |
algorithm |
choice (HS256, HS384, HS512) | ✓ | HS256 |
Endpoints
GET https://cdrn.fr/api/v1/tools- lists every available toolGET https://cdrn.fr/api/v1/tools/jwt-builder- returns the schema for this toolPOST https://cdrn.fr/api/v1/tools/jwt-builder/execute- runs this tool with a JSON payload