Skip to content
Dev Tools

How to Decode a JWT Safely: Decoding Is Not Verifying

By · Published

A JSON Web Token (JWT) is the long string that starts with eyJ in your Authorization header, cookie or local storage. When a login breaks or an API keeps returning 401, the first thing to do is look inside it. That part is easy. Doing it without leaking the token, and without trusting what you read before checking the signature, is where people slip.

What a JWT is made of

A JWT has three parts separated by dots:

header.payload.signature
  • The header says how the token was signed, for example {"alg":"HS256","typ":"JWT"}.
  • The payload holds the claims: who the user is, when the token expires, which roles it has.
  • The signature proves that the header and payload were not changed since the server signed them.

The header and payload are not encrypted. They are only Base64URL encoded, which is a URL-safe version of Base64 that uses - and _ instead of + and / and drops the = padding. Anyone holding the token can read them. So never put passwords, card numbers or other secrets in a JWT payload.

Decoding is not verifying

Decoding turns the first two parts back into JSON so you can read them. It tells you what the token claims. It does not tell you whether those claims are true. Anybody can write a payload that says "role":"admin" and encode it.

Verifying recomputes the signature with the right key and compares it with the third part. Only a verified token can be trusted. Your backend must always verify, with a maintained library, and must fix the allowed algorithm in its own config instead of taking it from the token header.

Where you paste a token matters

A valid access token is a password with an expiry date. Whoever has it can call your API as that user until it expires. Pasting it into a website that sends it to a server puts it in someone else's logs.

The JWT decoder on Toolsy Hub runs entirely in your browser. The token, secret and public key never leave your device. Even so, the safest habit is to debug with tokens from a development or staging environment, or with tokens that have already expired.

How to decode a token, step by step

  1. Copy the token. In browser dev tools, look in the Network tab under the request's Authorization header.
  2. Paste it into the JWT decoder. A leading Bearer does not need to be removed by hand.
  3. Read the header to see the algorithm, and the payload to see the claims.
  4. Check the time claims. The decoder turns exp, nbf and iat into real dates and tells you whether the token has expired or is not valid yet.

The claims you will see most often

Registered claims from RFC 7519. Your provider may add its own, such as roles or email.
ClaimNameWhat it means
issIssuerWho created the token, often your auth server's URL.
subSubjectWho the token is about, usually a user ID.
audAudienceWhich API or app should accept it. A token meant for one service should be rejected by another.
expExpiresUnix time in seconds after which the token must be rejected.
nbfNot beforeUnix time in seconds before which the token is not valid yet.
iatIssued atUnix time in seconds when the token was created.
jtiToken IDA unique ID, used to block a single token or stop it being replayed.

How to verify the signature

The key you need depends on the algorithm in the header:

  • HS256, HS384, HS512 use one shared secret. The same secret signs and verifies, so it must stay on the server.
  • RS, PS and ES algorithms use a key pair. The server signs with a private key, and anyone can verify with the public key. Paste the public key in PEM format, the block that starts with -----BEGIN PUBLIC KEY-----.

The decoder checks the signature as soon as you enter the key and tells you whether it is valid. It supports the HS, RS, PS and ES families with 256, 384 and 512-bit hashes.

Why your token is rejected: common causes

  • It expired. Access tokens often live for 5 to 60 minutes. Look at exp.
  • Clock skew. If the server's clock is a minute behind the one that issued the token, a fresh token can look "not valid yet". Most libraries allow a small leeway for this.
  • Wrong audience. A token issued for one API is sent to another, and aud does not match.
  • Wrong key. Staging and production often use different secrets or key pairs. If an identity provider rotated its keys, your service may still be holding the old public key.
  • A copy-paste error. A missing character or an extra space breaks the signature. The token should have exactly two dots.

Decoding from the command line

If you prefer the terminal, this prints the payload of a token stored in a variable:

echo "$TOKEN" | cut -d. -f2 | tr '_-' '/+' | base64 -d 2>/dev/null; echo

Because Base64URL drops padding, base64 -d may complain or cut off the last characters. That is fine for a quick look, but it does not verify anything. For anything more than a glance, use a proper library or the decoder, which also checks the signature.