Profiles·Public

jsonwebtoken

semver>=9.0.0postconditions15functions3last verified2026-06-23coverage score100%

Postconditions: what we check

  • verify · verify-token-expired
    error
    Whentoken's exp claim is before current time
    ThrowsTokenExpiredError: jwt expired
    Required handlingCaller MUST wrap jwt.verify() in try-catch block or use callback error-first pattern. TokenExpiredError indicates legitimate expiration - handle gracefully with 401 response and prompt user to refresh token or re-authenticate.
    costhighin prodimmediate exceptionusers seeauthentication failurevisibilityvisible
    Sources[1]
  • verify · verify-token-not-active
    error
    Whentoken's nbf claim is after current time
    ThrowsNotBeforeError: jwt not active
    Required handlingCaller MUST handle NotBeforeError. Token is valid but not yet active. Either reject with 401 or retry after specified date.
    costhighin prodimmediate exceptionusers seeauthentication failurevisibilityvisible
    Sources[1]
  • verify · verify-invalid-signature
    error
    Whentoken signature does not match expected value
    ThrowsJsonWebTokenError: invalid signature
    Required handlingCaller MUST handle JsonWebTokenError for invalid signatures. This indicates tampering or wrong secret/key. CRITICAL security event - log and reject with 403. Never expose error details to client.
    costhighin prodimmediate exceptionusers seeauthentication failurevisibilityvisible
    Sources[1]
  • verify · verify-malformed-token
    error
    Whentoken structure is invalid (not 3 parts, invalid base64, etc.)
    ThrowsJsonWebTokenError: jwt malformed
    Required handlingCaller MUST handle JsonWebTokenError for malformed tokens. Invalid structure indicates corrupted token or attack. Reject with 400 or 403.
    costhighin prodimmediate exceptionusers seeauthentication failurevisibilityvisible
    Sources[1]
  • verify · verify-invalid-algorithm
    error
    Whentoken algorithm not in options.algorithms whitelist
    ThrowsJsonWebTokenError: invalid algorithm
    Required handlingCaller MUST handle algorithm mismatch errors. This prevents CVE-2015-9235 algorithm confusion attack. Always specify algorithms option in verify().
    costhighin prodimmediate exceptionusers seeauthentication failurevisibilityvisible
    Sources[2]
  • verify · verify-audience-mismatch
    error
    Whentoken aud claim does not match options.audience
    ThrowsJsonWebTokenError: jwt audience invalid. expected: [expected]
    Required handlingCaller MUST handle audience validation errors when using options.audience. Audience mismatch indicates token intended for different service.
    costhighin prodimmediate exceptionusers seeauthentication failurevisibilityvisible
    Sources[3]
  • verify · verify-issuer-mismatch
    error
    Whentoken iss claim does not match options.issuer
    ThrowsJsonWebTokenError: jwt issuer invalid. expected: [expected]
    Required handlingCaller MUST handle issuer validation errors when using options.issuer. Issuer mismatch indicates token from untrusted source.
    costhighin prodimmediate exceptionusers seeauthentication failurevisibilityvisible
    Sources[3]
  • verify · verify-missing-secret
    error
    WhensecretOrPublicKey parameter is undefined or empty
    ThrowsJsonWebTokenError: secret or public key must be provided
    Required handlingCaller MUST handle missing secret errors. This typically indicates configuration error (missing environment variable). Fatal error - should fail fast on application startup, not at runtime.
    costhighin prodimmediate exceptionusers seeauthentication failurevisibilityvisible
    Sources[4]
  • sign · sign-invalid-payload
    warning
    Whenpayload is not a plain object, string, or buffer
    ThrowsError: Expected 'payload' to be a plain object, Buffer, or string
    Required handlingCaller SHOULD validate payload type before calling jwt.sign() or wrap in try-catch. Common when payload is null, undefined, or Promise object (forgot await on database query).
    costhighin prodimmediate exceptionusers seeauthentication failurevisibilityvisible
    Sources[5]
  • sign · sign-missing-secret
    warning
    WhensecretOrPrivateKey is undefined, null, or empty
    ThrowsError: secretOrPrivateKey must have a value
    Required handlingCaller SHOULD ensure secret exists before calling jwt.sign(). Missing secret indicates configuration error. Should fail fast on startup, not at runtime during login.
    costhighin prodimmediate exceptionusers seeauthentication failurevisibilityvisible
    Sources[5]
  • sign · sign-invalid-options
    warning
    Whenoptions.algorithm is invalid or unsupported
    ThrowsError: 'algorithm' must be a valid string enum value
    Required handlingCaller SHOULD validate algorithm option. Common mistake: typo in algorithm name (e.g., 'HS-256' instead of 'HS256').
    costhighin prodimmediate exceptionusers seeauthentication failurevisibilityvisible
    Sources[5]
  • sign · sign-invalid-expiresin
    warning
    Whenoptions.expiresIn is invalid format
    ThrowsError: invalid expiresIn option
    Required handlingCaller SHOULD validate expiresIn format. Accepts seconds (number) or time span string ('1h', '2d', '30s'). Invalid format throws.
    costhighin prodimmediate exceptionusers seeauthentication failurevisibilityvisible
    Sources[6]
  • sign · sign-claim-conflict
    warning
    Whenoptions.expiresIn provided but payload already has exp property
    ThrowsError: Bad 'options.expiresIn' option the payload already has an 'exp' property
    Required handlingCaller SHOULD NOT set exp in both payload and options. Choose one method: either payload.exp or options.expiresIn, not both.
    costhighin prodimmediate exceptionusers seeauthentication failurevisibilityvisible
    Sources[6]
  • decode · decode-used-for-authentication
    error
    Whenjwt.decode() return value is used to make authentication or authorization decisions (e.g., checking role claims, user ID, or admin status) without subsequently calling jwt.verify() with the same token.
    ReturnsJwtPayload | null | string
    Required handlingMUST use jwt.verify() for all authentication and authorization decisions. jwt.decode() is only safe for: (1) inspecting the header to select a verification key from a JWKS, (2) extracting non-security metadata after verification has already succeeded, (3) debugging and logging. Never use decode() result to make access control decisions.
    costcriticalin prodimmediate exceptionusers seesecurity breachvisibilitysilent
    Sources[7][8][9]
  • decode · decode-null-return-not-checked
    warning
    Whenjwt.decode() return value is used without checking for null, on input that may be untrusted, undefined, or malformed.
    Returnsnull | JwtPayload | string
    Required handlingAlways check the return value before accessing properties: const payload = jwt.decode(token); if (!payload) { return res.status(400).json({ error: 'Invalid token' }); }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[7]

