JWT Decoder & Validator→Specialized Version
🎟️

JWT Payload Decoder

Decode payload

Decoding happens in this tab. Nothing is sent to a server — but a JWT is a credential, so avoid pasting production tokens into any online tool.

✓Token is within its validity window

Expires: 3/17/2030, 5:46:40 PM (1294d 22h 29m from now)

Issued: 11/14/2023, 10:13:20 PM

PAYLOADData & claims

{
  "sub": "payload-demo",
  "name": "Grace Hopper",
  "roles": [
    "engineer",
    "admin"
  ],
  "tenant": "acme",
  "iat": 1700000000,
  "exp": 1900000000
}

HEADERAlgorithm & token type

{
  "alg": "HS256",
  "typ": "JWT"
}

SIGNATUREVerification hash

Mr3wZjPcYaHdSoUsE2iRgFxTlVnMyDbO9AcqL8XvNbKtMI

Claims

ClaimValueMeaning
subregisteredpayload-demoSubject — the user or entity it identifies
nameGrace HopperUser name
roles["engineer","admin"]User roles
tenantacmeCustom claim
iatregistered11/14/2023, 10:13:20 PMIssued at
expregistered3/17/2030, 5:46:40 PMExpiration time

JWT Payload Decoder

Decode the JWT payload to view all claims and data stored in the token. The payload contains the actual information the token conveys.

JWT Payload Structure

The payload is the second part of the JWT (between the dots) and contains claims:

``json { "sub": "1234567890", "name": "John Doe", "email": "john@example.com", "role": "admin", "iat": 1516239022, "exp": 1516242622 } `

Claim Types

CategoryClaimsDescription
Registerediss, sub, aud, exp, nbf, iat, jtiStandard claims with defined meanings
Publicname, email, pictureCommonly used claims (IANA registry)
Privaterole, permissions, tenant_idApplication-specific claims

Payload Decoder Implementation

`javascript function decodeJWTPayload(token) { const parts = token.split('.'); if (parts.length !== 3) { throw new Error('Invalid JWT format'); }

const payloadPart = parts[1];

// Base64URL decode const base64 = payloadPart.replace(/-/g, '+').replace(/_/g, '/'); const decoded = atob(base64); const payload = JSON.parse(decoded);

// Analyze claims const analysis = { raw: payload, registeredClaims: {}, customClaims: {}, timestamps: {} };

const registered = ['iss', 'sub', 'aud', 'exp', 'nbf', 'iat', 'jti'];

for (const [key, value] of Object.entries(payload)) { if (registered.includes(key)) { analysis.registeredClaims[key] = value; // Convert timestamps to readable dates if (['exp', 'nbf', 'iat'].includes(key)) { analysis.timestamps[key] = new Date(value * 1000).toISOString(); } } else { analysis.customClaims[key] = value; } }

return analysis; } `

Registered Claims Reference

ClaimNamePurpose
issIssuerIdentifies token creator
subSubjectIdentifies the user/entity
audAudienceIntended recipients
expExpirationWhen token becomes invalid
nbfNot BeforeWhen token becomes valid
iatIssued AtWhen token was created
jtiJWT IDUnique identifier for token

Best Practices

  • Keep payload small (affects token size)
  • Never store sensitive data (passwords, secrets)
  • Use registered claims when appropriate
  • Include only necessary information

The Attacks a Verifier Must Stop

alg: none. A token declaring no algorithm with an empty signature. A library that trusts the header's alg will accept it as valid. Always specify the expected algorithm when verifying rather than reading it from the token.

Algorithm confusion. A token signed with HMAC using the server's *public* RSA key as the secret. If the verifier picks the algorithm from the header, an RS256 verifier can be tricked into running HS256 with a key the attacker already has.

Weak secrets. HS256 with a short or dictionary secret is brute-forceable offline from a single captured token. Use at least 256 bits of real entropy.

No expiry check. exp is a claim, not an enforcement. A library that decodes without verifying, or code that reads claims from a decoded-but-unverified token, accepts expired and forged tokens alike.

`javascript // Specify the algorithm; never trust the header's jwt.verify(token, secret, { algorithms: ['HS256'] }); `

Anyone Can Read the Payload

A JWT is signed, not encrypted. The payload is base64url — readable by anyone holding the token, including the browser it is stored in. Never put anything in it you would not print on a postcard: no passwords, no PII beyond an identifier, no internal keys.

Revocation Is the Hard Part

A signed token is valid until it expires, and there is no way to withdraw one. Options:

ApproachCost
Short expiry (5–15 min) plus refresh tokensStandard; adds a refresh endpoint
A denylist of revoked JTIsReintroduces the state JWTs were meant to avoid
Rotate the signing keyRevokes every token at once
Version claim checked against the user recordA database read per request
If you need immediate revocation for every session, a server-side session is a simpler and more honest choice than a JWT.

Where to Store One

LocationXSS-safeCSRF-safe
localStorageNoYes
sessionStorage`NoYes
Cookie (HttpOnly, Secure, SameSite)YesYes, with SameSite
In-memoryYesYes
An HttpOnly cookie is the safest default. Any token reachable from JavaScript is reachable by any script that gets injected.

Frequently Asked Questions

What should I store in a JWT payload?

Store only what's needed for authentication/authorization: user ID (sub), roles/permissions, email, name. Don't store passwords, sensitive data, or large objects. Remember: payload is encoded, not encrypted—anyone with the token can read it. Keep it minimal to reduce token size.

Is the JWT payload encrypted?

No, standard JWTs (JWS) are signed but not encrypted. The payload is Base64URL-encoded—anyone can decode and read it. For encrypted payloads, use JWE (JSON Web Encryption). Even with JWE, don't store highly sensitive data in tokens.

What is the difference between sub and user_id?

sub (subject) is a registered claim with a standard meaning—the principal/user the token represents. user_id is a custom claim with no standard meaning. Use sub when possible as it's understood by JWT libraries and tools. The value should uniquely identify the user.

Related Tools

Explore other tools you might find useful:

More JWT Decoder & Validator tools

You might also need