Need to decode JWT tokens? Try the free JWT Decoder at Orbit2x - inspect headers, payloads, and verify signatures instantly without installing libraries.
JWT is a compact, URL-safe token format used for authentication and information exchange. JWTs are commonly used in:
- API Authentication - Bearer tokens for REST APIs
- Single Sign-On (SSO) - Auth0, Okta, AWS Cognito
- OAuth 2.0 - Access tokens and refresh tokens
- Session Management - Stateless session storage
- Microservices - Service-to-service authentication
A JWT consists of three Base64URL-encoded parts separated by dots (.):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
│ │ │
│ HEADER │ PAYLOAD │ SIGNATURE
Decoded:
// HEADER
{
"alg": "HS256",
"typ": "JWT"
}
// PAYLOAD
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
// SIGNATURE
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)No installation required! Use the free online decoder:
Features:
- ✅ Decode header and payload instantly
- ✅ Inspect all claims (sub, iss, exp, iat, nbf)
- ✅ Verify signatures (HS256, HS384, HS512, RS256)
- ✅ Check expiration status
- ✅ View token metadata
- ✅ 100% client-side (no server upload)
- ✅ Copy decoded JSON
- ✅ Free, no signup
// Decode without verification (read-only)
function decodeJWT(token) {
const parts = token.split('.');
if (parts.length !== 3) {
throw new Error('Invalid JWT format');
}
const header = JSON.parse(atob(parts[0]));
const payload = JSON.parse(atob(parts[1]));
return { header, payload };
}
// Usage
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
const decoded = decodeJWT(token);
console.log('Header:', decoded.header);
console.log('Payload:', decoded.payload);
console.log('Expires:', new Date(decoded.payload.exp * 1000));With Verification (using jsonwebtoken library):
npm install jsonwebtokenconst jwt = require('jsonwebtoken');
// Decode and verify HMAC signature
try {
const decoded = jwt.verify(token, 'your-256-bit-secret');
console.log('✅ Valid token:', decoded);
} catch (err) {
console.error('❌ Invalid token:', err.message);
}
// Decode without verification (unsafe for production)
const decoded = jwt.decode(token, { complete: true });
console.log('Header:', decoded.header);
console.log('Payload:', decoded.payload);
// Verify RSA signature (public key)
const publicKey = fs.readFileSync('public-key.pem');
const decoded = jwt.verify(token, publicKey, { algorithms: ['RS256'] });pip install pyjwtimport jwt
import json
from base64 import urlsafe_b64decode
# Decode without verification
def decode_jwt(token):
# Split token
parts = token.split('.')
if len(parts) != 3:
raise ValueError('Invalid JWT format')
# Add padding if needed
header = parts[0] + '=' * (4 - len(parts[0]) % 4)
payload = parts[1] + '=' * (4 - len(parts[1]) % 4)
# Decode
header_decoded = json.loads(urlsafe_b64decode(header))
payload_decoded = json.loads(urlsafe_b64decode(payload))
return header_decoded, payload_decoded
# With verification (PyJWT)
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
try:
# Verify HMAC signature
decoded = jwt.decode(token, 'your-256-bit-secret', algorithms=['HS256'])
print('✅ Valid token:', decoded)
except jwt.ExpiredSignatureError:
print('❌ Token expired')
except jwt.InvalidTokenError:
print('❌ Invalid token')
# Decode without verification (read claims only)
decoded = jwt.decode(token, options={"verify_signature": False})
print('Payload:', decoded)
# Get header
header = jwt.get_unverified_header(token)
print('Algorithm:', header['alg'])go get github.com/golang-jwt/jwt/v5package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"log"
"strings"
"github.com/golang-jwt/jwt/v5"
)
// Decode without verification
func decodeJWT(tokenString string) (map[string]interface{}, map[string]interface{}, error) {
parts := strings.Split(tokenString, ".")
if len(parts) != 3 {
return nil, nil, fmt.Errorf("invalid JWT format")
}
// Decode header
headerBytes, _ := base64.RawURLEncoding.DecodeString(parts[0])
var header map[string]interface{}
json.Unmarshal(headerBytes, &header)
// Decode payload
payloadBytes, _ := base64.RawURLEncoding.DecodeString(parts[1])
var payload map[string]interface{}
json.Unmarshal(payloadBytes, &payload)
return header, payload, nil
}
// With verification
func verifyJWT(tokenString string, secret []byte) (*jwt.Token, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
// Validate algorithm
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return secret, nil
})
if err != nil {
return nil, err
}
if !token.Valid {
return nil, fmt.Errorf("invalid token")
}
return token, nil
}
func main() {
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
// Decode without verification
header, payload, _ := decodeJWT(tokenString)
fmt.Println("Header:", header)
fmt.Println("Payload:", payload)
// Verify signature
token, err := verifyJWT(tokenString, []byte("your-256-bit-secret"))
if err != nil {
log.Fatal("Invalid token:", err)
}
// Extract claims
if claims, ok := token.Claims.(jwt.MapClaims); ok {
fmt.Println("User ID:", claims["sub"])
fmt.Println("Name:", claims["name"])
fmt.Println("Issued at:", claims["iat"])
}
}<?php
// Decode without verification
function decodeJWT($token) {
$parts = explode('.', $token);
if (count($parts) !== 3) {
throw new Exception('Invalid JWT format');
}
$header = json_decode(base64_decode(strtr($parts[0], '-_', '+/')), true);
$payload = json_decode(base64_decode(strtr($parts[1], '-_', '+/')), true);
return ['header' => $header, 'payload' => $payload];
}
// Usage
$token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
$decoded = decodeJWT($token);
echo "Header: " . json_encode($decoded['header']) . "\n";
echo "Payload: " . json_encode($decoded['payload']) . "\n";
// With verification (using firebase/php-jwt)
require 'vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
$secret = 'your-256-bit-secret';
try {
$decoded = JWT::decode($token, new Key($secret, 'HS256'));
echo "✅ Valid token\n";
print_r($decoded);
} catch (Exception $e) {
echo "❌ Invalid token: " . $e->getMessage() . "\n";
}
?>| Claim | Name | Type | Description |
|---|---|---|---|
iss |
Issuer | String | Token issuer (e.g., "auth0.com") |
sub |
Subject | String | User ID or subject identifier |
aud |
Audience | String/Array | Intended recipient(s) |
exp |
Expiration Time | Number | Unix timestamp when token expires |
nbf |
Not Before | Number | Unix timestamp when token becomes valid |
iat |
Issued At | Number | Unix timestamp when token was created |
jti |
JWT ID | String | Unique identifier for token |
{
"sub": "user123",
"name": "John Doe",
"email": "john@example.com",
"roles": ["admin", "editor"],
"permissions": ["read", "write", "delete"],
"org_id": "company-456",
"premium": true,
"iat": 1516239022,
"exp": 1516242622
}Try decoding your tokens: JWT Decoder
-
Use Strong Secrets
// ❌ Weak secret const secret = "password123"; // ✅ Strong secret (256+ bits) const secret = crypto.randomBytes(32).toString('hex'); // "a4f8e9c2b7d6f1a3e8c7b2d9f4a1e6c3b8d2f9a4e7c1b6d3f8a2e9c4b7d1f6a3"
-
Set Expiration Times
// ✅ Access token: 15-60 minutes jwt.sign(payload, secret, { expiresIn: '15m' }); // ✅ Refresh token: 7-30 days jwt.sign(payload, secret, { expiresIn: '7d' });
-
Use HTTPS Only
// ✅ Send JWT over HTTPS res.cookie('token', jwt, { httpOnly: true, secure: true, // HTTPS only sameSite: 'strict' });
-
Verify Algorithm
// ✅ Whitelist allowed algorithms jwt.verify(token, secret, { algorithms: ['HS256'] });
-
Check Expiration
// ✅ Validate exp claim const decoded = jwt.verify(token, secret); if (decoded.exp < Date.now() / 1000) { throw new Error('Token expired'); }
-
Don't use
nonealgorithm// ❌ DANGEROUS - No signature verification! { "alg": "none", "typ": "JWT" }
-
Don't store sensitive data
// ❌ JWT payload is NOT encrypted (only Base64 encoded) { "password": "secret123", // NEVER do this "credit_card": "1234-5678-9012-3456" // NEVER do this }
-
Don't accept all algorithms
// ❌ Vulnerable to algorithm confusion attacks jwt.verify(token, secret); // Missing algorithm check // ✅ Specify allowed algorithms jwt.verify(token, secret, { algorithms: ['HS256'] });
-
Don't skip signature verification
// ❌ Unsafe - anyone can modify payload const decoded = jwt.decode(token); // ✅ Always verify signature const decoded = jwt.verify(token, secret);
-
Don't use weak secrets
// ❌ Weak secrets can be cracked const secret = "secret"; // 6 characters = crackable in seconds // ✅ Use 256+ bit secrets const secret = crypto.randomBytes(32).toString('hex'); // 64 hex chars = 256 bits
👉 JWT Signature Cracker - Test your JWT secrets for weakness (educational/authorized testing only)
Features:
- ✅ Dictionary attack with 10,000+ common passwords
- ✅ Custom wordlist upload
- ✅ HS256/HS384/HS512 support
- ✅ Performance metrics
- ✅ Security recommendations
WARNING: Only test JWTs you own or have written permission to test.
# Check algorithm (look for "none" or weak algorithms)
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...." | cut -d'.' -f1 | base64 -d
# Check expiration
echo "eyJhbGci..." | cut -d'.' -f2 | base64 -d | jq '.exp'
date -d @1516242622 # Convert Unix timestamp
# Verify signature manually (HS256)
echo -n "header.payload" | openssl dgst -sha256 -hmac "your-secret" -binary | base64| Vulnerability | Description | Impact | Fix |
|---|---|---|---|
| Algorithm Confusion | Accept none algorithm |
No signature = attacker can forge tokens | Whitelist HS256 only |
| Weak Secret | Use short/common secrets | Brute force attack | Use 256+ bit random secrets |
| No Expiration | Missing exp claim |
Tokens valid forever | Set exp claim (15-60 min) |
| Public Key Substitution | RS256 → HS256 switching | Attacker uses public key as secret | Validate algorithm |
| JTI Reuse | Reuse jti claim |
Token replay attacks | Use unique jti per token |
Test your tokens: JWT Cracker (educational purposes)
| Method | Storage | Stateless | Scalability | Security | Use Case |
|---|---|---|---|---|---|
| JWT | Client | ✅ Yes | Excellent | Good* | APIs, SPAs, Mobile |
| Session Cookies | Server | ❌ No | Limited | Excellent | Traditional web apps |
| OAuth 2.0 | Server | ❌ No | Good | Excellent | Third-party auth |
| API Keys | Client | ✅ Yes | Excellent | Basic | Service-to-service |
| SAML | Server | ❌ No | Limited | Excellent | Enterprise SSO |
*Good if implemented correctly with strong secrets and HTTPS
// Login endpoint
app.post('/login', async (req, res) => {
const { email, password } = req.body;
// Verify credentials
const user = await User.findOne({ email });
if (!user || !await bcrypt.compare(password, user.password)) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Generate access token (15 min)
const accessToken = jwt.sign(
{
sub: user.id,
email: user.email,
roles: user.roles
},
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
// Generate refresh token (7 days)
const refreshToken = jwt.sign(
{ sub: user.id },
process.env.JWT_REFRESH_SECRET,
{ expiresIn: '7d' }
);
// Store refresh token in database
await RefreshToken.create({
userId: user.id,
token: refreshToken,
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
});
res.json({ accessToken, refreshToken });
});// Middleware to verify JWT
const authenticateJWT = (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No token provided' });
}
const token = authHeader.substring(7); // Remove "Bearer "
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded; // Attach user to request
next();
} catch (err) {
if (err.name === 'TokenExpiredError') {
return res.status(401).json({ error: 'Token expired' });
}
return res.status(403).json({ error: 'Invalid token' });
}
};
// Protected route
app.get('/profile', authenticateJWT, async (req, res) => {
const user = await User.findById(req.user.sub);
res.json({ user });
});app.post('/refresh', async (req, res) => {
const { refreshToken } = req.body;
if (!refreshToken) {
return res.status(401).json({ error: 'No refresh token' });
}
try {
// Verify refresh token
const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
// Check if token exists in database
const storedToken = await RefreshToken.findOne({
userId: decoded.sub,
token: refreshToken
});
if (!storedToken) {
return res.status(403).json({ error: 'Invalid refresh token' });
}
// Generate new access token
const user = await User.findById(decoded.sub);
const accessToken = jwt.sign(
{
sub: user.id,
email: user.email,
roles: user.roles
},
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
res.json({ accessToken });
} catch (err) {
return res.status(403).json({ error: 'Invalid refresh token' });
}
});app.post('/logout', authenticateJWT, async (req, res) => {
const { refreshToken } = req.body;
// Delete refresh token from database
await RefreshToken.deleteOne({
userId: req.user.sub,
token: refreshToken
});
res.json({ message: 'Logged out successfully' });
});- JWT Decoder - Decode and inspect JWT tokens
- JWT Cracker - Test JWT security (educational)
- Base64 Encoder - Encode/decode Base64
- Hash Generator - Generate SHA-256 hashes
- Node.js: jsonwebtoken - Most popular JWT library
- Python: PyJWT - Python JWT implementation
- Go: golang-jwt/jwt - Go JWT library
- PHP: firebase/php-jwt - Firebase JWT
- Rust: jsonwebtoken - Rust JWT
- Java: java-jwt - Auth0 JWT for Java
- RFC 7519 - JWT Specification
- JWT.io - Debugger and libraries
- OWASP JWT Cheat Sheet
A: JWT is secure IF implemented correctly:
- ✅ Use strong secrets (256+ bits)
- ✅ Verify signatures
- ✅ Set expiration times
- ✅ Use HTTPS only
- ❌ Don't store secrets in JWT payload
A: Yes, use JWE (JSON Web Encryption) for encrypted tokens. Standard JWT is only signed, not encrypted.
A:
- Cookies (HttpOnly, Secure, SameSite) - More secure against XSS
- localStorage - Easier to use but vulnerable to XSS
- Recommendation: Use HttpOnly cookies for web apps
A: JWTs can't be revoked (stateless). Solutions:
- Use short expiration times (15 min)
- Store token IDs in database (blacklist)
- Use refresh tokens for long-lived sessions
A:
- HS256 (HMAC): Symmetric secret key, faster, good for single-server
- RS256 (RSA): Asymmetric keypair, slower, good for distributed systems
- Argon2 Hash Generator - Secure password hashing
- OpenSSL Command Generator - Generate SSL/TLS commands
- PEM Decoder - Parse X.509 certificates
- All Security Tools - Complete security toolkit
Made with ❤️ by Orbit2x - Free Developer & Security Tools
Decode tokens now: JWT Decoder • Test Security