Sources

Every postcondition cites at least one of these. Grouped by source type; numbered to match the footnotes above.

Official documentation
Source code
Other references

Research notes

Curator notes from SOURCES.md captured when the profile was written so you can verify the reasoning, not just the rules.

Sources: jsonwebtoken

Official Documentation

Error Types and Handling

TokenExpiredError

Thrown when token's exp claim is before current time.

Properties:

  • name: 'TokenExpiredError'
  • message: 'jwt expired'
  • expiredAt: Date - timestamp when token expired

Source: https://github.com/auth0/node-jsonwebtoken#errors--codes

JsonWebTokenError

General error for invalid tokens, signatures, algorithms, claims, etc.

Common Messages:

  • 'jwt malformed' - invalid token structure
  • 'invalid signature' - signature verification failed
  • 'invalid algorithm' - algorithm not in whitelist
  • 'jwt audience invalid. expected: [expected]' - audience mismatch
  • 'jwt issuer invalid. expected: [expected]' - issuer mismatch

Source: https://github.com/auth0/node-jsonwebtoken#errors--codes

NotBeforeError

Thrown when current time is before token's nbf claim.

Properties:

  • name: 'NotBeforeError'
  • message: 'jwt not active'
  • date: Date - when token becomes valid

Source: https://github.com/auth0/node-jsonwebtoken#errors--codes

Security Vulnerabilities

CVE-2015-9235: Algorithm Confusion Attack

CVSS: 7.5 HIGH Affected: jsonwebtoken < 4.2.2 Fixed: v4.2.2 (2015)

Description: Attacker can bypass signature verification by changing algorithm from asymmetric (RS256) to symmetric (HS256) and using public key as HMAC secret.

Attack Vector:

  1. Server uses RS256 with RSA keypair
  2. Attacker obtains public key (usually public info)
  3. Attacker creates token with alg: HS256 in header
  4. Attacker signs with HMAC-SHA256 using public key as secret
  5. Server verifies with public key as HMAC secret (instead of RSA verification)
  6. Signature validates! Authentication bypassed.

