← Back to Learn

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

HS256 Example
// 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

RS256 Example
// 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

ES256 Example
// 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

ScenarioRecommended Algorithm
Single server applicationHS256
Microservices (shared secret)HS256
Public API / Distributed systemRS256 or ES256
Mobile app authenticationRS256 or ES256
Resource-constrained devicesES256
OAuth 2.0 / OpenID ConnectRS256 (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.

Preventing Algorithm Confusion
// ❌ 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