Skip to content

Bug: Incorrect Handling of Timestamps in exp, nbf, and iat Claims in fast-jwt #549

@rajibchy

Description

@rajibchy

Description

There is an issue in fast-jwt where the exp, nbf, and iat claims are incorrectly calculated when expiresIn is provided in seconds. Specifically, the issue arises when payload.iat is in milliseconds, but expiresIn is in seconds, which results in incorrect expiration calculations.

Steps to Reproduce

  1. Create a JWT with expiresIn in seconds.
  2. Ensure that payload.iat is set (or not set) and potentially in milliseconds.
  3. Observe that the exp and nbf values are incorrectly calculated if iat is in milliseconds.

Expected Behavior

  • The exp, nbf, and iat claims should all be calculated correctly with consistent units (typically in seconds).
  • The calculation should correctly handle cases where iat is in milliseconds.

Actual Behavior

  • When payload.iat is in milliseconds, and expiresIn is given in seconds, the exp (expiration) and nbf (not before) values are incorrectly calculated due to a mismatch in units.

Code Snippet

The issue is occurring in the following code:

const finalPayload = {
    ...payload,
    ...fixedPayload,
    iat: noTimestamp ? undefined : Math.floor(iat / 1000),  // this line
    exp: payload.exp ? payload.exp : expiresIn ? Math.floor((iat + expiresIn) / 1000) : undefined,  // this line
    nbf: payload.nbf ? payload.nbf : notBefore ? Math.floor((iat + notBefore) / 1000) : undefined  // this line
}

Suggested Fix

  • To solve the issue of inconsistent timestamp units, the iat should be converted to seconds before performing calculations for exp and nbf. Here’s the corrected solution:
const iat = payload.iat * 1000 || clockTimestamp || Date.now(); // Handle `iat` in milliseconds
const iatSec = (iat / 1000); // Convert `iat` to seconds

const finalPayload = {
    ...payload,
    ...fixedPayload,
    iat: noTimestamp ? undefined : Math.floor(iatSec),
    exp: payload.exp ? payload.exp : expiresIn ? Math.floor(iatSec + expiresIn) : undefined,
    nbf: payload.nbf ? payload.nbf : notBefore ? Math.floor(iatSec + notBefore) : undefined
}

Explanation:

  • iat is originally in milliseconds (as it's calculated by Date.now()), so it is first converted to seconds (iatSec).
  • The exp (expiration) and nbf (not before) are then calculated using iatSec, ensuring all timestamp calculations are in seconds.
  • This fix resolves the inconsistency in units between expiresIn (in seconds) and iat (in milliseconds).

Environment

  • fast-jwt version: 5.0.5
  • Node.js version: v20.9.0
  • OS: Windows X64

Additional Information

  • The issue may not be immediately apparent unless the iat is not manually set and the expiresIn is in seconds.
  • This issue leads to incorrect expiration times if the iat is in milliseconds.

Link to Source Code

You can find the relevant code in the fast-jwt repository here: fast-jwt Signer.js - Line 93.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingjavascriptPull requests that update Javascript code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions