How to decode a JWT without pasting it into a website

A JSON Web Token is a bearer credential: whoever holds it can act as you until it expires. Pasting one into a web decoder hands that credential to a third party.

Why the usual advice is wrong

Every guide to reading a JWT says the same thing: paste it into a decoder site. The token is only base64, the reasoning goes, so nothing secret is being revealed.

That reasoning confuses encoding with protection. The payload is indeed only base64url — but the token as a whole is the credential. A JWT from a staging or production system will typically authenticate as a real user for as long as its expiry allows, which is often an hour and sometimes very much longer. Anyone who receives it can replay it.

The site may be honest, and most are. The risk is not that a decoder is malicious; it is that you have copied a working key into a system you do not control, whose logs you cannot read, whose retention you do not set, and whose breach you would never hear about.

What a JWT is actually made of

Three base64url segments joined by dots: header, payload, signature. The first two are readable by anyone. The third proves the first two were not altered, and verifying it requires the signing key — which is why decoding and verifying are different operations.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
  .eyJzdWIiOiIxMjM0NTY3ODkwIiwiZXhwIjoxNzAwMDAwMDAwfQ
  .SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

header.payload.signature — split on the dots, and the first two segments decode to JSON.

The claims worth reading

  • exp — expiry, as a Unix timestamp. The single most common reason to open a token at all.
  • iat — issued at. Together with exp it tells you the token's intended lifetime.
  • nbf — not before. A token can be valid-looking and still be rejected because this is in the future.
  • sub — the subject, usually the user id the token authenticates as.
  • aud — the audience. A token minted for one service is often rejected by another purely because of this.
  • iss — the issuer. Useful when several identity providers are in play.
  • alg — in the header, not the payload. If it reads "none", the token is unsigned and any system that accepts it has a serious problem.

Reading expiry without doing arithmetic

The reason a token is opened is nearly always to answer one question: has it expired? But exp is stored as seconds since 1970, so a decoder that prints 1700000000 has handed the question back to you.

Bellows converts the timestamps and states the answer — issued at, not before, expires, and whether that is in the past, expressed in units you can read rather than a raw number of days.

Decoding is not verifying

Bellows decodes and inspects; it does not check the signature. Nothing that runs entirely on your machine can, unless you give it the signing key, and a tool that quietly accepted your key would defeat the purpose of using it.

This matters when reading the output: a decoded token tells you what it claims, not that the claims are true. Signature verification belongs in the service that accepts the token.

How to check the claim rather than trust it

Bellows is signed without a network entitlement. That is not a policy someone wrote down — it is a permission the binary does not have, and macOS enforces it whether the app wants to connect or not. You can read the entitlements it was signed with:

codesign -d --entitlements - /Applications/Bellows.app

Neither com.apple.security.network.client nor com.apple.security.network.server appears in the output. Without them the sandbox refuses to open a socket.

JWT Decoder is one of 41 tools in Bellows

A native Mac app that ships with no network entitlement, so macOS blocks it from connecting at all. One-time purchase, every future update included.

What Bellows is →

Questions

Is it safe to decode a JWT online?

The decoding itself is harmless, but sending the token is not. A JWT is a bearer credential — anyone holding it can use it until it expires. If the token is from a live system, treat pasting it into any website the same way you would treat pasting a password.

Can a JWT be decoded without the secret key?

Yes. The header and payload are base64url-encoded, not encrypted, so anyone can read them. The secret is needed only to verify the signature or to mint a new token.

How do I tell if a JWT has expired?

Read the exp claim, which is a Unix timestamp in seconds. If it is earlier than now, the token has expired. Bellows converts it to a date and says how long ago that was.

What does alg: none mean in a JWT header?

It means the token is unsigned. It is a legitimate part of the specification but a well-known attack vector, because a server that accepts alg: none will trust a token anyone can forge. Seeing it on a real token is worth investigating.

Related