Generate Production-Ready JWT Secrets in One Click
Create cryptographically secure JWT signing secrets instantly. Get 256-bit, 384-bit, or 512-bit secrets ready for immediate use in your authentication system.
๐ง Interactive JWT Decoder & Encoder
Decode existing JWTs to inspect their structure, or build new ones with custom claims.
JWT Decoder
JWT Builder
โฐ JWT Expiration Calculator
Calculate precise expiration times for your JWT tokens with various time formats and validation.
Expiration Settings
Quick Presets
Expiration Preview
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1786721982,
"exp": 1786808382
}โ ๏ธ Expiration Best Practices
- Short-lived tokens: Use 15-30 minutes for sensitive operations
- Session tokens: 1-24 hours for user sessions
- API tokens: Consider 1-7 days for automated systems
- Refresh strategy: Implement token refresh for better UX
- Clock skew: Account for time differences between servers
๐ JWT Debugger & Validator
Debug JWT tokens, validate structure, and analyze potential security issues.
๐ JWT Algorithm Comparison
| Algorithm | Type | Key Length | Security | Performance | Use Cases |
|---|---|---|---|---|---|
| HS256 | HMAC-SHA256 | 256 bits (32 bytes) | Good | Fastest | Most common, good for web apps |
| HS384 | HMAC-SHA384 | 384 bits (48 bytes) | Better | Medium | Higher security requirements |
| HS512 | HMAC-SHA512 | 512 bits (64 bytes) | Best | Slower | Maximum security, critical systems |
| RS256 | RSA-SHA256 | 2048+ bits | High | Slow | Public key verification |
| ES256 | ECDSA-SHA256 | 256 bits | High | Fast | Modern alternative to RSA |
๐ก Current selection: HS256 offers hmac with sha-256 (most common) with 256 bits of security. Perfect for most web applications.
๐งฉ JWT Claims Builder
Build standard JWT claims with validation and examples.
Standard Claims
Custom Claims
Generated Claims Preview
{
"iss": "your-app.com",
"sub": "user-123",
"aud": "api.example.com",
"iat": 1786721982,
"exp": 1786808382,
"scope": "read:profile write:posts",
"role": "user",
"email": "[email protected]"
}Usage Examples
JWT_SECRET=your-secret-hereconst jwt = require('jsonwebtoken');
const token = jwt.sign(
{ userId: '123', role: 'admin' },
process.env.JWT_SECRET,
{ algorithm: 'HS256', expiresIn: '24h' }
);JWT Secret Security Best Practices
Secret Length Requirements
Secret length matters
For HMAC-based JWT algorithms, the secret should be at least as long as the hash output:
- HS256 requires at least 256 bits (32 bytes)
- HS384 requires at least 384 bits (48 bytes)
- HS512 requires at least 512 bits (64 bytes)
Using a shorter secret weakens the security of your tokens and makes them vulnerable to brute force attacks.
Secure Secret Storage
- Environment Variables: Store secrets in environment variables, never in source code
- Secret Management: Use dedicated services like AWS Secrets Manager, Azure Key Vault, or HashiCorp Vault
- File Permissions: If storing in files, use strict permissions (600 or 640)
- Version Control: Never commit secrets to Git repositories
- Container Security: Use Docker secrets or Kubernetes secrets in containerized environments
Secret Rotation Strategy
โ Do
- Rotate secrets regularly (every 90 days minimum)
- Support multiple active secrets during rotation
- Use automated rotation tools when possible
- Log secret usage for audit trails
- Test rotation procedures regularly
โ Don't
- Wait for security incidents to rotate
- Use the same secret across environments
- Forget to update all services simultaneously
- Leave old secrets active indefinitely
- Skip testing after rotation
Choosing the Right Algorithm
| Algorithm | Security Level | Performance | Recommendation |
|---|---|---|---|
| HS256 | Good | Fast | Default choice for most applications |
| HS384 | Better | Medium | Use for higher security requirements |
| HS512 | Best | Slower | Maximum security, slight performance cost |
Production Deployment Checklist
- Generate unique secrets for each environment (dev, staging, prod)
- Implement proper secret storage (environment variables or secret manager)
- Set up monitoring for failed JWT validation attempts
- Configure appropriate token expiration times
- Test secret rotation procedure
- Audit code for hardcoded secrets
Complete Implementation Examples
Express.js Middleware with Security
const jwt = require('jsonwebtoken');
const rateLimit = require('express-rate-limit');
// Rate limiting for token endpoints
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5,
message: 'Too many authentication attempts'
});
class JWTService {
constructor() {
this.secret = process.env.JWT_SECRET;
this.algorithm = 'HS256';
if (!this.secret) {
throw new Error('JWT_SECRET environment variable is required');
}
}
generateToken(payload) {
return jwt.sign({
...payload,
iat: Math.floor(Date.now() / 1000),
jti: require('crypto').randomBytes(16).toString('hex')
}, this.secret, {
algorithm: this.algorithm,
expiresIn: '24h',
issuer: 'your-app',
audience: 'your-users'
});
}
verifyToken(token) {
try {
return jwt.verify(token, this.secret, {
algorithms: [this.algorithm],
issuer: 'your-app',
audience: 'your-users'
});
} catch (error) {
throw new Error('Invalid token: ' + error.message);
}
}
}
// Authentication middleware
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Access token required' });
}
try {
const jwtService = new JWTService();
req.user = jwtService.verifyToken(token);
next();
} catch (error) {
return res.status(403).json({ error: 'Invalid token' });
}
};
module.exports = { authenticateToken, authLimiter };Python Flask with Error Handling
import jwt
import os
from datetime import datetime, timedelta, timezone
from functools import wraps
from flask import request, jsonify
class JWTAuth:
def __init__(self):
self.secret = os.getenv('JWT_SECRET')
self.algorithm = 'HS256'
if not self.secret:
raise ValueError('JWT_SECRET environment variable required')
def generate_token(self, user_data):
payload = {
'user_id': user_data['id'],
'email': user_data['email'],
'role': user_data.get('role', 'user'),
'exp': datetime.now(timezone.utc) + timedelta(hours=24),
'iat': datetime.now(timezone.utc),
'iss': 'your-app',
'aud': 'your-users'
}
return jwt.encode(payload, self.secret, algorithm=self.algorithm)
def verify_token(self, token):
try:
return jwt.decode(
token, self.secret,
algorithms=[self.algorithm],
options={"verify_aud": True, "verify_iss": True}
)
except jwt.ExpiredSignatureError:
raise ValueError('Token has expired')
except jwt.InvalidTokenError:
raise ValueError('Invalid token')
# Decorator for protected routes
def token_required(f):
@wraps(f)
def decorated(*args, **kwargs):
auth_header = request.headers.get('Authorization')
if not auth_header:
return jsonify({'error': 'Token missing'}), 401
try:
token = auth_header.split(' ')[1]
jwt_auth = JWTAuth()
payload = jwt_auth.verify_token(token)
request.current_user = payload
except (IndexError, ValueError) as e:
return jsonify({'error': str(e)}), 401
return f(*args, **kwargs)
return decoratedJWT Security Audit Checklist
๐ Secret & Configuration
๐ก๏ธ Implementation Security
๐ Quick Security Test
Test your JWT implementation:
- โข Try using 'none' algorithm โ should be rejected
- โข Send expired token โ should return 401
- โข Modify token signature โ should be invalid
- โข Test with wrong audience/issuer โ should be rejected
Bulk Generation
Implementation Examples
Node.js with Express.js
const jwt = require('jsonwebtoken');
// Generate token (login)
function generateToken(user) {
const payload = {
userId: user.id,
email: user.email,
role: user.role
};
return jwt.sign(payload, process.env.JWT_SECRET, {
algorithm: 'HS256',
expiresIn: '24h',
issuer: 'your-app-name',
audience: 'your-app-users'
});
}
// Verify token (middleware)
function verifyToken(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Access token required' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256']
});
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({ error: 'Invalid token' });
}
}Python with Flask
import jwt
import os
from datetime import datetime, timedelta
from functools import wraps
from flask import request, jsonify
JWT_SECRET = os.getenv('JWT_SECRET')
JWT_ALGORITHM = 'HS256'
def generate_token(user_data):
payload = {
'user_id': user_data['id'],
'email': user_data['email'],
'exp': datetime.utcnow() + timedelta(hours=24),
'iat': datetime.utcnow(),
'iss': 'your-app-name'
}
return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM)
def verify_token(f):
@wraps(f)
def decorated(*args, **kwargs):
token = request.headers.get('Authorization')
if not token:
return jsonify({'error': 'Token missing'}), 401
try:
token = token.split(' ')[1] # Remove 'Bearer '
decoded = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
request.user = decoded
except jwt.ExpiredSignatureError:
return jsonify({'error': 'Token expired'}), 401
except jwt.InvalidTokenError:
return jsonify({'error': 'Invalid token'}), 401
return f(*args, **kwargs)
return decoratedJava with Spring Boot
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class JwtUtil {
@Value("${jwt.secret}")
private String jwtSecret;
private final int jwtExpirationMs = 86400000; // 24 hours
public String generateToken(String username, String role) {
return Jwts.builder()
.setSubject(username)
.claim("role", role)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + jwtExpirationMs))
.signWith(Keys.hmacShaKeyFor(jwtSecret.getBytes()), SignatureAlgorithm.HS256)
.compact();
}
public Claims extractClaims(String token) {
return Jwts.parserBuilder()
.setSigningKey(Keys.hmacShaKeyFor(jwtSecret.getBytes()))
.build()
.parseClaimsJws(token)
.getBody();
}
public boolean isTokenExpired(String token) {
return extractClaims(token).getExpiration().before(new Date());
}
public boolean validateToken(String token) {
try {
extractClaims(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
}Go with Gin Framework
package middleware
import (
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v4"
)
var jwtSecret = []byte("your-secret-here")
type Claims struct {
UserID string `json:"user_id"`
Role string `json:"role"`
jwt.RegisteredClaims
}
func GenerateToken(userID, role string) (string, error) {
claims := Claims{
UserID: userID,
Role: role,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: "your-app",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(jwtSecret)
}
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header required"})
c.Abort()
return
}
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
claims := &Claims{}
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
return jwtSecret, nil
})
if err != nil || !token.Valid {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
c.Abort()
return
}
c.Set("userID", claims.UserID)
c.Set("role", claims.Role)
c.Next()
}
}C# with .NET Core
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
public class JwtService
{
private readonly string _secret = "your-secret-here";
private readonly string _issuer = "your-app";
public string GenerateToken(string userId, string role)
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_secret));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, userId),
new Claim(ClaimTypes.Role, role),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat,
DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
};
var token = new JwtSecurityToken(
issuer: _issuer,
audience: _issuer,
claims: claims,
expires: DateTime.UtcNow.AddHours(24),
signingCredentials: credentials
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
public ClaimsPrincipal ValidateToken(string token)
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_secret));
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = key,
ValidateIssuer = true,
ValidIssuer = _issuer,
ValidateAudience = true,
ValidAudience = _issuer,
ClockSkew = TimeSpan.Zero
};
return tokenHandler.ValidateToken(token, validationParameters, out SecurityToken validatedToken);
}
}PHP with Laravel
<?php
namespace App\Helpers;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Exception;
class JwtHelper
{
private static $secret = 'your-secret-here';
private static $issuer = 'your-app';
private static $algorithm = 'HS256';
public static function generateToken($userId, $role)
{
$payload = [
'iss' => self::$issuer,
'sub' => $userId,
'role' => $role,
'iat' => time(),
'exp' => time() + (24 * 60 * 60) // 24 hours
];
return JWT::encode($payload, self::$secret, self::$algorithm);
}
public static function validateToken($token)
{
try {
$decoded = JWT::decode($token, new Key(self::$secret, self::$algorithm));
return (array) $decoded;
} catch (Exception $e) {
throw new Exception('Invalid token: ' . $e->getMessage());
}
}
public static function refreshToken($token)
{
try {
$decoded = self::validateToken($token);
// Check if token expires in next hour
if ($decoded['exp'] - time() < 3600) {
return self::generateToken($decoded['sub'], $decoded['role']);
}
return $token; // No refresh needed
} catch (Exception $e) {
throw new Exception('Cannot refresh token: ' . $e->getMessage());
}
}
}
// Laravel Middleware
class JwtMiddleware
{
public function handle($request, Closure $next)
{
$token = $request->bearerToken();
if (!$token) {
return response()->json(['error' => 'Token not provided'], 401);
}
try {
$decoded = JwtHelper::validateToken($token);
$request->merge(['user' => $decoded]);
return $next($request);
} catch (Exception $e) {
return response()->json(['error' => 'Unauthorized'], 401);
}
}
}๐ Implementation Security Tips
- Always validate the algorithm to prevent algorithm confusion attacks
- Set appropriate expiration times - shorter is more secure but less convenient
- Include audience and issuer claims for additional validation
- Use HTTPS only in production to prevent token interception
- Implement proper error handling without exposing sensitive information
- Consider implementing token blacklisting for logout functionality
Generate in Terminal
For production systems, generate secrets locally:
OpenSSL (256-bit, base64)
openssl rand -base64 32OpenSSL (256-bit, hex)
openssl rand -hex 32Python secrets module
python3 -c "import secrets; print(secrets.token_urlsafe(32))"Node.js crypto
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"