JWT Structure
A JWT has 3 parts separated by dots: header.payload.signature
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. ← Header (Base64)
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik ← Payload (Base64)
pvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ. ← Signature
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header
{
"alg": "HS256", // Algorithm: HS256, RS256, ES256
"typ": "JWT"
}
Common Claims (Payload)
| Claim | Name | Description |
|---|---|---|
sub | Subject | User ID |
iss | Issuer | Who issued the token |
aud | Audience | Intended recipient |
exp | Expiration | Unix timestamp expiry |
iat | Issued At | When token was created |
nbf | Not Before | Token not valid before |
jti | JWT ID | Unique token identifier |
Security Best Practices
- Use short expiration times (15 min for access tokens)
- Use refresh tokens for session renewal
- Store in httpOnly cookies (not localStorage)
- Always validate the signature server-side
- Use RS256 (asymmetric) for distributed systems
- Never put sensitive data in the payload (it's Base64, not encrypted)
- Implement token revocation for logout
Node.js Example
import jwt from 'jsonwebtoken';
// Create token
const token = jwt.sign(
{ userId: 123, role: 'admin' },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
// Verify token
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
console.log(decoded.userId); // 123
} catch (err) {
console.error('Invalid token');
}