ByteTools Logo

JWT Decoding Guide: How to Decode and Verify JSON Web Tokens

Master JWT decoding and verification for secure authentication. Learn token structure, claims validation, signature verification, and security best practices.

🔐 What is JWT?

JWT (JSON Web Token) is an open standard (RFC 7519) for securely transmitting information between parties as a JSON object. JWTs are commonly used for authentication and information exchange in web applications.

Why Use JWT?

✅ Benefits

  • Stateless - No server-side session storage
  • Portable - Can be used across different domains
  • Compact - Small size for HTTP headers
  • Self-contained - Contains all necessary information

⚠️ Considerations

  • Size - Larger than session IDs
  • Cannot revoke - Valid until expiration
  • Sensitive data - Payload is readable
  • Key management - Requires secure secret handling

🏗️ JWT Structure

A JWT consists of three Base64-encoded parts separated by dots:

header.payload.signature
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

📋 Header

Contains metadata about the token:

{
"alg": "HS256",
"typ": "JWT"
}
  • alg: Signing algorithm
  • typ: Token type

📦 Payload

Contains claims (statements about the user):

{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516242622
}
  • sub: Subject (user ID)
  • iat: Issued at time
  • exp: Expiration time

✍️ Signature

Verifies token integrity:

HMACSHA256(
base64UrlEncode(header) +
"." +
base64UrlEncode(payload),
secret
)
  • • Prevents tampering
  • • Requires secret key
  • • Verifies authenticity

🔍 JWT Decoding Process

Decoding a JWT involves extracting and parsing each part:

Step-by-Step Decoding

1

Split the Token

Separate the token at the dots (.) to get three parts

const [header, payload, signature] = token.split('.');
2

Base64 Decode

Decode each part from Base64URL encoding

const decodedHeader = JSON.parse(atob(header));
const decodedPayload = JSON.parse(atob(payload));
3

Parse JSON

Convert the decoded strings to JSON objects

// Now you have readable header and payload objects
⚠️

Important Note

Decoding only reveals content - it doesn't verify authenticity. Always validate the signature on the server!

📋 Understanding JWT Claims

Claims are statements about an entity (typically the user) and additional data:

🎯 Standard Claims

"iss" (Issuer)
Who issued the token
Example: "https://auth.example.com"
"sub" (Subject)
Who the token is about
Example: "user123" or "john@example.com"
"aud" (Audience)
Who should accept the token
Example: "api.example.com"
"exp" (Expiration)
When the token expires
Unix timestamp: 1704067200
"iat" (Issued At)
When the token was issued
Unix timestamp: 1704063600

🛠️ Custom Claims

"name"
User's full name
Example: "John Doe"
"email"
User's email address
Example: "john@example.com"
"roles"
User's permissions/roles
Example: ["admin", "user"]
"permissions"
Specific permissions
Example: ["read:posts", "write:posts"]
"tenant_id"
Multi-tenant identifier
Example: "company-abc"

⚠️ Security Reminder

Never include sensitive information like passwords, social security numbers, or private keys in JWT payload. The payload is only Base64-encoded, not encrypted!

🐛 Common Debugging Scenarios

🚨 "Token Expired" Errors

When users get authentication failures, check the token expiration:

// Check exp claim
const now = Math.floor(Date.now() / 1000);
const isExpired = payload.exp < now;

Debug Steps:

  • 1. Decode the JWT to see the exp claim
  • 2. Convert Unix timestamp to readable date
  • 3. Compare with current time
  • 4. Check token refresh logic

🔍 Permission Debugging

When users can't access resources, check roles and permissions:

// Check roles in payload
const userRoles = payload.roles || [];
const hasAdminRole = userRoles.includes('admin');

Debug Steps:

  • 1. Decode JWT and examine roles claim
  • 2. Check permissions array if present
  • 3. Verify role-to-permission mapping
  • 4. Test with different user accounts

🔒 Invalid Signature

When signature verification fails:

// Common causes:
- Wrong secret key
- Token was modified
- Different algorithm used

Debug Steps:

  • 1. Verify the signing algorithm in header
  • 2. Check if secret key matches
  • 3. Ensure token wasn't tampered with
  • 4. Validate key rotation timing

❌ Malformed Token

When token structure is invalid:

// Valid JWT structure:
header.payload.signature
// Must have exactly 3 parts

Debug Steps:

  • 1. Count dots (.) - should be exactly 2
  • 2. Check for truncated tokens
  • 3. Verify Base64 encoding
  • 4. Test with a known good token

🛡️ Security Best Practices

✅ Security Do's

  • Use HTTPS - Always transmit JWTs over encrypted connections
  • Short expiration times - Use 15-60 minutes for access tokens
  • Strong secrets - Use cryptographically secure random keys
  • Validate all claims - Check iss, aud, exp on every request
  • Use refresh tokens - Implement proper token refresh flow
  • Store securely - Use secure, httpOnly cookies when possible

❌ Security Don'ts

  • Don't store sensitive data - Payload is readable by anyone
  • Don't use weak secrets - Avoid predictable or short keys
  • Don't skip signature verification - Always validate server-side
  • Don't use "none" algorithm - Disabled unsigned tokens
  • Don't store in localStorage - Vulnerable to XSS attacks
  • Don't ignore expiration - Always check exp claim

🔐 Algorithm Security

✅ Recommended

  • HS256 - HMAC with SHA-256
  • RS256 - RSA with SHA-256
  • ES256 - ECDSA with SHA-256

⚠️ Deprecated

  • HS1 - Weak hash function
  • RS1 - Vulnerable to attacks
  • • Custom algorithms

❌ Never Use

  • none - No signature
  • HS0 - No security
  • • MD5-based algorithms

🔧 Common Issues & Solutions

🕐 Clock Skew Issues

Problem:

Server and client clocks are out of sync, causing premature token expiration.

Solution:

  • • Add clock skew tolerance (usually 5 minutes)
  • • Use NTP to sync server clocks
  • • Implement proper time validation

🔄 Token Refresh Problems

Problem:

Users get logged out frequently due to short token lifetimes.

Solution:

  • • Implement refresh token flow
  • • Auto-refresh before expiration
  • • Use sliding expiration windows

📱 Mobile App Considerations

Challenges:

  • • App backgrounding affects timers
  • • Network connectivity issues
  • • Secure storage limitations

Best Practices:

  • • Use device keychain/keystore
  • • Implement offline token validation
  • • Handle app lifecycle events

🛠️ Testing Your JWT Implementation

Test Cases:

  • • Valid token acceptance
  • • Expired token rejection
  • • Invalid signature detection
  • • Malformed token handling

Security Tests:

  • • Algorithm confusion attacks
  • • None algorithm bypass
  • • Key confusion attacks
  • • Token replay attacks

Tools:

Ready to Decode Your JWT Tokens?

Use our secure JWT decoder with claim validation, signature verification, and detailed token analysis.

🚀 Start Decoding JWT Tokens Now