JWT + Timestamp API Debugging Workflow
Decode JWT tokens, convert Unix timestamps to human-readable dates, and debug API authentication issues. Essential workflow for troubleshooting auth failures with code examples.
The Problem
Your API returns 401 Unauthorized and you need to figure out why. The JWT token looks valid but something is wrong — maybe it is expired, has incorrect claims, or the payload is malformed. Debugging requires decoding the token, checking timestamps, and inspecting the JSON payload, which involves multiple separate steps.
Why This Workflow Matters
JWT authentication failures are one of the most common API debugging scenarios. Tokens contain encoded claims with Unix timestamps that are hard to read, Base64-encoded payloads that need decoding, and nested JSON that needs formatting. A systematic debugging workflow reduces mean time to resolution from hours to minutes.
Workflow Overview
Step-by-Step Instructions
Decode the JWT token
Paste the full JWT (header.payload.signature) into the JWT Decoder. It splits the token into its three parts and decodes the Base64url-encoded header and payload without needing the signing key.
Check token expiry timestamps
Copy the exp (expiration), iat (issued at), and nbf (not before) values from the decoded payload. Paste each into the Timestamp Converter to see the human-readable dates. Compare with current time.
Inspect the full payload
Copy the decoded payload JSON and paste it into the JSON Formatter. Check all claims: iss (issuer), aud (audience), sub (subject), scope or roles. Look for mismatches with your API configuration.
Verify Base64 encoding integrity
If the token looks corrupted, manually decode each segment with the Base64 Encoder (using Base64url variant). This catches truncation from URL encoding, copy-paste errors, or incorrect padding.
Before & After
Opaque JWT, unclear why auth fails
HTTP 401 Unauthorized Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOi... # What went wrong? Is it expired? Wrong audience? # Is the payload even valid JSON?
Decoded JWT with timestamp analysis
Header: {"alg": "RS256"} ✓
Payload: {"sub": "user1", "exp": 1713440000}
Expiry: 2024-04-18T12:00:00Z — EXPIRED 2 hours ago ✗
Fix: Refresh the token or extend TTL in auth config
Automate This Workflow
Copy these scripts to automate the workflow in your preferred language:
$
Bash
# Decode JWT without a library
TOKEN="eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyMSIsImV4cCI6MTcxMzQ0MDAwMH0.sig"
# Split and decode payload (part 2)
PAYLOAD=$(echo "$TOKEN" | cut -d. -f2)
# Add padding and decode
echo "$PAYLOAD" | tr '_-' '/+' | base64 -d 2>/dev/null | jq '.'
# Check expiry
EXP=$(echo "$PAYLOAD" | tr '_-' '/+' | base64 -d 2>/dev/null | jq -r '.exp')
NOW=$(date +%s)
if [ "$EXP" -lt "$NOW" ]; then
echo "TOKEN EXPIRED $(( (NOW - EXP) / 3600 )) hours ago"
echo "Expired at: $(date -d @$EXP 2>/dev/null || date -r $EXP)"
else
echo "Token valid for $(( (EXP - NOW) / 60 )) more minutes"
fi
Py
Python
import json
import base64
from datetime import datetime, timezone
def decode_jwt(token: str) -> dict:
"""Decode JWT without verification (for debugging only)."""
parts = token.split(".")
if len(parts) != 3:
raise ValueError("Invalid JWT format")
# Decode header and payload (Base64url)
def b64_decode(s):
s += "=" * (4 - len(s) % 4) # Add padding
return json.loads(base64.urlsafe_b64decode(s))
header = b64_decode(parts[0])
payload = b64_decode(parts[1])
return {"header": header, "payload": payload}
# Decode and check
token = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyMSIsImV4cCI6MTcxMzQ0MDAwMH0.sig"
result = decode_jwt(token)
print(json.dumps(result, indent=2))
# Check expiry
exp = result["payload"].get("exp")
if exp:
exp_dt = datetime.fromtimestamp(exp, tz=timezone.utc)
now = datetime.now(timezone.utc)
if exp_dt < now:
print(f"EXPIRED {now - exp_dt} ago")
else:
print(f"Valid for {exp_dt - now}")
JS
Node.js
// Decode JWT without verification (debugging only)
function decodeJwt(token) {
const [header, payload] = token.split(".").slice(0, 2)
.map(part => {
const padded = part + "=".repeat((4 - part.length % 4) % 4);
return JSON.parse(
Buffer.from(padded, "base64url").toString()
);
});
return { header, payload };
}
const token = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyMSIsImV4cCI6MTcxMzQ0MDAwMH0.sig";
const { header, payload } = decodeJwt(token);
console.log("Algorithm:", header.alg);
console.log("Subject:", payload.sub);
// Check expiry
if (payload.exp) {
const expDate = new Date(payload.exp * 1000);
const now = new Date();
if (expDate < now) {
const hoursAgo = Math.round((now - expDate) / 3600000);
console.log(`EXPIRED ${hoursAgo} hours ago`);
} else {
const minsLeft = Math.round((expDate - now) / 60000);
console.log(`Valid for ${minsLeft} minutes`);
}
}
Frequently Asked Questions
Is it safe to decode JWTs in the browser?
What are the most common JWT authentication failures?
exp in the past), (2) wrong audience (aud mismatch), (3) clock skew between servers, (4) wrong algorithm (alg mismatch), (5) malformed token (truncated during URL encoding). This workflow catches all five.
How do I handle JWT clock skew between servers?
exp and nbf claims. Most JWT libraries support a clockTolerance or leeway option. Use NTP to synchronize clocks across your servers to minimize the issue.
Should I decode JWTs on the client or server side?
Related Workflows
Try These Tools Now
All tools in this workflow are free and work directly in your browser — no sign-up required.