Skip to main content
securityjwt

Decoding JWTs Safely in the Browser

Why client-side JWT inspection never leaks secrets, and how to read claims without a network round-trip.

Astound1 min read

The header and payload of a JSON Web Token are only Base64URL-encoded, not encrypted. That means you can inspect them entirely in the browser without ever sending the token to a server. The JWT decoder on this site runs 100% client-side — nothing is transmitted.

The three parts of a JWT

A JWT is three Base64URL strings joined by dots:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhYmMxMjMifQ.signature
  • Header — the algorithm and token type.
  • Payload — the claims (sub, exp, iat, custom ones).
  • Signature — the only part that proves integrity.

Only the signature is cryptographically meaningful, and verifying it requires the secret/public key. Decoding the first two segments reveals nothing an attacker couldn't read themselves.

Reading the expiry claim

The exp claim is a Unix timestamp in seconds. Compare it to Date.now() / 1000:

function isExpired(token: string): boolean {
  const [, payload] = token.split('.')
  const decoded = JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/')))
  return typeof decoded.exp === 'number' && decoded.exp * 1000 < Date.now()
}

Never trust claims for authorization on the client. Decode for display and debugging only — the server must always re-verify the signature.

What stays private

The signing secret, refresh tokens, and any server-side authorization logic never touch the browser. That is the trust boundary: the token's contents are public, its validity is not.

AstoundPart of the Astound Dev Tools writing on developer tools.