Build a signed JSON Web Token (JWT)

builds a signed JWT (HS256, HS384, HS512) from a JSON payload and an HMAC secret, ready for API testing or authentication tokens

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 azerty123 is 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

  1. Enter the payload as valid JSON. It is an object, so it starts with { and ends with }.
  2. Provide the HMAC secret. Choose a long random string for production tokens.
  3. Pick the algorithm: HS256 by default, HS384 or HS512 depending on your needs.
  4. Click create. The signed JWT appears, ready to paste into an Authorization: Bearer ... header.
  5. 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 tool
  • GET https://cdrn.fr/api/v1/tools/jwt-builder - returns the schema for this tool
  • POST https://cdrn.fr/api/v1/tools/jwt-builder/execute - runs this tool with a JSON payload