skip to content

What does JwtTimestampValidator check on an incoming JWT, and why does it allow a small clock skew by default?

level: juniorimportance: must knowfreq 62%

answer

  1. exp and nbf claims
  2. default 60s clock skew
  3. OAuth2TokenValidator<Jwt>
  4. part of JwtValidators.createDefault()
  5. replacing validator loses it

basics

~20 s

JwtTimestampValidator checks the token's time claims: it rejects tokens that are expired (exp) or not yet valid (nbf). It allows a default 60-second clock skew so tokens are not rejected just because server clocks differ slightly.

solid answer

~40 s

JwtTimestampValidator is an OAuth2TokenValidator<Jwt> that validates the temporal claims of a JWT: `exp` (expiration time) and `nbf` (not-before). If the current time is after `exp`, or before `nbf`, validation fails and the token is rejected. To tolerate small differences between the issuer's clock and the resource server's clock, it applies a configurable clock skew, defaulting to 60 seconds — so a token is only treated as expired 60 seconds past its stated `exp`. It's part of the default validator set that NimbusJwtDecoder installs automatically via JwtValidators.createDefault(), so you get expiry checking for free without wiring it yourself. You can tighten or widen the skew with `new JwtTimestampValidator(Duration.ofSeconds(30))`.

code

java · 13 lines
java
// Tighten the default 60s skew to 30s
OAuth2TokenValidator<Jwt> timestamp =
    new JwtTimestampValidator(Duration.ofSeconds(30));

// It is normally installed for you as part of the default set:
OAuth2TokenValidator<Jwt> defaults = JwtValidators.createDefault();
// -> includes JwtTimestampValidator (exp/nbf) among others

// Manual use returns success or a failure carrying an OAuth2Error:
OAuth2TokenValidatorResult result = timestamp.validate(jwt);
if (result.hasErrors()) {
    // token expired or not-yet-valid beyond the allowed skew
}

go deeper

for a junior

Know it enforces exp/nbf and allows ~60s skew, and that it's on by default.

for a middle

Should know how to tune skew and that replacing the validator drops it.

for a senior

Explains it's one member of the default set and the interplay with signature verification vs. validation.

for a principal

Reasons about clock-sync/NTP posture, skew tuning for third-party issuers, and security tradeoffs of loose skew.

## What it is `JwtTimestampValidator` is a concrete implementation of the `OAuth2TokenValidator<Jwt>` interface in Spring Security. An `OAuth2TokenValidator<T>` has a single method, `OAuth2TokenValidatorResult validate(T token)`, that returns either success or a failure carrying an `OAuth2Error`. `JwtTimestampValidator` focuses only on the **time-based claims** of a decoded JWT. ## What a JWT and its time claims are A JWT (JSON Web Token) is a signed set of claims (key/value assertions about the user/token). Two standard time claims matter here: - **`exp` (expiration time)** — the instant after which the token must be rejected. - **`nbf` (not before)** — the instant before which the token must not be accepted. Both are NumericDate values (seconds since the Unix epoch). In Spring these surface on the `Jwt` object as `jwt.getExpiresAt()` and `jwt.getNotBefore()` (`java.time.Instant`). ## The check it performs When `validate(Jwt)` runs, it compares the current time (`Clock`, default system UTC) against these claims: - If `exp` is present and **now > exp + clockSkew**, it fails with an `OAuth2Error` whose description says the token expired. - If `nbf` is present and **now < nbf - clockSkew**, it fails saying the token is not valid yet. - If a claim is absent, that particular check is simply skipped (a token with no `exp` will not be rejected by this validator on expiry grounds). ## Clock skew — why and how Distributed systems rarely have perfectly synchronized clocks. If the authorization server's clock is a few seconds ahead of the resource server's, a freshly minted token could momentarily look "not yet valid," or a just-expired token could be rejected a hair too aggressively. To smooth this over, `JwtTimestampValidator` applies a **clock skew** tolerance, **defaulting to 60 seconds**. Constructors: - `new JwtTimestampValidator()` — 60-second skew. - `new JwtTimestampValidator(Duration.ofSeconds(30))` — custom skew. - There is also a setter `setClock(Clock)` for testing with a fixed clock. ## Where it comes from automatically You usually don't construct it directly. When Spring Boot auto-configures a resource server (e.g. `spring.security.oauth2.resourceserver.jwt.issuer-uri=...`), it builds a `NimbusJwtDecoder` whose default validator is produced by `JwtValidators.createDefaultWithIssuer(issuer)` (or `createDefault()`), and that set **includes** a `JwtTimestampValidator`. So expiry/not-before enforcement is on by default. ## Gotchas - **Replacing the validator drops it.** If you call `decoder.setJwtValidator(myValidator)` with a bare custom validator, you **overwrite** the defaults and lose timestamp checking. Always combine with the defaults via `DelegatingOAuth2TokenValidator` or start from `JwtValidators.createDefault()`. - **`exp` is validated here, not by signature verification.** Signature checking (was the token really signed by the issuer) is a separate step done by the decoder; timestamp validation is a post-decode validator. - **Missing `exp` is not an error to this validator** — enforce presence elsewhere if your policy requires it. - Skew is symmetric and applies to both `exp` and `nbf`. ## When to tune it Lower the skew (e.g. 0–5s) in high-security contexts where you control clock sync via NTP and want tight expiry. Keep or raise the default when integrating with third-party issuers whose clocks you don't control.

  • If you set a custom validator with decoder.setJwtValidator(...), what happens to expiry checking?
    It is lost unless you re-include it. setJwtValidator replaces the entire validator, so you must combine your custom validator with JwtValidators.createDefault() (or a JwtTimestampValidator) using DelegatingOAuth2TokenValidator.
  • Does JwtTimestampValidator verify the token's signature?
    No. Signature verification is done by the decoder (e.g. NimbusJwtDecoder using the JWK set). JwtTimestampValidator only checks the exp/nbf time claims after the token is decoded.

saying these in an interview costs you the question

  • Thinking JwtTimestampValidator verifies the signature
  • Believing there is zero tolerance so tokens expire to the exact second
  • Not realizing a custom validator replaces (not adds to) the defaults
  • Claiming a token without an exp claim is automatically rejected by it

context