Mitigation: Always specify algorithms option in jwt.verify():

jwt.verify(token, publicKey, { algorithms: ['RS256'] });

Sources:

CVE-2022-23529: JWT Secret Poisoning

CVSS: 7.6 HIGH Affected: jsonwebtoken <= 8.5.1 Fixed: v9.0.0 (December 2022)

Description: Malicious actor can inject arbitrary objects into server's JavaScript runtime through insecure deserialization in jwt.verify(), potentially leading to Remote Code Execution (RCE).

Requirements for Exploitation:

  • Attacker can modify key retrieval parameter
  • Application passes user-controlled input to jwt.verify()
  • Attacker crafts malicious object that triggers code execution

Mitigation:

  1. Upgrade to jsonwebtoken >= 9.0.0
  2. Never use user input as secret
  3. Load keys from trusted sources only
  4. Validate all inputs before passing to JWT functions

Sources:

CVE-2022-23540: Invalid Token Parsing

CVSS: 5.9 MEDIUM Affected: jsonwebtoken <= 8.5.1 Fixed: v9.0.0 (December 2022)

Description: In certain edge cases, jwt.verify() can fail to properly validate malformed tokens, potentially allowing invalid tokens to be accepted.

Mitigation: Upgrade to jsonwebtoken >= 9.0.0

Source: https://www.acunetix.com/vulnerabilities/sca/cve-2022-23540-vulnerability-in-npm-package-jsonwebtoken/

Common Mistakes and Antipatterns

1. Using jwt.decode() for Authentication

CRITICAL SECURITY BUG

jwt.decode() does NOT verify signatures - it only decodes the token.

Vulnerable Code:

const decoded = jwt.decode(userToken);
if (decoded.isAdmin) {
  grantAdminAccess(); // ATTACKER CAN FORGE TOKENS\!
}

Correct Code:

try {
  const decoded = jwt.verify(userToken, secret, { algorithms: ['HS256'] });
  if (decoded.isAdmin) {
    grantAdminAccess(); // Signature verified
  }
} catch (error) {
  // Invalid token
}

Sources:

2. Missing Error Handling on verify()

Common Pattern:

// BUG: No try-catch - crashes on invalid/expired token
const decoded = jwt.verify(token, secret);
console.log(decoded.userId);

Correct Pattern:

try {
  const decoded = jwt.verify(token, secret, { algorithms: ['HS256'] });
  console.log(decoded.userId);
} catch (error) {
  if (error instanceof jwt.TokenExpiredError) {
    // Handle expiration
  } else if (error instanceof jwt.JsonWebTokenError) {
    // Handle invalid token
  }
}

3. Not Checking Callback Error Parameter

Vulnerable Code:

jwt.verify(token, secret, (err, decoded) => {
  console.log(decoded.userId); // BUG: decoded undefined if err exists\!
});

Correct Code:

jwt.verify(token, secret, (err, decoded) => {
  if (err) {
    console.error('Verification failed:', err.message);
    return;
  }
  console.log(decoded.userId);
});

4. Missing algorithms Option

Vulnerable (CVE-2015-9235):

jwt.verify(token, publicKey); // No algorithm whitelist\!

Secure:

jwt.verify(token, publicKey, { algorithms: ['RS256'] });

5. Exposing Error Details to Clients

Bad Practice:

catch (error) {
  res.status(401).json({ error: error.message });
  // Exposes: "invalid signature", "jwt malformed", etc.
}

Best Practice:

catch (error) {
  console.error('JWT error:', error.message); // Log internally
  res.status(401).json({ error: 'Unauthorized' }); // Generic message
}

Best Practices

1. Always Wrap verify() in Try-Catch

try {
  const decoded = jwt.verify(token, secret, {
    algorithms: ['HS256'],
    audience: 'myapp',
    issuer: 'auth-service'
  });
  return decoded;
} catch (error) {
  if (error instanceof jwt.TokenExpiredError) {
    // Prompt user to refresh token
  } else if (error instanceof jwt.JsonWebTokenError) {
    // Invalid token
  }
  throw error;
}

2. Always Specify algorithms Option

// HS256 for symmetric (shared secret)
jwt.verify(token, secret, { algorithms: ['HS256'] });

// RS256 for asymmetric (public/private key)
jwt.verify(token, publicKey, { algorithms: ['RS256'] });

3. Always Set Token Expiration

