Developer tools · JWT decoder
How to Decode a JWT Payload by Hand With base64 and jq
· How it works
jwt command-line developer-workflow
On a headless box you can still read a token with cut, tr, base64 and jq. This post gives the commands, explains the base64url conversion each one performs, and lists the pitfalls.
Reading a token over SSH — when a browser is not an option
A headless server may leave you with a token-shaped string and no browser UI. The required inspection work is still small: isolate one segment, translate base64url spelling, restore padding, decode bytes and parse JSON. The danger is operational rather than computational because shell history can preserve a live credential.
Use an expired or synthetic token whenever possible. If incident response requires examining a real value, follow your organization’s credential-handling controls, prevent it entering shared history or logs and rotate it after exposure. Command-line decoding remains inspection only; it supplies no verification key or trust policy.
Splitting on dots — cut or awk to isolate the payload segment
A compact JWS-shaped JWT has three dot-separated fields. The payload is the second. A shell can split it with a delimiter-aware tool, but quote variables so the shell does not expand characters or split whitespace. Remove a leading `Bearer ` label before selecting fields because that prefix belongs to HTTP syntax.
Count the fields rather than blindly taking field two. ToolAcre rejects anything other than three parts and separately identifies five-part encrypted input. A shell pipeline should apply the same structural caution; receiving a segment from malformed input can produce plausible JSON while concealing that the original token was truncated.
Converting the alphabet — tr to turn hyphen and underscore back into plus and slash
Standard command-line Base64 implementations commonly expect plus and slash where a JWT segment may contain hyphen and underscore. Translating `-` to `+` and `_` to `/` maps the URL-safe symbols back to their standard positions without changing the represented six-bit values.
Use a translation command whose option parsing cannot mistake a leading hyphen for a flag, and keep the data in a quoted variable or standard input. The alphabet conversion is reversible encoding work. It does not decrypt the claims, and success does not establish that the token came from the named issuer.
Restoring padding — the arithmetic that adds the right number of equals signs
After translation, compute the length modulo four. Remainder zero needs no equals signs, remainder two needs two, and remainder three needs one. Remainder one indicates truncation and should stop the pipeline. Appending arbitrary padding until a utility stops complaining can hide damage rather than diagnose it.
ToolAcre uses exactly this length rule in `base64ToBytes` and rejects the impossible remainder. Shell utilities vary in whether they accept omitted padding, so normalising the input first makes the pipeline explicit and portable in concept, though command flags can still differ between operating systems.
Decoding and pretty-printing — base64 -d piped into jq
Pipe the padded value to the platform’s Base64 decoder and then to `jq`. The first command recovers bytes; the second requires those bytes to form JSON. A successful Base64 command followed by a jq parse error means encoding was structurally decodable but its content was not a JSON payload.
That distinction mirrors ToolAcre’s error paths. It first reports invalid base64url or UTF-8, then separately reports invalid JSON, then rejects null, arrays and primitives because a JWT header or payload must be an object for this tool. Keeping stages separate makes a failure actionable.
Worked example — the full pipeline on a sample token, with the output at each stage
For a synthetic example, the payload segment `eyJzdWIiOiJkZW1vIiwicm9sZSI6InJlYWRlciJ9` needs no alphabet translation or padding. Decoding yields `{"sub":"demo","role":"reader"}`, and jq formats that object across lines. The visible role is merely a string supplied by the token.
Now alter the JSON, encode it again and attach any third segment. The pipeline still prints the changed object. This proves why a decode command cannot serve as a validity check: both legitimate and fabricated claims traverse the same public transformations unless a separate verifier checks the signature.
Pitfalls — shell history capturing the token, base64 implementations that reject missing padding, and tokens with a leading 'Bearer '
Common failures include retaining the HTTP prefix, selecting the wrong dot-separated field, losing trailing characters during copying and using a Base64 implementation that requires padding. Another pitfall is placing the entire token directly on the command line, where process listings or history may retain it.
Prefer standard input and ephemeral variables under appropriate controls, and never paste a production token into chat, tickets or shared terminals for convenience. Also remember that the third segment is binary signature material rather than JSON, so sending it through jq should fail and tells you nothing about signature validity.
Takeaway: the same decoding, any environment — when you do have a browser, the ToolAcre JWT decoder does this locally with nothing uploaded
The shell pipeline and ToolAcre perform the same decode-only sequence in different environments: split, normalise, pad, decode UTF-8 and parse JSON. Use whichever environment you can inspect and control, with non-sensitive data as the default.
Neither path verifies authenticity or authorizes a caller. After reading payload shape, move to the service’s trusted verifier and logs for cryptographic and policy decisions. A command that produces pretty JSON has completed a formatting task, not a security judgment.