{"record":{"id":"4cc63668a8ded1b6","repo":"passbolt/passbolt_api","slug":"expired-refresh-token-provided","errorCode":null,"errorMessage":"Expired refresh token provided.","messagePattern":"Expired refresh token provided\\.","errorType":"http","errorClass":"ExpiredRefreshTokenAccessException","httpStatus":400,"severity":"error","filePath":"plugins/PassboltCe/JwtAuthentication/src/Service/RefreshToken/RefreshTokenAbstractService.php","lineNumber":220,"sourceCode":"        return $refreshToken;\n    }\n\n    /**\n     * @param \\App\\Model\\Entity\\AuthenticationToken $refreshToken Refresh token\n     * @return void\n     * @throws \\Passbolt\\JwtAuthentication\\Error\\Exception\\RefreshToken\\ConsumedRefreshTokenAccessException if the token was already consumed\n     * @throws \\Passbolt\\JwtAuthentication\\Error\\Exception\\RefreshToken\\ExpiredRefreshTokenAccessException if the token is expired\n     */\n    public function throwSecurityExceptionsOnInvalidRefreshToken(AuthenticationToken $refreshToken): void\n    {\n        if ($refreshToken->isNotActive()) {\n            throw new ConsumedRefreshTokenAccessException(\n                __('The refresh token provided was already used.')\n            );\n        }\n\n        if ($refreshToken->isExpired()) {\n            throw new ExpiredRefreshTokenAccessException(\n                __('Expired refresh token provided.')\n            );\n        }\n    }\n}\n","sourceCodeStart":202,"sourceCodeEnd":226,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltCe/JwtAuthentication/src/Service/RefreshToken/RefreshTokenAbstractService.php#L202-L226","documentation":"throwSecurityExceptionsOnInvalidRefreshToken raises ExpiredRefreshTokenAccessException when the refresh token entity is past its expiry date (isExpired()). Refresh tokens carry a finite lifetime (check_expiry_date on the authentication token); once elapsed the token must not be accepted and the client must re-authenticate.","triggerScenarios":"Presenting a refresh token whose check_expiry_date is in the past — long-lived sessions without activity, tokens issued before an expiry policy change, or clock skew making the token appear expired.","commonSituations":"A user returning after the refresh token lifetime elapsed; servers with misconfigured timezones/clocks; load balancers with clock drift comparing expiry differently; extending session lifetimes requires re-login.","solutions":["Re-authenticate (username/password or GPG auth) to mint a fresh token pair; expiry is by design not extendable client-side.","Refresh tokens proactively before their expiry (e.g. refresh on a schedule well before check_expiry_date).","Check server clock/NTP sync if tokens expire earlier than expected.","If the deployment needs longer sessions, increase the refresh token expiry configuration server-side and reissue tokens."],"exampleFix":"// before\nsetInterval(() => refresh(store.refreshToken), 24*3600*1000); // interval longer than token lifetime\n// after\nconst lifetimeMs = new Date(store.refreshTokenExpiry) - Date.now();\nsetTimeout(() => refresh(store.refreshToken), Math.max(0, lifetimeMs - 60_000)); // refresh 1 min early","handlingStrategy":"try-catch","validationCode":"const expiresAtMs = new Date(storedRefreshTokenExpiry).getTime();\nif (!Number.isFinite(expiresAtMs) || expiresAtMs <= Date.now() + 60_000) await reauthenticate(); // refresh before expiry margin","typeGuard":"function isRefreshable(tok: {token: string, expiry: string|number}): boolean {\n  const ms = new Date(tok.expiry).getTime();\n  return Number.isFinite(ms) && ms > Date.now() + 60_000;\n}","tryCatchPattern":"try {\n  $tokens = $service->renewToken($token, $userId);\n} catch (ExpiredRefreshTokenAccessException $e) {\n  redirectToLogin(); // expired: full re-authentication required\n}","preventionTips":["Schedule refreshes ahead of expiry (e.g. 1-5 min margin)","Keep server clocks NTP-synced to avoid premature expiry detection","On session resume, check stored expiry before calling the API","Increase refresh token TTL in config if users regularly hit the limit"],"tags":["jwt","refresh-token","expired","authentication"],"backgroundTag":"jwt-token-expired","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}