Fastify JWT Plugin Silently Issues Tokens That Never Expire
The Bottom Line CVE-2026-107275 affects @fastify/jwt, the JSON Web Token plugin for the Fastify web framework. Versions before 10.2.3 silently drop malformed time span values instead of rejecting them. The result: token…
The Bottom Line
CVE-2026-107275 affects @fastify/jwt, the JSON Web Token plugin for the Fastify web framework. Versions before 10.2.3 silently drop malformed time span values instead of rejecting them. The result: tokens that never expire, and expired tokens that still pass verification.
The vulnerability hits both the signing and verification paths. On signing, a bad expiresIn or notBefore value produces a token with no expiration claim at all. On verification, a bad maxAge value means the plugin stops enforcing token age limits entirely.
A fix exists. Version 10.2.3 resolves the issue. Teams running @fastify/jwt in production should check their version immediately and confirm any time span values they pass are actually valid.
What Happened
@fastify/jwt handles JWT signing and verification for applications built on the Fastify web framework. The plugin accepts human-readable time span strings for expiresIn, notBefore, and maxAge options, values like "15m" or "7d" that developers expect the plugin to parse and enforce.
The flaw lives in how the plugin's parser handles inputs it cannot read. Compound spans, month units, ISO 8601 durations, decimal commas, and values with surrounding whitespace all fall outside what the parser understands. Instead of throwing an error or rejecting the input, the plugin silently drops it.
That silent failure has two distinct consequences depending on where it occurs. On the signing path, a dropped expiresIn or notBefore value means the resulting token carries no expiration claim whatsoever. The token becomes permanently valid, regardless of what the developer intended.
On the verification path, the failure mode is arguably worse. A dropped maxAge value means the plugin stops enforcing any age limit on incoming tokens. A token that should have been rejected for being too old gets accepted instead, because the enforcement logic never fires.
In both cases, the application has no indication anything went wrong. There is no error, no warning, no log entry flagging the malformed input. Developers who pass a time span string that looks reasonable but falls outside the parser's supported formats get silent failure instead of a loud one.
The fix in version 10.2.3 addresses the parsing behavior directly. The plugin maintainers also documented a workaround for teams that cannot upgrade immediately.
Who Is Affected
Any application using @fastify/jwt in versions before 10.2.3 is affected, provided it passes expiresIn, notBefore, or maxAge as a time span string rather than a plain number of seconds. This covers Fastify-based Node.js applications across cloud and on-prem deployments that rely on this plugin for JWT-based authentication or session management.
The risk is highest in applications that construct time span strings dynamically, from configuration files, environment variables, or user input, where a malformed or unexpected format could slip through undetected. Applications that hardcode simple, well-formed strings like "15m" face lower practical risk, but any reliance on string parsing rather than numeric seconds leaves the door open.
Both the token-issuing side and the token-verifying side of an application are in scope. A service that only verifies tokens issued elsewhere can still be exposed if it uses maxAge with an unparseable string.
By the Numbers
CVE-2026-107275 affects @fastify/jwt versions before 10.2.3. The fix is available in version 10.2.3 and later. The plugin's parser fails to handle at least five distinct input categories: compound time spans, month units, ISO 8601 durations, decimal commas, and strings with surrounding whitespace.
Why It Matters
This is a silent failure bug, not a loud one, and that makes it dangerous. Authentication systems are supposed to fail closed, rejecting what they cannot validate. Here, the plugin fails open, issuing tokens with no expiration and accepting tokens it should reject for age.
Silent degradation of security controls is a recurring pattern in authentication libraries. A misconfigured or malformed input doesn't throw an exception that a developer would catch in testing. It just quietly removes a security boundary, and the application keeps running as if nothing changed.
Tokens with no expiration are a long-term liability. If one of those tokens leaks, through a log file, a browser history, or a compromised client, it remains valid indefinitely. There is no built-in expiration to limit the blast radius of that leak.
What To Do Now
First, upgrade @fastify/jwt to version 10.2.3 or later in every affected application. Second, audit your codebase for any expiresIn, notBefore, or maxAge values passed as strings. Third, pass these options as a plain number of seconds instead of a string wherever possible, since numeric values bypass the parser entirely. Fourth, if you must use string values, verify that each one parses to a finite value before relying on it in production. Fifth, review issued tokens for signs of missing expiration claims, particularly tokens signed before the upgrade. Sixth, rotate any JWT signing secrets if you suspect tokens without expiration have already been issued and could be in circulation. Monitor authentication logs for tokens that appear older than your intended maxAge policy, since those are the clearest indicator this bug was active in your environment.