Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Bug: clockTolerance accepts arbitrarily large values, bypassing exp verification entirely

Open
#1,021 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
52/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Quiet
Tech stack
javascript, node.js

Research direction

Start in verify.js, tracing clockTolerance validation and the exp/nbf comparisons. Confirm the supported upper bound and error behavior with maintainers, then add focused regression coverage for oversized tolerance and both checks. Done means large values are rejected without weakening normal clock-skew handling.

Written by the indexing model from the issue text.

Description

Summary

The clockTolerance option in verify() accepts any positive integer with no upper bound validation. Passing Number.MAX_SAFE_INTEGER (or any value large enough that exp + clockTolerance overflows to Infinity) causes the expiry check to silently pass for any expired token, regardless of how long ago it expired.

Environment

  • jsonwebtoken version: 9.0.3 (latest)
  • Node.js: v20+

Reproduction

const jwt = require("jsonwebtoken");
const SECRET = "supersecret";

// Sign a token that expired 1 year ago
const expiredToken = jwt.sign(
  { sub: "user", role: "admin" },
  SECRET,
  { expiresIn: "-365d" }
);

// Normal verify correctly rejects it
try {
  jwt.verify(expiredToken, SECRET);
} catch (e) {
  console.log(e.message); // "jwt expired"
}

// Bypass with MAX_SAFE_INTEGER clockTolerance
const payload = jwt.verify(expiredToken, SECRET, {
  clockTolerance: Number.MAX_SAFE_INTEGER  // 9007199254740991
});

console.log(payload); // { sub: "user", role: "admin", ... } — token accepted!

Root Cause

In verify.js, the expiry check is:

if (clockTimestamp >= payload.exp + (options.clockTolerance || 0)) {
  return done(new TokenExpiredError(...));
}

When clockTolerance is Number.MAX_SAFE_INTEGER, the addition payload.exp + 9007199254740991 produces a value far larger than any realistic clockTimestamp. The comparison becomes:

1775002429 >= 9007200998207420  →  false

So the expiry check is skipped entirely. The same issue affects the nbf (not before) check via the same pattern.

No validation is performed on clockTolerance beyond checking it is a number:

// Current validation (insufficient):
if (options.clockTimestamp && typeof options.clockTimestamp !== "number") {
  return done(new JsonWebTokenError("clockTimestamp must be a number"));
}
// clockTolerance has NO validation at all

Impact

Any application that:

  1. Reads clockTolerance from user input, a config file, environment variable, or a database without strict validation, OR
  2. Has a dependency that passes an unvalidated clockTolerance

...is vulnerable to complete expiry bypass. An attacker who can influence the clockTolerance value can reuse tokens that expired days, months, or years ago.

This is particularly dangerous in multi-tenant systems where token verification options may be partially user-controlled.

Suggested Fix

Add an upper bound to clockTolerance. A reasonable maximum is 300 seconds (5 minutes) or at most 86400 (1 day). For example:

if (options.clockTolerance !== undefined) {
  if (typeof options.clockTolerance !== "number" || options.clockTolerance < 0) {
    return done(new JsonWebTokenError("clockTolerance must be a non-negative number"));
  }
  if (options.clockTolerance > 300) {
    return done(new JsonWebTokenError("clockTolerance must not exceed 300 seconds"));
  }
}

Alternatively, document clearly that clockTolerance must be a small value and add a warning when it exceeds a reasonable threshold.


Discovered via manual source code audit of v9.0.3.

Reported by Travis Burmaster — travis@burmaster.com

Dominant language
JavaScript
Stars
18.2k
Forks
1.3k
PR merge metrics
No merged PRs in 30d

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from auth0/node-jsonwebtoken

All issues in auth0/node-jsonwebtoken

Similar issues

More JavaScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.