flashman
← All posts

JWT clock skew: fix exp and nbf errors without guesswork

Fix JWT clock skew, expired token, and not-before errors by comparing exp, nbf, iat, server time, leeway, and deployment logs before changing auth code.

2026-07-05 · 6 min read · Rahul Chitturi

  • jwt
  • auth
  • debugging

Clock skew bugs are easy to misdiagnose because the token can be correctly signed and still fail validation. A backend that is a few minutes ahead may reject a freshly issued token as not valid yet, while a slow worker may keep accepting tokens that should be expired.

Before widening leeway or disabling lifetime checks, decode a safe test token and compare the time claims with the clocks and logs from every service involved in the request.

Read every time claim together

The exp claim tells verifiers when a token must stop being accepted. The nbf claim tells them when acceptance can begin. The iat claim records when the issuer created the token. Those values only make sense when compared with the verifier clock, issuer clock, and any gateway that checks the token first.

  • Convert exp, nbf, and iat from Unix seconds to human-readable UTC
  • Compare the converted times with the exact rejection timestamp
  • Check whether gateways and app servers use the same NTP source
  • Record configured leeway instead of assuming library defaults

Separate clock drift from environment drift

A token that works locally but fails in staging may not be a clock problem at all. It might come from a different issuer, a test tenant with shorter lifetimes, or a frontend that cached an old token after login. Diff the working and failing claims before changing server timeouts.

When only scheduled jobs fail, compare the job runtime clock with the API verifier clock. Containers, serverless workers, and long-lived virtual machines can drift in different ways when time sync is unhealthy.

Use leeway deliberately

Small leeway can absorb harmless network and clock differences. Large leeway can accidentally extend token lifetime, hide deployment issues, or make incident timelines harder to reason about. Treat leeway as a documented production setting, not a quick workaround.

  • Keep access tokens short-lived and refresh them intentionally
  • Avoid accepting tokens before nbf unless the issuer requires it
  • Alert on clock drift instead of compensating forever in auth code
  • Add tests with fixed timestamps around boundary conditions

A Flashman workflow

Decode a synthetic JWT with the JWT tool, convert each time claim with the timestamp converter, and format any verifier configuration JSON before comparing it with deployment logs. Use the diff tool to compare a passing token and failing token after redacting sensitive claims.

Keep production tokens out of tickets. If the bug needs to be shared, recreate the same exp, nbf, iat, issuer, and audience shape with placeholder values.

Try these tools