{"id":"CVE-2026-110024","kind":"vulnerability","aliases":["CVE-2026-110024"],"related":[],"title":"@fastify/jwt: per-call options drop global expiry, so tokens never expire","description":"@fastify/jwt is a JWT utility plugin for Fastify. In versions prior to 10.2.5, the top-level sign and verify methods replace the globally configured defaults with the per-call options instead of merging them, so passing any per-call option silently drops every global default that the call does not restate, including temporal protections such as token expiration and maximum accepted token age. A token signed this way carries no expiration claim and never expires, and verification stops enforcing a configured maximum age, so stale or never-expiring tokens are accepted. The route-level decorators for signing and verifying already merge options and are not affected. The issue is fixed in @fastify/jwt 10.2.5, and users should upgrade to 10.2.5 or later.","summary":"Versions of @fastify/jwt before 10.2.5 replace the global sign and verify defaults with per-call options instead of merging them. An attacker can then present tokens that were issued without an expiration or that exceed the configured maximum age, and they are accepted.","severity":"medium","cvss":6.8,"epss":null,"exploited":false,"exploited_at":null,"products":[],"ecosystems":[],"tags":["auth","fastify","jwt","node","package"],"not_affected_if":["no-global-expiry-defaults","route-level-decorators-only"],"how_to_check":"- Run `npm ls @fastify/jwt` (or `pnpm why @fastify/jwt`) to find the installed version.\n- Search the code for calls to top-level `fastify.jwt.sign(` or `fastify.jwt.verify(` that pass an options object, and check whether the app also sets global defaults such as `expiresIn` or `maxAge` at registration.","fix":"- Upgrade `@fastify/jwt` to `10.2.5` or later.\n- Until upgraded, pass the full set of expiry options on every top-level `sign` and `verify` call, or use the route-level decorators, which already merge options.","status":"open","published_at":"2026-10-18T15:16:51+00:00","first_seen_at":"2026-10-23T21:57:15+00:00","triaged_at":"2026-10-25T03:44:55+00:00","updated_at":"2026-10-25T03:44:55+00:00","category":"package","confidence":0.6,"triage":{"at":"2026-10-25T03:44:55Z","model":"claude-haiku-5-5","output":{"fix":"- Upgrade `@fastify/jwt` to `10.2.5` or later.\n- Until upgraded, pass the full set of expiry options on every top-level `sign` and `verify` call, or use the route-level decorators, which already merge options.","reach":"network","title":"@fastify/jwt: per-call options drop global expiry, so tokens never expire","impact":"medium","summary":"Versions of @fastify/jwt before 10.2.5 replace the global sign and verify defaults with per-call options instead of merging them. An attacker can then present tokens that were issued without an expiration or that exceed the configured maximum age, and they are accepted.","category":"package","nicknames":[],"who_cares":["fastify","node","jwt","auth"],"confidence":0.6,"needs_auth":"unknown","worst_case":"Expired or never-expiring tokens are accepted as valid authentication","analyst_note":"","how_to_check":"- Run `npm ls @fastify/jwt` (or `pnpm why @fastify/jwt`) to find the installed version.\n- Search the code for calls to top-level `fastify.jwt.sign(` or `fastify.jwt.verify(` that pass an options object, and check whether the app also sets global defaults such as `expiresIn` or `maxAge` at registration.","needs_analyst":false,"not_affected_if":["route-level-decorators-only","no-global-expiry-defaults"],"who_is_affected":"Applications that use the top-level sign and verify methods of @fastify/jwt and that rely on globally configured expiration or maximum token age. Exposure requires that a per-call option is passed, since that is what drops the defaults. Route-level decorators are not affected, and teams that only use those can skip it.","user_interaction":"no","needs_special_setup":"yes","severity_if_unknown":"medium"},"cost_usd":0.000655,"input_tokens":3437,"output_tokens":622,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-110024':15A,18A '-2026':14A,17A '10.2.5':28B,80C,185C,191C 'accept':67B,131C,162C 'affect':178C 'age':63B,133C,153C 'alreadi':172C 'attack':46B 'auth':19B 'call':4A,39B,98C,109C,118C 'carri':139C 'claim':142C 'configur':61B,92C,151C 'cve':13A,16A 'decor':167C 'default':35B,93C,115C 'drop':6A,112C 'enforc':149C 'everi':113C 'exceed':59B 'expir':12A,56B,128C,141C,145C,159C 'expiri':8A 'fastifi':20B,75C 'fastify/jwt':1A,26B,68C,184C 'fix':182C 'global':7A,31B,91C,114C 'includ':122C 'instead':41B,100C 'issu':53B,180C 'jwt':21B,71C 'later':193C 'level':84C,166C 'maximum':62B,130C,152C 'merg':43B,102C,173C 'method':88C 'never':11A,144C,158C 'never-expir':157C 'node':22B 'option':5A,40B,99C,110C,174C 'packag':23B 'pass':105C 'per':3A,38B,97C,108C 'per-cal':2A,37B,96C,107C 'plugin':73C 'present':49B 'prior':78C 'protect':124C 'replac':29B,89C 'restat':121C 'rout':165C 'route-level':164C 'sign':32B,85C,136C,169C 'silent':111C 'stale':155C 'stop':148C 'tempor':123C 'token':10A,50B,127C,132C,135C,160C 'top':83C 'top-level':82C 'upgrad':189C 'user':187C 'util':72C 'verif':147C 'verifi':34B,87C,171C 'version':24B,77C 'way':138C 'without':54B","nicknames":[],"mitigations":[],"who_affected":"Applications that use the top-level sign and verify methods of @fastify/jwt and that rely on globally configured expiration or maximum token age. Exposure requires that a per-call option is passed, since that is what drops the defaults. Route-level decorators are not affected, and teams that only use those can skip it.","tier":"C","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-18T15:16:51+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N","cvss_by":"ce714d77-add3-4f53-aff5-83d477b104bb","poc_count":0,"sources":[{"source":"nvd","external_id":"CVE-2026-110024","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-110024","severity":null,"published_at":"2026-10-18T15:16:51+00:00","modified_at":"2026-10-20T19:17:29+00:00","first_seen_at":"2026-10-23T21:57:15+00:00","fetched_at":"2026-10-23T22:28:56+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}