JWT Standard Claims — iss, sub, aud, exp, nbf, iat, jti Explained
JWT registered claims define standard metadata for tokens: issuer (iss), subject (sub), audience (aud), expiration (exp), not-before (nbf), issued-at (iat), and JWT ID (jti)....
Use the tool
JWT Decoder
Decode and inspect JSON Web Tokens. Local-only — tokens never leave your browser.
JWT registered claims are standardized fields in the payload. They’re optional but widely recognized — libraries like jsonwebtoken validate them automatically.
Decode and inspect JWT claims with the JWT Decoder.
The seven registered claims (RFC 7519)
{
"iss": "https://auth.example.com",
"sub": "user:123456",
"aud": ["https://api.example.com", "https://app.example.com"],
"exp": 1747070400,
"nbf": 1747066800,
"iat": 1747066800,
"jti": "550e8400-e29b-41d4-a716-446655440000"
}
iss — Issuer
// Who created and signed the token
// Value: usually the authentication server URL
jwt.sign({ sub: userId }, secret, {
issuer: 'https://auth.example.com',
// Adds: iss: "https://auth.example.com"
});
// Verify: rejects tokens from unexpected issuers
jwt.verify(token, secret, {
issuer: 'https://auth.example.com',
});
// Throws if iss ≠ "https://auth.example.com"
sub — Subject
// Identifies the principal the token is about
// Usually the user ID — must be unique within the issuer
jwt.sign({
sub: 'user:123456', // User ID as string
name: 'Alice Johnson',
email: 'alice@example.com',
}, secret);
// Access it after verification:
const { sub } = jwt.verify(token, secret);
const userId = sub;
aud — Audience
// Who is the token intended for
// Validates that the token is being used by the right service
// Single audience:
jwt.sign({ sub: 'user:123' }, secret, {
audience: 'https://api.example.com',
});
// Multiple audiences:
jwt.sign({ sub: 'user:123' }, secret, {
audience: ['https://api.example.com', 'https://mobile.example.com'],
});
// Verify — must match at least one audience:
jwt.verify(token, secret, {
audience: 'https://api.example.com',
});
// Throws if "https://api.example.com" not in aud
exp — Expiration Time
// Unix timestamp after which the token must not be accepted
// All JWT libraries validate this automatically
jwt.sign({ sub: 'user:123' }, secret, {
expiresIn: '15m', // 15 minutes
// expiresIn: 900, // 900 seconds
// expiresIn: '1d', // 1 day
// Adds: exp: Math.floor(Date.now()/1000) + 900
});
// When token is expired:
jwt.verify(expiredToken, secret);
// Throws: TokenExpiredError: jwt expired
// Check expiry without throwing:
const decoded = jwt.decode(token);
const isExpired = decoded.exp < Math.floor(Date.now() / 1000);
// Allow some clock skew (servers may have slightly different times):
jwt.verify(token, secret, {
clockTolerance: 30, // Allow 30 seconds of clock difference
});
nbf — Not Before
// Unix timestamp before which the token must not be accepted
// Useful for "issued in advance" tokens (e.g., scheduled emails)
const futureTime = Math.floor(Date.now() / 1000) + 3600; // 1 hour from now
jwt.sign({ sub: 'user:123' }, secret, {
notBefore: '1h', // Not valid for 1 hour
// or: notBefore: 3600
});
// Throws: NotBeforeError: jwt not active
jwt.verify(notYetActiveToken, secret);
iat — Issued At
// Unix timestamp of when the token was issued
// Added automatically by most libraries
jwt.sign({ sub: 'user:123' }, secret);
// Payload includes: iat: 1747066800
// Useful for:
// - Detecting tokens issued before a password change
// - Logging when tokens were created
// - Computing token age
const decoded = jwt.verify(token, secret);
const tokenAgeSeconds = Math.floor(Date.now() / 1000) - decoded.iat;
// Reject tokens older than X (manual check):
if (tokenAgeSeconds > 86400) throw new Error('Token too old');
// Or use issuedAt validation:
jwt.sign({ sub: 'user:123' }, secret, {
// No direct option — check iat manually after verify
});
jti — JWT ID
// Unique identifier for the token
// Enables token revocation and replay attack prevention
import { v4 as uuidv4 } from 'uuid';
// Create token with unique ID:
const jti = uuidv4();
jwt.sign({ sub: 'user:123', jti }, secret, { expiresIn: '15m' });
// Revoke a specific token (server-side blocklist):
const revokedTokens = new Set(); // Use Redis in production
app.post('/auth/logout', (req, res) => {
const decoded = jwt.verify(req.headers.authorization.split(' ')[1], secret);
revokedTokens.add(decoded.jti);
res.json({ message: 'Logged out' });
});
// Check blocklist on every request:
function authMiddleware(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
const decoded = jwt.verify(token, secret);
if (revokedTokens.has(decoded.jti)) {
return res.status(401).json({ error: 'Token revoked' });
}
req.user = decoded;
next();
}
Complete token with all claims
import { v4 as uuidv4 } from 'uuid';
function createAccessToken(userId, userRoles) {
return jwt.sign({
// Standard claims:
iss: 'https://auth.example.com',
aud: 'https://api.example.com',
sub: `user:${userId}`,
jti: uuidv4(),
// Custom claims (use namespaced keys to avoid conflicts):
'https://example.com/roles': userRoles,
'https://example.com/version': 1,
}, ACCESS_SECRET, {
expiresIn: '15m',
// iat and exp added automatically
});
}
Related tools
- JWT Decoder — inspect JWT claims
- Timestamp Converter — convert exp/iat Unix timestamps
- UUID Generator — generate jti claim values
Related posts
- Decoding a JWT Is Not the Same as Verifying It — Every JWT bug in production reduces to the same mistake: trusting a decoded toke…
- JWT Security Checklist for 2026 — Twelve checks every JWT implementation should pass before shipping. The actual c…
- JWT Authentication Flow — Login, Token Storage, Refresh Tokens — JWT authentication involves issuing tokens on login, sending them with requests,…
- JWT Security Best Practices — Common Vulnerabilities and How to Avoid Them — JWT vulnerabilities include algorithm confusion attacks, weak secrets, missing e…
- JWT Token Structure — Header, Payload, and Signature Explained — A JWT has three Base64URL-encoded parts: header, payload, and signature, separat…
Related tool
JWT Decoder
Decode and inspect JSON Web Tokens. Local-only — tokens never leave your browser.
Written by Mian Ali Khalid. Part of the Encoding & Crypto pillar.