socket.help
← Blog

How to decode a JWT safely, and what a decoder cannot tell you

Decode a JWT locally to read its header and claims, then verify it in your own code. Decoding shows what a token says; only verification proves who issued it.

A JWT is encoded, not encrypted

A signed JSON Web Token has three parts separated by dots: a header, a set of claims, and a signature. The first two are JSON objects encoded with base64url. Anyone holding the token can read them. Encoding makes the token safe to put in a header or URL; it does not hide anything.

That is why decoding is useful when you debug a login flow. You can see which algorithm the header names, which user the subject claim identifies, which audience the token was issued for, and when it expires. It is also why a JWT should never carry secrets in its claims. Encrypted tokens use a different five-part format called JWE, and a JWT decoder cannot read them.

Decode where the token does not leave your device

A JWT is often a working credential. If it has not expired, whoever holds it can usually call the API it was issued for. Pasting a production token into a website that sends it to a server hands that credential to someone else. The same applies to screenshots, bug reports, and chat messages: a token visible in any of them can be copied and used until it expires.

The socket.help JWT inspector decodes in your browser. The token stays on your device and is not uploaded. Even so, prefer test tokens from a development identity provider, and treat a real token you have pasted anywhere as something to let expire or revoke.

  • Open the JWT inspector and paste a token with three dot-separated parts.
  • Choose Inspect token.
  • Read the decoded header and claims, and note that verified is always false.

Read the claims that usually explain a failure

Most authentication bugs show up in a handful of registered claims from RFC 7519. exp is the expiry time and nbf is the time before which the token must not be accepted; both are Unix timestamps in seconds, not milliseconds. iss names the issuer and aud names the intended recipient. A token issued for one API and sent to another often fails on aud alone.

The inspector shows these values as they appear in the token. It does not convert timestamps into dates or compare them with the current time. Use the Unix timestamp converter to turn exp into a readable UTC date, and remember that clocks on different servers can disagree by a few seconds.

The header is worth a look as well. alg tells you which algorithm the issuer claims to have used, and kid, when present, names the key that signed the token. If your API rejects every token after a key rotation, a kid that does not appear in the key set your service has cached is a likely cause.

Inspector output for a test token
{
  "verified": false,
  "header": {
    "alg": "HS256",
    "typ": "JWT"
  },
  "claims": {
    "sub": "user_test_1",
    "iss": "https://auth.example.test",
    "aud": "orders-api",
    "iat": 1790000000,
    "exp": 1790003600
  }
}

What a decoder cannot tell you

A decoder cannot tell you whether the token is genuine. Anyone can write a header and claims, encode them, and append any signature. The decoded output looks exactly the same as the output of a real token. Only checking the signature with the issuer’s key shows that the issuer created it and nobody changed it afterwards.

A decoder also does not check expiry, issuer, audience, or whether the token has been revoked. The socket.help inspector marks every result as verified: false to make this explicit. It even accepts a token with an empty signature, the form RFC 7519 calls an Unsecured JWT, because that is still something you might need to read while debugging. Your application must never accept one.

Verify in your application, with an allow-list

RFC 8725 collects the mistakes that have caused real vulnerabilities. Do not let the token’s own alg header choose the algorithm; configure the algorithms you accept and reject everything else, including none. Check iss against the issuer you trust, check that aud contains your service, and check exp and nbf with a small allowance for clock skew.

Use a maintained library rather than writing the checks yourself. The example below uses the jose package for Node.js and fetches the issuer’s public keys from its JWKS endpoint. The URL, issuer, and audience are placeholders for your own identity provider.

Verify with an explicit algorithm, issuer, and audience
import { createRemoteJWKSet, jwtVerify } from 'jose';

const keys = createRemoteJWKSet(new URL('https://auth.example.test/.well-known/jwks.json'));

const { payload } = await jwtVerify(token, keys, {
  algorithms: ['RS256'],
  issuer: 'https://auth.example.test',
  audience: 'orders-api',
});

Decoding from a script

The inspector is also available as the tool ID developer-jwt-inspector through the HTTP API, CLI, SDK, SSH, and MCP. A hosted run sends the token to the server, so reserve it for test tokens. Inputs are not kept as execution history, but the browser page is the better choice for anything that still works.

If the inspector reports that the input is not a JWT with three base64url segments, check for the usual copy problems: a leading Bearer prefix, surrounding quotes, a line break in the middle, or a five-part encrypted token. If it reports that the header or claims are not JSON objects, the value is probably an opaque access token. Some identity providers issue those instead of JWTs, and only the provider can tell you what they mean.

Inspect a test token over HTTP
curl https://socket.help/api/tools/developer-jwt-inspector/run \
  -H 'Content-Type: application/json' \
  -d '{"input":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyX3Rlc3RfMSIsImlzcyI6Imh0dHBzOi8vYXV0aC5leGFtcGxlLnRlc3QiLCJhdWQiOiJvcmRlcnMtYXBpIiwiaWF0IjoxNzkwMDAwMDAwLCJleHAiOjE3OTAwMDM2MDB9.c2lnbmF0dXJl"}'

Questions people ask

Is it safe to paste a JWT into an online decoder?

Only if the decoder runs locally. A live token is a credential. The socket.help inspector decodes in your browser, but test tokens are still the safer habit.

Does decoding a JWT verify it?

No. Decoding only reads the header and claims. Verification checks the signature with the issuer’s key and validates exp, nbf, iss, and aud.

Can the inspector read encrypted tokens?

No. It reads signed tokens with three parts. Encrypted JWE tokens have five parts and need the recipient’s key to decrypt.

Why is exp a large number?

JWT times are Unix timestamps in seconds. Convert the value to a UTC date with a timestamp converter.

Sources