Skip to content
Dev Utilities

JWT generator

Build and sign a JWT with an HMAC secret

HMAC only, on purpose. RSA and EC algorithms sign with a private key, and pasting a private key into a web page is a habit worth not having — so this offers HS256, HS384 and HS512 and nothing else. Signing happens in this tab with WebCrypto: no request is made, the secret is never stored, and nothing here ends up in the URL. Treat what comes out as a test fixture, not a credential you have issued: a token minted from a secret you typed into a browser tab has no business authorising anything real.

Header

Read the secret as

An HMAC secret is bytes, and the same characters mean different bytes under the two readings — which is the usual reason a token signed in one place fails to verify in another. Currently 19 bytes.

RFC 7518 §3.2 asks for a key of at least 32 bytes for HS256 — the hash's own output size. WebCrypto will happily sign with fewer, and the result is a token whose security is whatever a short secret is worth. Fine for a fixture; not fine anywhere else.

Registered claims

Time claims

These are seconds since the Unix epoch (RFC 7519 §2, NumericDate), taken from — the moment this page loaded, or the last restamp. Held still on purpose: a token whose iat moved as you typed would be a different token every time you copied it.

Merged under the claims above, so anything set in a box here wins over the same name typed in the JSON.

Header, as signed

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload, as signed

{
  "name": "Ada Lovelace",
  "scope": "read:tools",
  "iss": "https://idp.example.com",
  "sub": "user-123",
  "aud": "dev-utilities"
}

Signed token

Nothing signed yet.

Signing input — the bytes the MAC is over

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiQWRhIExvdmVsYWNlIiwic2NvcGUiOiJyZWFkOnRvb2xzIiwiaXNzIjoiaHR0cHM6Ly9pZHAuZXhhbXBsZS5jb20iLCJzdWIiOiJ1c2VyLTEyMyIsImF1ZCI6ImRldi11dGlsaXRpZXMifQ

RFC 7515 §5.1: the MAC is taken over the ASCII of BASE64URL(header) + "." + BASE64URL(payload) — these exact bytes, not the JSON above. Re-serialising the JSON with different whitespace or key order produces different bytes and a signature that will not verify, which is why a verifier must check the segments it was given rather than ones it rebuilt.

Checking it, and what it is not

Paste the token and the same secret into the JWT decoder to see it verify — the two tools are a matched pair, and that round trip is the quickest way to tell a signing problem from a verifier's configuration. If you generated the secret with the button above, keep the “Base64” reading in mind: the decoder tries the secret both as text and Base64-decoded, and says which one matched.

A signature proves who signed a token and that it has not been altered. It says nothing about whether a token should be accepted — that is iss, aud, exp and the verifier's own policy, and a token minted here satisfies none of them by accident.

Not implemented, deliberately: alg: none (an unsigned token is a forgery waiting to happen, and there is no legitimate reason for a tool to make one easy), nested JWTs, and JWE encryption. Nor RSA or EC signing, for the reason at the top of this page.