You've got a JWT token from an API response, a cookie, or an authentication header — and you need to see what's inside. The good news: you don't need the signing secret to decode a JWT. The header and payload are simply Base64URL-encoded, not encrypted. In this guide, you'll learn exactly how JWT decoding works and how to inspect tokens safely.
Why You Can Decode JWT Without a Secret
A JWT (JSON Web Token) has three parts, separated by dots:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
|___________________________________| |______________________________________________| |___________________________________|
HEADER PAYLOAD SIGNATUREJWT Structure
The header and payload are just Base64URL-encoded JSON — anyone can decode them. Only the signature requires the secret key to verify (not decode). This is by design: JWTs are meant for authorization, not secrecy.
Decoding = reading the payload (no secret needed). Verifying = confirming the signature is valid (requires the secret/public key). Always verify signatures on the server — never trust a decoded JWT without verification.
Decoded JWT Breakdown
Header
{
"alg": "HS256", // Signing algorithm (HS256, RS256, ES256)
"typ": "JWT" // Token type
}JSON
Payload (Claims)
{
"sub": "1234567890", // Subject (user ID)
"name": "John Doe", // Custom claim
"email": "[email protected]", // Custom claim
"iat": 1516239022, // Issued At (Unix timestamp)
"exp": 1516242622, // Expiration (Unix timestamp)
"iss": "https://auth.example.com", // Issuer
"aud": "my-api", // Audience
"roles": ["admin", "editor"] // Custom claim (array)
}JSON
Standard JWT Claims Reference
Claim Full Name Description
─────────────────────────────────────────────────────
iss Issuer Who created the token
sub Subject Who the token is about (usually user ID)
aud Audience Intended recipient (API/service)
exp Expiration When the token expires (Unix timestamp)
nbf Not Before Token not valid before this time
iat Issued At When the token was created
jti JWT ID Unique identifier for the tokenJWT Claims
How to Decode JWT in Code
JavaScript (Browser)
function decodeJWT(token) {
const parts = token.split('.');
if (parts.length !== 3) throw new Error('Invalid JWT');
const header = JSON.parse(atob(parts[0]));
const payload = JSON.parse(
atob(parts[1].replace(/-/g, '+').replace(/_/g, '/'))
);
return { header, payload };
}
const { header, payload } = decodeJWT(token);
console.log('Expires:', new Date(payload.exp * 1000));JavaScript
Python
import json, base64
def decode_jwt(token):
parts = token.split('.')
# Add padding for Base64
payload = parts[1] + '=' * (4 - len(parts[1]) % 4)
decoded = base64.urlsafe_b64decode(payload)
return json.loads(decoded)
claims = decode_jwt(token)
print(f"User: {claims['sub']}, Expires: {claims['exp']}")Python
C#
using System.Text;
using System.Text.Json;
string[] parts = token.Split('.');
string payload = parts[1]
.Replace('-', '+').Replace('_', '/');
// Add padding
switch (payload.Length % 4) {
case 2: payload += "=="; break;
case 3: payload += "="; break;
}
var json = Encoding.UTF8.GetString(
Convert.FromBase64String(payload)
);
var claims = JsonSerializer.Deserialize<JsonElement>(json);
Console.WriteLine($"Subject: {claims.GetProperty("sub")}");C#
Common JWT Debugging Scenarios
- "401 Unauthorized" — Check
expclaim. Token may be expired. Use our DateTime Converter to convert the Unix timestamp. - "403 Forbidden" — Check
rolesorscopeclaims. User may lack required permissions. - "Invalid audience" — The
audclaim doesn't match the API's expected audience. - "Token not yet valid" — The
nbf(Not Before) claim is in the future. Check server clock sync.
Try JWT Decoder
Paste any JWT token and instantly see the decoded header and payload. 100% stateless — your token never leaves your browser.
Open JWT Decoder →Security Best Practices
- Never store sensitive data (passwords, credit cards) in JWT payloads — they're readable by anyone
- Always verify JWT signatures on the server before trusting claims
- Set short expiration times (
exp) — 15 minutes for access tokens, days for refresh tokens - Use
RS256(asymmetric) overHS256(symmetric) for microservice architectures - Validate
issandaudclaims to prevent token substitution attacks
Conclusion
Decoding JWT tokens without a secret is not only possible — it's essential for debugging OAuth2 flows, API authentication issues, and token expiration problems. Use Polymorpher's free JWT Decoder to inspect tokens instantly, without any data transmission. For related tasks, check out the Base64 Encoder for understanding JWT encoding, and the Hash Generator for exploring the cryptographic algorithms behind JWT signatures.