How to Decode and Inspect JWT Tokens
If you've worked with APIs, OAuth, or any modern authentication system, you've likely encountered JWT tokens. They show up as long, opaque strings in Authorization headers, cookies, and URL parameters. But they're not actually opaque — they're designed to be readable.
This guide shows you how to crack one open and understand what's inside.
What Is a JWT?
A JSON Web Token (JWT), pronounced "jot", is a compact, URL-safe way to represent claims between two parties. It's commonly used for:
- Authentication: After login, the server issues a JWT. The client sends it with every request to prove identity.
- Authorization: The token contains the user's roles and permissions, so the server can make access decisions without hitting a database.
- Information exchange: JWTs can carry any JSON payload between services, signed to guarantee integrity.
The Three Parts
Every JWT has three parts separated by dots:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgRG9lIiwiaWF0IjoxNzA5MDAwMDAwLCJleHAiOjE3MDkwODY0MDB9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Split on the dots, these are:
The header and payload are Base64URL-encoded JSON. They're not encrypted — anyone can decode them. The signature proves the token hasn't been tampered with.
Decoding the Header
The header tells you the token type and signing algorithm:
{
"alg": "HS256",
"typ": "JWT"
}
Common algorithms include:
| Algorithm | Type | Description |
|---|---|---|
HS256 | Symmetric | HMAC with SHA-256. Uses a shared secret. |
RS256 | Asymmetric | RSA with SHA-256. Uses public/private key pair. |
ES256 | Asymmetric | ECDSA with P-256. Smaller keys, same security. |
Decoding the Payload
The payload contains the claims — the actual data the token carries:
{
"sub": "1234567890",
"name": "Jane Doe",
"iat": 1709000000,
"exp": 1709086400
}
Registered Claims
These are standard, reserved claim names defined in the JWT spec:
| Claim | Full Name | Description |
|---|---|---|
iss | Issuer | Who created the token (e.g., your auth server) |
sub | Subject | Who the token is about (e.g., user ID) |
aud | Audience | Intended recipient (e.g., your API) |
exp | Expiration | Unix timestamp when the token expires |
iat | Issued At | Unix timestamp when the token was created |
nbf | Not Before | Token is invalid before this time |
jti | JWT ID | Unique identifier for the token |
Tip: The exp and iat claims are Unix timestamps (seconds since January 1, 1970). To check if a token is expired, compare exp against the current time. BoltKit's EpochTime tool can help you convert these quickly.
Custom Claims
Beyond the registered claims, you can include any custom data:
{
"sub": "user_42",
"role": "admin",
"permissions": ["read", "write", "delete"],
"org_id": "acme-corp",
"exp": 1709086400
}
Custom claims are where JWTs get useful — embedding roles, permissions, and context directly in the token so your API doesn't need a database lookup on every request.
Decoding from the Command Line
You can decode a JWT with standard command-line tools. The payload is the second segment:
# Extract and decode the payload (macOS / Linux)
echo 'eyJzdWIiOiIxMjM0NTY3ODkwIn0' | base64 -d 2>/dev/null
# Full pipeline: extract payload, decode, pretty-print
echo 'YOUR.JWT.TOKEN' | cut -d'.' -f2 | base64 -d 2>/dev/null | jq .
Note: JWTs use Base64URL encoding (with - and _ instead of + and /, and no padding). Some base64 commands need the input padded or translated first. A dedicated tool handles this automatically.
The Signature
The third part is the signature, created by signing the header and payload with a secret or private key:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
The signature guarantees that:
- The token was issued by someone who knows the secret
- The header and payload haven't been modified
You cannot verify the signature without the secret key (for HMAC) or the public key (for RSA/ECDSA). Decoding only reveals the claims — it doesn't prove they're trustworthy.
Security Considerations
JWTs are not encrypted by default. Anyone who intercepts a JWT can read its contents. Never put sensitive data (passwords, credit card numbers, PII) in the payload. JWTs provide integrity (tamper-proof), not confidentiality (secret).
- Always verify the signature before trusting a token's claims. Decoding is not verification.
- Check the
expclaim. Reject expired tokens. A common mistake is only decoding without checking expiry. - Validate the
issandaudclaims. Make sure the token was issued by your auth server and intended for your service. - Reject
alg: "none". Some JWT libraries accept unsigned tokens if the algorithm is set tonone. Always reject this. - Keep tokens short-lived. Access tokens should expire in minutes to hours, not days. Use refresh tokens for longer sessions.
- Use HTTPS only. JWTs in HTTP headers can be intercepted on unencrypted connections.
When to Use JWTs
JWTs work well for:
- Stateless API authentication — no server-side session storage needed
- Single sign-on (SSO) — one token works across multiple services
- Service-to-service communication — short-lived tokens between microservices
JWTs are a poor fit when you need immediate revocation (like banning a user). Since JWTs are self-contained and stateless, the server can't invalidate one without maintaining a blocklist — which defeats the purpose. For those cases, consider opaque tokens with server-side sessions.
Inspect JWTs Instantly
BoltKit's JWTInspect tool decodes any JWT token on your device — showing the header, payload, claims, algorithm, and expiry status at a glance. No data ever leaves your phone.
Get BoltKit Free