Skip to main content

jsonwebtoken

PropertyValue
Packagejsonwebtoken
Versions Covered>=9.0.0
Contract Version1.0.0
Statusdraft
Last Verified2026-02-27
Maintainercorpus-team

Installation

npm install jsonwebtoken

Covered Functions

This contract covers 2 function(s):

verify()

Verifies a JWT token signature and returns decoded payload - CRITICAL security boundary

Import:

import { verify } from 'jsonwebtoken';

Postconditions

What happens after calling this function:

🔴 ERROR - verify-token-expired

Condition: token's exp claim is before current time

Throws: TokenExpiredError: jwt expired

Required Handling:

Caller 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.

📖 Source

🔴 ERROR - verify-token-not-active

Condition: token's nbf claim is after current time

Throws: NotBeforeError: jwt not active

Required Handling:

Caller MUST handle NotBeforeError. Token is valid but not yet active. Either reject with 401 or retry after specified date.

📖 Source

🔴 ERROR - verify-invalid-signature

Condition: token signature does not match expected value

Throws: JsonWebTokenError: invalid signature

Required Handling:

Caller 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.

📖 Source

🔴 ERROR - verify-malformed-token

Condition: token structure is invalid (not 3 parts, invalid base64, etc.)

Throws: JsonWebTokenError: jwt malformed

Required Handling:

Caller MUST handle JsonWebTokenError for malformed tokens. Invalid structure indicates corrupted token or attack. Reject with 400 or 403.

📖 Source

🔴 ERROR - verify-invalid-algorithm

Condition: token algorithm not in options.algorithms whitelist

Throws: JsonWebTokenError: invalid algorithm

Required Handling:

Caller MUST handle algorithm mismatch errors. This prevents CVE-2015-9235 algorithm confusion attack. Always specify algorithms option in verify().

📖 Source

🔴 ERROR - verify-audience-mismatch

Condition: token aud claim does not match options.audience

Throws: JsonWebTokenError: jwt audience invalid. expected: [expected]

Required Handling:

Caller MUST handle audience validation errors when using options.audience. Audience mismatch indicates token intended for different service.

📖 Source

🔴 ERROR - verify-issuer-mismatch

Condition: token iss claim does not match options.issuer

Throws: JsonWebTokenError: jwt issuer invalid. expected: [expected]

Required Handling:

Caller MUST handle issuer validation errors when using options.issuer. Issuer mismatch indicates token from untrusted source.

📖 Source

🔴 ERROR - verify-missing-secret

Condition: secretOrPublicKey parameter is undefined or empty

Throws: JsonWebTokenError: secret or public key must be provided

Required Handling:

Caller MUST handle missing secret errors. This typically indicates configuration error (missing environment variable). Fatal error - should fail fast on application startup, not at runtime.

📖 Source

Edge Cases

Known gotchas and sharp edges:

⚠️ WARNING - algorithm-confusion-cve-2015-9235

CRITICAL: Always specify algorithms option in jwt.verify() to prevent CVE-2015-9235 algorithm confusion attack. Without algorithms whitelist, attacker can change RS256 (asymmetric) to HS256 (symmetric) and use public key as HMAC secret, bypassing signature verification. Vulnerable: jwt.verify(token, publicKey) Secure: jwt.verify(token, publicKey, algorithms: ['RS256'] )

📖 Source

⚠️ WARNING - callback-error-handling

When using callback-style jwt.verify(token, secret, callback), MUST check err parameter before using decoded value. Common mistake: jwt.verify(token, secret, (err, decoded) = console.log(decoded.userId); // BUG: decoded undefined if error! ); Correct pattern: Check err first, return early if error exists.

📖 Source


sign()

Signs a JWT token with secret or private key

Import:

import { sign } from 'jsonwebtoken';

Postconditions

What happens after calling this function:

🔴 ERROR - sign-invalid-payload

Condition: payload is not a plain object, string, or buffer

Throws: Error: Expected 'payload' to be a plain object, Buffer, or string

Required Handling:

Caller MUST 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).

📖 Source

🔴 ERROR - sign-missing-secret

Condition: secretOrPrivateKey is undefined, null, or empty

Throws: Error: secretOrPrivateKey must have a value

Required Handling:

Caller MUST ensure secret exists before calling jwt.sign(). Missing secret indicates configuration error. Should fail fast on startup, not at runtime during login.

📖 Source

🔴 ERROR - sign-invalid-options

Condition: options.algorithm is invalid or unsupported

Throws: Error: 'algorithm' must be a valid string enum value

Required Handling:

Caller MUST validate algorithm option. Common mistake: typo in algorithm name (e.g., 'HS-256' instead of 'HS256').

📖 Source

🔴 ERROR - sign-invalid-expiresin

Condition: options.expiresIn is invalid format

Throws: Error: invalid expiresIn option

Required Handling:

Caller MUST validate expiresIn format. Accepts seconds (number) or time span string ('1h', '2d', '30s'). Invalid format throws.

📖 Source

🔴 ERROR - sign-claim-conflict

Condition: options.expiresIn provided but payload already has exp property

Throws: Error: Bad 'options.expiresIn' option the payload already has an 'exp' property

Required Handling:

Caller MUST NOT set exp in both payload and options. Choose one method: either payload.exp or options.expiresIn, not both.

📖 Source


Example: Proper Error Handling

import jsonwebtoken from 'jsonwebtoken';

async function example() {
try {
const result = await verify(/* args */);
// Handle success
return result;
} catch (error) {
// Handle error according to contract postconditions
console.error('Error:', error);
throw error;
}
}

See Also