Your server returns a 401 Unauthorized. Did the token expire, or is the scope wrong? The answer is written inside the token, but it looks like a long, meaningless string, so it is tempting to just log in again and move on. Here is how to open that string and read it.
A JWT is encoding, not encryption
A JSON Web Token is three parts joined by dots: header.payload.signature, each one base64url-encoded.
This is where a common misconception starts. base64url is encoding, not encryption, so anyone can reverse it and read the contents without a key. The user ID and roles inside the payload are visible to anyone holding the token. That is exactly why you must never put secrets like passwords in the payload.
Reading the claims inside the payload
Each field inside the payload is called a claim. RFC 7519 defines a set of registered claims, so once you know the names, the meaning reads straight off.
| Claim | Meaning |
|---|---|
iss | Issuer |
sub | Subject (usually the user ID) |
aud | Audience |
exp | Expiration time |
iat | Issued at |
nbf | Not valid before |
jti | Unique token ID |
The three time claims, exp, iat, and nbf, are the ones you reach for most often when debugging.
Checking expiry: exp is a Unix epoch
exp is the expiry, but its value looks like 1700003600. It was never meant to be read by a human: it is a Unix epoch, the number of seconds since 1970.
So a tool does the conversion for you. Drop a token into the PiPi Worlds JWT decoder and the time claims expand into ISO timestamps next to the payload. For example, iat: 1700000000 becomes 2023-11-14T22:13:20Z and exp: 1700003600 becomes 2023-11-14T23:13:20Z, and the gap between them tells you this token lives for one hour. When the expiry is in the past, an expired badge appears. After a 401, that single badge settles whether you simply need to refresh.
Decoding is not verifying
This is the distinction that matters most. Reading a token’s contents and confirming the token is genuine are two different things.
The third part, the signature, is a value the issuer created with a signing key. Confirming it has not been tampered with means recomputing the signature with the same secret or public key and comparing (RFC 7515). A decoder never receives or stores a key, so it does not verify anything. Treat the payload you can see as untrusted until your server checks the signature. Even if the payload says role: admin, believing that without verification is how privilege bugs happen.
Where are you pasting that production token?
This is where security incidents start. To debug a 401, people paste a production access token into whatever online decoder comes up first. The token is itself the credential, so any site that sends your input to its server has just handed that credential to a third party.
The PiPi Worlds JWT decoder decodes entirely in your browser. The token is never transmitted, logged, or stored, so production tokens are safe to inspect. Since the payload is JSON, a JSON formatter helps when you want to expand the structure further, and the Base64 tool is there if the base64url encoding underneath is what you want to explore.