{"record":{"id":"c1f7bed65ae25840","repo":"lcobucci/jwt","slug":"the-token-was-issued-in-the-future-strictvalidat","errorCode":null,"errorMessage":"The token was issued in the future","messagePattern":"The token was issued in the future","errorType":"validation","errorClass":"ConstraintViolation","httpStatus":null,"severity":"error","filePath":"src/Validation/Constraint/StrictValidAt.php","lineNumber":81,"sourceCode":"    {\n        if (! $token->claims()->has(Token\\RegisteredClaims::NOT_BEFORE)) {\n            throw ConstraintViolation::error('\"Not Before\" claim missing', $this);\n        }\n\n        if (! $token->isMinimumTimeBefore($now)) {\n            throw ConstraintViolation::error('The token cannot be used yet', $this);\n        }\n    }\n\n    /** @throws ConstraintViolation */\n    private function assertIssueTime(UnencryptedToken $token, DateTimeInterface $now): void\n    {\n        if (! $token->claims()->has(Token\\RegisteredClaims::ISSUED_AT)) {\n            throw ConstraintViolation::error('\"Issued At\" claim missing', $this);\n        }\n\n        if (! $token->hasBeenIssuedBefore($now)) {\n            throw ConstraintViolation::error('The token was issued in the future', $this);\n        }\n    }\n}\n","sourceCodeStart":63,"sourceCodeEnd":85,"githubUrl":"https://github.com/lcobucci/jwt/blob/375813049c24c7111bda8b6884c57b071ceb2fe7/src/Validation/Constraint/StrictValidAt.php#L63-L85","documentation":"This error comes from lcobucci/jwt's StrictValidAt validation constraint. When a token is validated with the constraint (via Validator::assert), it checks the 'iat' (Issued At) claim against the current clock using Token::hasBeenIssuedBefore(). If the iat timestamp is later than 'now' — even by one second — the library rejects the token because it could not have been issued yet, which usually means clock skew between the issuer and validator or a bad clock on the machine.","triggerScenarios":"Running Validator->assert($token, new StrictValidAt($clock)) where the token's iat claim is a Unix timestamp greater than the clock's current time. Typical concrete calls: validating a JWT received from another server/service whose clock is a few seconds or minutes ahead, or a token minted with a hand-built iat set incorrectly in the future (e.g. using milliseconds instead of seconds, or a wrong timezone conversion).","commonSituations":"Distributed systems with unsynchronized NTP clocks between the token issuer (auth server) and validator (API server); local development where the machine clock drifted; test fixtures that hardcode iat timestamps in the future; microservices where the token was just generated and iat equals the remote clock's 'now' while the local clock lags behind.","solutions":["Synchronize clocks on both issuer and validator machines with NTP (e.g. enable timed/chrony, 'sudo timedatectl set-ntp true') so iat is never in the future relative to the validator.","If small skew is unavoidable, allow leeway: pass a DateInterval to the constraint, e.g. new StrictValidAt(SystemClock::fromUTC(), new DateInterval('PT30S')), which tolerates tokens issued up to 30s in the future.","Fix the token generation code that sets iat — ensure it uses the current time in seconds (new DateTimeImmutable('@' . time()) or simply new DateTimeImmutable('now')) and not milliseconds or a future date.","Catch RequiredConstraintsViolated and reject/refresh the token instead of crashing, logging the iat vs now values to diagnose persistent skew."],"exampleFix":"// before\n$validator->assert($token, new StrictValidAt($clock)); // throws when iat is a few seconds ahead\n\n// after — tolerate up to 30 seconds of clock skew\n$validator->assert(\n    $token,\n    new StrictValidAt(SystemClock::fromUTC(), new DateInterval('PT30S'))\n);","handlingStrategy":"try-catch","validationCode":"// before asserting, sanity-check the iat claim yourself\n$claims = $token->claims();\n$leeway = 30;\nif ($claims->has('iat')) {\n    $iat = $claims->get('iat');\n    $now = new DateTimeImmutable('now', new DateTimeZone('UTC'));\n    if ($iat->getTimestamp() > $now->getTimestamp() + $leeway) {\n        // token issued in the future: fix clocks or reject early\n    }\n}","typeGuard":"function hasFutureIat(Lcobucci\\JWT\\Token $token, int $leeway = 30): bool\n{\n    if (!$token->claims()->has('iat')) {\n        return false;\n    }\n    $iat = $token->claims()->get('iat');\n    return $iat->getTimestamp() > (new DateTimeImmutable())->getTimestamp() + $leeway;\n}","tryCatchPattern":"use Lcobucci\\JWT\\Validation\\RequiredConstraintsViolated;\n\ntry {\n    $validator->assert($token, new StrictValidAt($clock, new DateInterval('PT30S')));\n} catch (RequiredConstraintsViolated $e) {\n    foreach ($e->getViolations() as $v) {\n        if ($v->getMessage() === 'The token was issued in the future') {\n            // handle clock skew: refresh token or resync NTP\n        }\n    }\n}","preventionTips":["Run NTP (chrony/timed) on every server that issues or validates tokens.","Always configure a leeway DateInterval (15–60s) in StrictValidAt/ValidAt.","Never hand-build iat claims; use the library's Builder->issuedAt(new DateTimeImmutable('now')) and keep timestamps in seconds, not milliseconds.","In tests, generate tokens relative to a frozen clock (setTestNow) consistent with the validation clock."],"tags":["jwt","validation","clock-skew","php"],"backgroundTag":"jwt-token-expired","analyzedSha":"375813049c24c7111bda8b6884c57b071ceb2fe7","analyzedAt":"2026-09-14T11:12:28.004Z","contentChangedAt":"2026-09-14T11:12:28.004Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}