flashman
← All posts

JWT generator algorithm overwrite debugging

Debug a signed JWT whose alg is not the header you typed: the generator writes the selected button's algorithm, and decode never checks the signature.

2026-10-21 · 6 min read · Rahul Chitturi

  • jwt
  • auth
  • debugging

The JWT generator signs whatever JSON objects are in the header and payload boxes, then replaces the header alg with the algorithm selected above the form. HS256 and RS256 are the only choices. A header you typed as alg none, or alg RS256 while the HS256 button is active, is signed as the button's algorithm.

The decoder on the same page does not verify that signature. It base64url-decodes the first two segments and pretty-prints them when they are JSON.

See where alg is written

Generation parses the header, requires a non-array object, and stringifies the object with alg set to the button. If the key was missing, it is appended: a header of typ and kid becomes typ, kid, then alg HS256. If alg was already present, the value is replaced and the key stays where it was. {"typ":"JWT","alg":"none","extra":1} with the HS256 button becomes typ, alg HS256, extra.

Switching the button also rewrites the header box. The selected alg is written, typ is set to JWT, and the box is pretty-printed at two spaces. A header that is not valid JSON is replaced by a fresh object that contains only that alg and typ. Duplicate keys follow JSON.parse, so the last alg wins, and the button then overwrites it.

  • Read the generated header, not the draft you typed.
  • Expect alg to match the active button.
  • Expect an invalid header box to be replaced on a button click.
  • Keep payload claims in a JSON object.

Reject the values the signer will not accept

An array, null, or a JSON string in either box throws Header and payload must be JSON objects. An empty HS256 secret throws Enter a shared secret for HS256 before any key is imported. The secret is the UTF-8 bytes of that string. It is not decoded as base64.

RS256 imports a PKCS#8 PEM. The label it strips is PRIVATE KEY. A file that says BEGIN RSA PRIVATE KEY is left intact, and signing then fails with Invalid character from the base64 decoder. An empty private-key box throws Paste a PKCS#8 private key PEM first.

Decode without treating it as verification

A blank token clears both panels and shows no error. One segment shows Not a JWT (expected header.payload.signature). Two segments are enough. A fourth segment is ignored. JSON is reprinted at two spaces with a trailing newline. A segment that is not JSON, such as the text hello, is shown unchanged. Bad base64 shows Invalid character.

Use the JWT tool to compare the typed header with the decoded header, the JSON formatter for the claims, the Base64 tool only after you convert URL-safe characters, and the PEM newline tool when a PKCS#8 key was stored as a one-line environment value. Verification still belongs in the API.

Try these tools