const token = jwt.sign(
  { userId: 123 },
  secret,
  {
    expiresIn: '15m', // Access token
    algorithm: 'HS256'
  }
);

Recommended Expiration:

  • Access tokens: 15 minutes to 1 hour
  • Refresh tokens: 7 to 30 days

4. Use Strong Secrets

For HMAC algorithms (HS256, HS384, HS512):

// ❌ WEAK
const secret = 'password123';

// ✅ STRONG
const secret = crypto.randomBytes(64).toString('hex');

Minimum Secret Strength:

  • HS256: 256+ bits (32+ bytes)
  • HS384: 384+ bits (48+ bytes)
  • HS512: 512+ bits (64+ bytes)

5. Validate Claims

jwt.verify(token, secret, {
  algorithms: ['HS256'],
  audience: 'myapp',       // Validate aud claim
  issuer: 'auth-service',  // Validate iss claim
  maxAge: '2h'             // Additional age limit
});

6. Handle Specific Error Types

catch (error) {
  if (error instanceof jwt.TokenExpiredError) {
    return { valid: false, reason: 'expired', expiredAt: error.expiredAt };
  }
  if (error instanceof jwt.NotBeforeError) {
    return { valid: false, reason: 'not-active', date: error.date };
  }
  if (error instanceof jwt.JsonWebTokenError) {
    return { valid: false, reason: 'invalid' };
  }
  throw error; // Unexpected error
}

7. NEVER Use decode() for Authentication

// ✅ ONLY use decode() for debugging
const decoded = jwt.decode(token, { complete: true });
console.log('Token header:', decoded?.header);
console.log('Token payload:', decoded?.payload);
// Do NOT make security decisions based on this\!

// ✅ ALWAYS use verify() for authentication
try {
  const decoded = jwt.verify(token, secret, { algorithms: ['HS256'] });
  // Now safe to make security decisions
} catch (error) {
  // Invalid token
}

Supported Algorithms

HMAC (Symmetric - Shared Secret)

  • HS256 - HMAC using SHA-256 (most common for symmetric)
  • HS384 - HMAC using SHA-384
  • HS512 - HMAC using SHA-512

RSA (Asymmetric - Public/Private Key)

  • RS256 - RSASSA-PKCS1-v1_5 using SHA-256
  • RS384 - RSASSA-PKCS1-v1_5 using SHA-384
  • RS512 - RSASSA-PKCS1-v1_5 using SHA-512

ECDSA (Asymmetric - Elliptic Curve)

  • ES256 - ECDSA using P-256 and SHA-256
  • ES384 - ECDSA using P-384 and SHA-384
  • ES512 - ECDSA using P-521 and SHA-512

PSS (Asymmetric - Probabilistic Signature)

  • PS256 - RSASSA-PSS using SHA-256
  • PS384 - RSASSA-PSS using SHA-384
  • PS512 - RSASSA-PSS using SHA-512

None (DANGEROUS - No Signature)

  • none - No signature verification

WARNING: Never allow none algorithm in production! Always whitelist specific algorithms.

Minimum Safe Version

Recommended: >=9.0.0

Rationale:

  • CVE-2015-9235 fixed in 4.2.2
  • CVE-2022-23529 fixed in 9.0.0
  • CVE-2022-23540 fixed in 9.0.0
  • Modern security improvements in 9.x
  • Active maintenance

Latest Version (2026-02-27): v9.0.2

Contract Justification

This contract requires error handling because:

  1. jwt.verify() is a critical security boundary

    • Throws TokenExpiredError, NotBeforeError, JsonWebTokenError
    • Missing error handling = authentication bypass or crash
    • Security decisions depend on proper error handling
  2. jwt.sign() can throw on invalid inputs

    • Invalid payload, secret, or options cause errors
    • Missing error handling = crash during login/token generation
  3. Common mistakes are security-critical

    • Using decode() instead of verify() = complete auth bypass
    • Missing algorithms option = vulnerable to CVE-2015-9235
    • Not checking callback errors = undefined behavior
  4. Error types indicate different security states

    • TokenExpiredError: Legitimate expiration (refresh needed)
    • JsonWebTokenError: Invalid/tampered token (reject access)
    • NotBeforeError: Token not yet valid (retry later)

The library is designed to fail fast on security violations, making proper error handling essential for both security and reliability.

Additional Resources

Need a different package?
Request a profile