{"record":{"id":"4a180e7b5ef2f7e9","repo":"spring-projects/spring-security","slug":"cookie-token-1-has-expired-expired-on-expiryda","errorCode":null,"errorMessage":"Cookie token[1] has expired (expired on '<expiryDate>'; current time is '<now>')","messagePattern":"Cookie token\\[1\\] has expired \\(expired on '<expiryDate>'; current time is '<now>'\\)","errorType":"exception","errorClass":"InvalidCookieException","httpStatus":null,"severity":"warning","filePath":"web/src/main/java/org/springframework/security/web/authentication/rememberme/TokenBasedRememberMeServices.java","lineNumber":134,"sourceCode":"\t * @since 5.8\n\t */\n\tpublic TokenBasedRememberMeServices(String key, UserDetailsService userDetailsService,\n\t\t\tRememberMeTokenAlgorithm encodingAlgorithm) {\n\t\tsuper(key, userDetailsService);\n\t\tAssert.notNull(encodingAlgorithm, \"encodingAlgorithm cannot be null\");\n\t\tthis.encodingAlgorithm = encodingAlgorithm;\n\t}\n\n\t@Override\n\tprotected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,\n\t\t\tHttpServletResponse response) {\n\t\tif (!isValidCookieTokensLength(cookieTokens)) {\n\t\t\tthrow new InvalidCookieException(\n\t\t\t\t\t\"Cookie token did not contain 3 or 4 tokens, but contained '\" + Arrays.asList(cookieTokens) + \"'\");\n\t\t}\n\t\tlong tokenExpiryTime = getTokenExpiryTime(cookieTokens);\n\t\tif (isTokenExpired(tokenExpiryTime)) {\n\t\t\tthrow new InvalidCookieException(\"Cookie token[1] has expired (expired on '\" + new Date(tokenExpiryTime)\n\t\t\t\t\t+ \"'; current time is '\" + new Date() + \"')\");\n\t\t}\n\t\t// Check the user exists. Defer lookup until after expiry time checked, to\n\t\t// possibly avoid expensive database call.\n\t\tUserDetails userDetails = getUserDetailsService().loadUserByUsername(cookieTokens[0]);\n\t\tAssert.notNull(userDetails, () -> \"UserDetailsService \" + getUserDetailsService()\n\t\t\t\t+ \" returned null for username \" + cookieTokens[0] + \". \" + \"This is an interface contract violation\");\n\t\t// Check signature of token matches remaining details. Must do this after user\n\t\t// lookup, as we need the DAO-derived password. If efficiency was a major issue,\n\t\t// just add in a UserCache implementation, but recall that this method is usually\n\t\t// only called once per HttpSession - if the token is valid, it will cause\n\t\t// SecurityContextHolder population, whilst if invalid, will cause the cookie to\n\t\t// be cancelled.\n\t\tString actualTokenSignature = cookieTokens[2];\n\t\tRememberMeTokenAlgorithm actualAlgorithm = this.matchingAlgorithm;\n\t\t// If the cookie value contains the algorithm, we use that algorithm to check the\n\t\t// signature\n\t\tif (cookieTokens.length == 4) {","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/web/src/main/java/org/springframework/security/web/authentication/rememberme/TokenBasedRememberMeServices.java#L116-L152","documentation":"The second token in the remember-me cookie is the expiry timestamp in epoch millis. TokenBasedRememberMeServices compares it with the current time; if the expiry is in the past it throws InvalidCookieException with both dates. This is the standard expired remember-me cookie condition, forcing a full re-authentication.","triggerScenarios":"processAutoLoginCookie decodes a cookie whose tokenValiditySeconds window (default 14 days) has elapsed — isTokenExpired(tokenExpiryTime) returns true and the cookie is rejected.","commonSituations":"User returns after the configured tokenValiditySeconds period; server clock skew between nodes making cookies appear expired; very short validity configured while expecting longer sessions; expired cookie kept by browser and replayed.","solutions":["Have the user log in again — expected behavior for expired cookies","Increase tokenValiditySeconds in the rememberMe() configuration if sessions expire too soon","Verify all servers (and DB time if applicable) use NTP-synced clocks","Remember expiry is absolute from login, not sliding; use persistent tokens if sliding sessions are needed"],"exampleFix":"// before\nhttp.rememberMe(r -> r.key(\"secret\")); // default 14 days\n// after\nhttp.rememberMe(r -> r.key(\"secret\").tokenValiditySeconds(60 * 60 * 24 * 30)); // 30 days","handlingStrategy":"try-catch","validationCode":"long expiry = Long.parseLong(parts[1]);\nif (expiry <= System.currentTimeMillis()) {\n    // cookie already expired client-side; go straight to login\n    response.sendRedirect(\"/login\");\n    return;\n}","typeGuard":"boolean isRememberMeCookieExpired(String[] cookieTokens) {\n    return Long.parseLong(cookieTokens[1]) <= System.currentTimeMillis();\n}","tryCatchPattern":"try {\n    UserDetails u = rememberMeServices.autoLogin(request, response);\n} catch (InvalidCookieException e) {\n    if (e.getMessage().contains(\"has expired\")) {\n        response.sendRedirect(\"/login?expired=true\");\n        return;\n    }\n    throw e;\n}","preventionTips":["Set tokenValiditySeconds appropriate to user expectations","Keep server clocks NTP-synced in clusters","Redirect to login with an 'expired' hint for better UX","Use persistent tokens if you need longer-lived auto-login"],"tags":["remember-me","spring-security","cookie","expired"],"backgroundTag":"jwt-token-expired","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}