JWT Algorithms
JWT tokens can be signed using various algorithms. Understanding these algorithms is crucial for implementing secure JWT authentication.
Algorithm Types
JWT supports three types of algorithms:
- Symmetric (HMAC): Uses a shared secret key
- Asymmetric (RSA): Uses public/private key pair
- Asymmetric (ECDSA): Uses elliptic curve cryptography
1. HS256 (HMAC SHA-256)
Type: Symmetric (HMAC)
Use Case: Single server, internal services, microservices with shared secret
// Signing
const token = jwt.sign(payload, secret, { algorithm: 'HS256' });
// Verification
const decoded = jwt.verify(token, secret, { algorithms: ['HS256'] });
// Requirements:
// - Secret: At least 32 characters (256 bits)
// - Same secret for signing and verification
// - Secret must be kept secure✅ Advantages
- Fast signing and verification
- Simple to implement
- Most commonly used
- Good for single-server applications
⚠️ Considerations
- Requires shared secret between parties
- Not suitable for public APIs
- Secret must be distributed securely
2. RS256 (RSA SHA-256)
Type: Asymmetric (RSA)
Use Case: Distributed systems, microservices, public APIs
// Generate key pair
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
// Signing (with private key)
const token = jwt.sign(payload, privateKey, { algorithm: 'RS256' });
// Verification (with public key)
const decoded = jwt.verify(token, publicKey, { algorithms: ['RS256'] });
// Requirements:
// - Private key: Keep secure, only for signing
// - Public key: Can be shared, used for verification
// - Key size: At least 2048 bits✅ Advantages
- Public key can verify without sharing private key
- Perfect for distributed systems
- Ideal for microservices architecture
- Public key can be published (JWKS endpoint)
⚠️ Considerations
- Slower than symmetric algorithms
- Requires key pair management
- Need to protect private key
3. ES256 (ECDSA P-256 SHA-256)
Type: Asymmetric (ECDSA)
Use Case: Similar to RS256, but with smaller key sizes
// Generate key pair
const { publicKey, privateKey } = crypto.generateKeyPairSync('ec', {
namedCurve: 'prime256v1',
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
// Signing
const token = jwt.sign(payload, privateKey, { algorithm: 'ES256' });
// Verification
const decoded = jwt.verify(token, publicKey, { algorithms: ['ES256'] });
// Requirements:
// - Private key: Keep secure
// - Public key: Can be shared
// - Smaller key size than RSA (256 bits vs 2048 bits)✅ Advantages
- Smaller key sizes than RSA
- Faster than RSA
- Same security level with smaller keys
- Good for resource-constrained environments
Other Algorithms
HS384 / HS512
Symmetric algorithms with longer hash outputs. Use when you need stronger security guarantees.
RS384 / RS512
RSA variants with longer hash outputs. Generally not recommended - RS256 is sufficient.
ES384 / ES512
ECDSA variants with longer hash outputs. ES256 is usually sufficient.
"none" Algorithm
⚠️ NEVER USE - This algorithm provides no security. Always explicitly reject it.
Algorithm Selection Guide
| Scenario | Recommended Algorithm |
|---|---|
| Single server application | HS256 |
| Microservices (shared secret) | HS256 |
| Public API / Distributed system | RS256 or ES256 |
| Mobile app authentication | RS256 or ES256 |
| Resource-constrained devices | ES256 |
| OAuth 2.0 / OpenID Connect | RS256 (standard) |
Algorithm Confusion Attack Prevention
⚠️ Critical Security Issue
If a server expects RS256 but accepts HS256, an attacker can forge tokens using the public key as a secret.
// ❌ VULNERABLE - Accepts any algorithm
jwt.verify(token, publicKey);
// ✅ SECURE - Explicitly specify allowed algorithms
jwt.verify(token, publicKey, {
algorithms: ['RS256'] // Only accept RS256
});
// ✅ BEST - Use algorithm whitelist
const ALLOWED_ALGORITHMS = ['RS256'];
jwt.verify(token, publicKey, {
algorithms: ALLOWED_ALGORITHMS
});
// Never trust the algorithm in the token header
// Always explicitly specify what algorithms you accept