Vulnerabilities in pkg:npm/fast-jwt

PACKAGE
Advisory Identifier: GHSA-x937-hj6v-793p
Advisory Summary

CVE-2026-107719 is a MODERATE (CVSS 4.2) verifier cache accepts expired jwts vulnerability (CWE-613: Insufficient Session Expiration) affecting fast-jwt libraries in the npm ecosystem. The issue involves verifier cache accepts expired jwts without iat.. Remediation is available by upgrading affected packages to version 6.3.4.

14 Known Advisory Record(s)
Package Vulnerability Intelligence

pkg:npm/fast-jwt

14 Known Advisory Record(s)

Vulnerability advisories affecting package pkg:npm/fast-jwt. Select an advisory below to view technical details or fixes.

Advisory ID CVE / NVD Severity Summary
GHSA-x937-hj6v-793p CVE-2026-107719 MODERATE fast-jwt: Verifier cache accepts expired JWTs without iat.
GHSA-8wpc-h4q6-8fxv CVE-2026-107720 HIGH fast-jwt: createVerifier accepts unsigned JWTs when key is '' or null and algorithms is explicitly set
GHSA-ww5h-9m49-7xx4 CVE-2026-107722 CRITICAL fast-jwt: Incomplete patch of CVE-2026-34950: Non-whitespace key-prefix re-enables RSA→HS256 algorithm confusion
GHSA-687g-22h4-j4w4 CVE-2026-107721 MODERATE fast-jwt clockTolerance: Infinity silently bypasses both exp and nbf validation (and persists in the verifier cache)
GHSA-5hjw-83fp-phq9 CVE-2026-107723 HIGH fast-jwt : Silent claim-validator bypass when JWT payload is a JSON array
GHSA-g3jj-5cmm-3hxx CVE-2026-107724 HIGH fast-jwt treats raw public JWK JSON as an HMAC secret, enabling HS256 token forgery
GHSA-gmvf-9v4p-v8jc CVE-2026-44351 CRITICAL fast-jwt: JWT auth bypass due to empty HMAC secret accepted by async key resolver
GHSA-cjw9-ghj4-fwxf CVE-2026-35041 MODERATE fast-jwt has a ReDoS when using RegExp in allowed* leading to CPU exhaustion during token verification
GHSA-3j8v-cgw4-2g6q CVE-2026-35040 MODERATE fast-jwt: Stateful RegExp (/g or /y) causes non-deterministic allowed-claim validation (logical DoS)
GHSA-rp9m-7r4c-75qg CVE-2026-35039 CRITICAL fast-jwt: Cache Confusion via cacheKeyBuilder Collisions Can Return Claims From a Different Token (Identity/Authorization Mixup)
GHSA-mvf2-f6gm-w987 CVE-2026-34950 CRITICAL fast-jwt: Incomplete fix for CVE-2023-48223: JWT Algorithm Confusion via Whitespace-Prefixed RSA Public Key
GHSA-hm7r-c7qw-ghp6 CVE-2026-35042 HIGH fast-jwt accepts unknown `crit` header extensions (RFC 7515 violation)
GHSA-gm45-q3v2-6cf8 CVE-2025-30144 MODERATE Fast-JWT Improperly Validates iss Claims
GHSA-c2ff-88x2-x9pg CVE-2023-48223 MODERATE JWT Algorithm Confusion

CVE-2026-107719 at a Glance

CVE Identifier CVE-2026-107719
GHSA Identifier GHSA-x937-hj6v-793p
Severity MODERATE
CVSS v3.1 Base Score 4.2
CWE Classification CWE-613: Insufficient Session Expiration
Affected Product fast-jwt
Package Ecosystem npm
Published Date
Last Updated

Security Impact

CVSS: 4.2

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:N).

Attack Vector Network (AV:N)
Attack Complexity High (AC:H)
Privileges Required Low (PR:L)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality Low (C:L)
Integrity Low (I:L)
Availability None (A:N)

How to Fix CVE-2026-107719

To remediate this vulnerability, upgrade all affected fast-jwt dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

