2026-09-18 · 8 min read
- jwt
- time
- authentication
JWT time claims use NumericDate, a count of seconds from the Unix epoch that can represent non-integer values. Many issuers intentionally emit whole seconds, while parsers and verifiers differ in number representation and fractional handling.
Reliable validation requires a token profile that defines accepted claims, units, precision, comparisons, clock source, and leeway. It also requires signature and claim validation; converting exp to a readable date is not authentication.
Normalize units before dates
Keep the decoded JSON value visible and identify its unit before passing it to a date constructor. A Unix-millisecond integer mistaken for NumericDate seconds can appear tens of thousands of years in the future.
- Require JSON numbers for standard NumericDate claims.
- Reject non-finite, malformed, or out-of-profile values.
- Document whether fractional seconds are accepted.
- Use bounded ranges appropriate to the application.
Define exact boundary comparisons
Expiration defines a point after which the token must not be accepted, while not-before defines a point before which it must not be accepted. Use the verifier library's documented semantics and regression tests at equality rather than relying on informal wording.
Keep comparison precision consistent. Truncating the current time but preserving a fractional claim, or rounding differently in two runtimes, can disagree within the final second.
Use clocks and leeway deliberately
Production validation should use a stable system clock synchronized through normal infrastructure. Tests should inject or freeze time so exact boundaries do not depend on a moving wall clock.
Set small leeway from measured clock uncertainty and expected transport delay. Do not use it to compensate for milliseconds errors, stale caches, unexpectedly long token lifetimes, or unsynchronized hosts.
Publish cross-runtime vectors
Use Flashman's JWT decoder for synthetic tokens, timestamp converter for NumericDate values, JSON formatter to preserve number shape, diff for verifier settings, and units converter to expose seconds-versus-milliseconds mistakes.
Test whole and fractional values, exp and nbf equality, leeway edges, negative and far-future times, milliseconds input, JSON round trips, large-number precision, malformed types, clock adjustments, issuer and audience failures, algorithm rejection, and every supported producer-verifier pair.