fast-jwt fixed: 6.3.4
npm install fast-jwt@6.3.4
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

npm ls fast-jwt

How to Check if You Are Affected

To determine whether your Node.js application is affected by CVE-2026-107719, inspect your project dependencies using npm:

npm ls fast-jwt

Analyze the terminal output to locate any version of fast-jwt in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 6.3.4), your application is affected.

Note: The npm ls fast-jwt command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-613: Insufficient Session Expiration, where according to wasc, "insufficient session expiration is when a web site permits an attacker to reuse old session credentials or session ids for authorization.". In fast-jwt, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

### Summary `cacheSet` only derives its `exp` cache deadline inside `hasIat` (`src/verifier.js:127-140`). JWT `iat` is optional. For a valid token with `exp` but no `iat`, `cacheSet` substitutes `clockTimestamp + clockTolerance + cacheTTL` (`src/verifier.js:142-146`). Later, the cache path returns the saved payload before decoding or invoking `verifyToken` (`src/verifier.js:372-388`). Since expiration validation is in `verifyToken`, the expired token remains accepted until the cache deadline. ### Details The bug is an expiration-check bypass caused by caching. Normally, verification works like this: 1. Verify signature. 2. Check exp against the current time. 3. Return the JWT claims. With caching enabled, `fast-jwt` saves successful verification results so it can avoid repeating crypto work for the same token. The intended invariant is: > A cached token must stop being accepted at the same time as a non-cached token. But the cache computes its expiration incorrectly. In `fast-jwt/src/verifier.js:127`, the code first checks whether the payload has an iat (“issued at”) claim: ```js const hasIat = payload && typeof payload.iat === 'number' if (hasIat) { if (!ignoreExpiration && typeof payload.exp === 'number') { cacheValue[2] = payload.exp * 1000 + clockTolerance } } ``` That means `exp` is considered only when `iat` exists. However, `iat` is optional in JWT. A perfectly valid JWT may contain: ```js { "sub": "alice", "exp": 1700000001 } ``` with no iat. For that token, the expiration-based cache deadline is never set. The code falls back to the generic cache TTL—10 minutes by default: ```js const maxTTL = clockTimestamp + clockTolerance + cacheTTL cacheValue[2] = cacheValue[2] === 0 ? maxTTL : Math.min(cacheValue[2], maxTTL) ``` Later, cache lookup happens before decoding and expiration validation: ```js const [value, min, max] = cache.get(cacheKeyBuilder(token)) || [undefined, 0, 0] if (typeof value !== 'undefined' && (max === 0 || now <= max)) { return handleCachedResult(value, callback, promise) } ``` So the timeline is: ``` 12:00:00 Token has exp = 12:00:01, no iat. 12:00:00 Server verifies it successfully and caches its claims. 12:00:01 Token expires. 12:00:02 Attacker replays the identical token. 12:00:02 Cache hit returns old claims; exp is never rechecked. 12:10:00 Cache TTL finally ends; normal expiration rejection resumes. ``` An attacker cannot forge a token through this issue. They need a valid token first; typically their own, or a stolen bearer token. But they can keep using it after its intended expiration if all of these are true: - The application enables `cache`. - The JWT has `exp` but no `iat`. - The token was cached before expiry. - It is replayed before the cache entry expires. Impact depends on the application. For a short-lived access token, it can extend access by up to the configured `cacheTTL` of 10 minutes by default, or more if the application configured it that way. This undermines expiry as an authentication/session boundary. The minimal repair is to calculate the cache deadline from `exp` independently of `iat`. `iat` should only matter for `maxAge`, because max age is inherently defined relative to issuance time. ### PoC ```js const assert = require('node:assert/strict') const { createSigner, createVerifier } = require('fast-jwt') const key = 'audit-only-secret' const originalNow = Date.now try { const initialNow = 1_700_000_000_000 const exp = Math.floor(initialNow / 1000) + 1 // expires one second later // noTimestamp intentionally omits iat, while exp is retained. const sign = createSigner({ key, algorithm: 'HS256', noTimestamp: true }) const token = sign({ sub: 'alice', exp }) const verify = createVerifier({ key, algorithms: ['HS256'], cache: true, cacheTTL: 60_000 }) Date.now = () => initialNow assert.equal(verify(token).sub, 'alice') // Valid; stores cache entry. Date.now = () => exp * 1000 + 1 assert.equal(verify(token).sub, 'alice') // BUG: should throw FAST_JWT_EXPIRED. console.log('VULNERABLE: expired exp-without-iat token served from cache') } finally { Date.now = originalNow } ``` ### Impact This is an authentication/session-expiration bypass caused by incorrect cache validation. The following are impacted: - Applications using the affected fast-jwt code with cache: true. - Applications that rely on JWT exp to end sessions or limit bearer-token lifetime. - Tokens with exp but no iat, after they were successfully verified and cached.

CVE-2026-107719 Affected fast-jwt Versions

1 component(s)
Fixed Versions:
6.3.4
Product / Package Ecosystem Affected Versions Fixed Version
fast-jwt
npm
< 6.3.4
6.3.4

References

Frequently Asked Questions

What is CVE-2026-107719?
CVE-2026-107719 is a security vulnerability in fast-jwt involving Verifier cache accepts expired JWTs without iat. under CWE-613: Insufficient Session Expiration.
Which fast-jwt versions are affected?
Affected versions include releases prior to 6.3.4 across affected packages: fast-jwt.
What version fixes CVE-2026-107719?
Upgrading to 6.3.4 (or the latest patched release for your respective branch) resolves the vulnerability.
Is fast-jwt affected?
Yes, fast-jwt is affected in releases prior to 6.3.4. Upgrade your dependency to 6.3.4 or later.
What is the CVSS score of CVE-2026-107719?
CVE-2026-107719 has a CVSS v3.1 base score of 4.2 (MODERATE) with vector string CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:N.
What CWE is associated with CVE-2026-107719?
CVE-2026-107719 is classified under CWE-613: Insufficient Session Expiration (According to WASC, "Insufficient Session Expiration is when a web site permits an attacker to reuse old session credentials or session IDs for authorization.").
How can I check npm dependencies for CVE-2026-107719?
Run the dependency tree command: 'npm ls fast-jwt'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-x937-hj6v-793p",
  "modified" : "2026-10-08T22:10:35Z",
  "published" : "2026-10-08T22:10:35Z",
  "aliases" : [ "CVE-2026-107719" ],
  "summary" : "fast-jwt: Verifier cache accepts expired JWTs without iat.",
  "details" : "### Summary\n`cacheSet` only derives its `exp` cache deadline inside `hasIat` (`src/verifier.js:127-140`). JWT `iat` is optional. For a valid token with `exp` but no `iat`, `cacheSet` substitutes `clockTimestamp + clockTolerance + cacheTTL` (`src/verifier.js:142-146`). Later, the cache path returns the saved payload before decoding or invoking `verifyToken` (`src/verifier.js:372-388`). Since expiration validation is in `verifyToken`, the expired token remains accepted until the cache deadline.\n\n### Details\nThe bug is an expiration-check bypass caused by caching.\n\nNormally, verification works like this:\n\n  1. Verify signature.\n  2. Check exp against the current time.\n  3. Return the JWT claims.\n\nWith caching enabled, `fast-jwt` saves successful verification results so it can avoid repeating crypto work for the same token.\n\nThe intended invariant is:\n\n  > A cached token must stop being accepted at the same time as a non-cached token.\n\nBut the cache computes its expiration incorrectly.\n\nIn `fast-jwt/src/verifier.js:127`, the code first checks whether the payload has an iat (“issued at”) claim:\n\n```js\nconst hasIat = payload && typeof payload.iat === 'number'\n\nif (hasIat) {\n  if (!ignoreExpiration && typeof payload.exp === 'number') {\n    cacheValue[2] = payload.exp * 1000 + clockTolerance\n  }\n}\n```\n\nThat means `exp` is considered only when `iat` exists.\n\nHowever, `iat` is optional in JWT. A perfectly valid JWT may contain:\n\n```js\n{\n    \"sub\": \"alice\",\n    \"exp\": 1700000001\n}\n```\nwith no iat.\n\nFor that token, the expiration-based cache deadline is never set. The code falls back to the generic cache TTL—10 minutes by default:\n\n```js\nconst maxTTL = clockTimestamp + clockTolerance + cacheTTL\ncacheValue[2] = cacheValue[2] === 0 ? maxTTL : Math.min(cacheValue[2], maxTTL)\n```\n\nLater, cache lookup happens before decoding and expiration validation:\n\n```js\nconst [value, min, max] = cache.get(cacheKeyBuilder(token)) || [undefined, 0, 0]\n\nif (typeof value !== 'undefined' && (max === 0 || now <= max)) {\n  return handleCachedResult(value, callback, promise)\n}\n```\n\nSo the timeline is:\n\n```\n  12:00:00  Token has exp = 12:00:01, no iat.\n  12:00:00  Server verifies it successfully and caches its claims.\n  12:00:01  Token expires.\n  12:00:02  Attacker replays the identical token.\n  12:00:02  Cache hit returns old claims; exp is never rechecked.\n  12:10:00  Cache TTL finally ends; normal expiration rejection resumes.\n```\n\nAn attacker cannot forge a token through this issue. They need a valid token first; typically their own, or a stolen bearer token. But they can keep using it after its intended expiration if all of these are true:\n\n  - The application enables `cache`.\n  - The JWT has `exp` but no `iat`.\n  - The token was cached before expiry.\n  - It is replayed before the cache entry expires.\n\nImpact depends on the application. For a short-lived access token, it can extend access by up to the configured `cacheTTL` of 10 minutes by default, or more if the application configured it that way. This undermines expiry as an authentication/session boundary.\n\nThe minimal repair is to calculate the cache deadline from `exp` independently of `iat`. `iat` should only matter for `maxAge`, because max age is inherently defined relative to issuance time.\n\n### PoC\n```js\nconst assert = require('node:assert/strict')\nconst { createSigner, createVerifier } = require('fast-jwt')\n\nconst key = 'audit-only-secret'\nconst originalNow = Date.now\n\ntry {\n  const initialNow = 1_700_000_000_000\n  const exp = Math.floor(initialNow / 1000) + 1 // expires one second later\n\n  // noTimestamp intentionally omits iat, while exp is retained.\n  const sign = createSigner({\n    key,\n    algorithm: 'HS256',\n    noTimestamp: true\n  })\n  const token = sign({ sub: 'alice', exp })\n\n  const verify = createVerifier({\n    key,\n    algorithms: ['HS256'],\n    cache: true,\n    cacheTTL: 60_000\n  })\n\n  Date.now = () => initialNow\n  assert.equal(verify(token).sub, 'alice') // Valid; stores cache entry.\n\n  Date.now = () => exp * 1000 + 1\n  assert.equal(verify(token).sub, 'alice') // BUG: should throw FAST_JWT_EXPIRED.\n\n  console.log('VULNERABLE: expired exp-without-iat token served from cache')\n} finally {\n  Date.now = originalNow\n}\n```\n\n### Impact\nThis is an authentication/session-expiration bypass caused by incorrect cache validation.\nThe following are impacted:\n  - Applications using the affected fast-jwt code with cache: true.\n  - Applications that rely on JWT exp to end sessions or limit bearer-token lifetime.\n  - Tokens with exp but no iat, after they were successfully verified and cached.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "npm",
      "name" : "fast-jwt"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "6.3.4"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 6.3.3"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/nearform/fast-jwt/security/advisories/GHSA-x937-hj6v-793p"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/nearform/fast-jwt/commit/fc1ddbbe5ce38066ba2cea0b0ce0233932757167"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/nearform/fast-jwt"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/nearform/fast-jwt/releases/tag/v6.3.4"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-613" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-10-08T22:10:35Z",
    "nvd_published_at" : null
  }
}