spring-projects/spring-security · info · RememberMeAuthenticationException

Remember-me login has expired

Error message

Remember-me login has expired

What it means

processAutoLoginCookie validates the stored token's date against the configured token validity period. If token.getDate() + tokenValiditySeconds*1000 is earlier than the current time the remember-me login has expired and a RememberMeAuthenticationException is thrown, rejecting the cookie even though the series/token values match.

Source

Thrown at web/src/main/java/org/springframework/security/web/authentication/rememberme/PersistentTokenBasedRememberMeServices.java:119

		}
		String presentedSeries = cookieTokens[0];
		String presentedToken = cookieTokens[1];
		PersistentRememberMeToken token = this.tokenRepository.getTokenForSeries(presentedSeries);
		if (token == null) {
			// No series match, so we can't authenticate using this cookie
			throw new RememberMeAuthenticationException("No persistent token found for series id: " + presentedSeries);
		}
		// We have a match for this user/series combination
		if (!presentedToken.equals(token.getTokenValue())) {
			// Token doesn't match series value. Delete all logins for this user and throw
			// an exception to warn them.
			this.tokenRepository.removeUserTokens(token.getUsername());
			throw new CookieTheftException(this.messages.getMessage(
					"PersistentTokenBasedRememberMeServices.cookieStolen",
					"Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack."));
		}
		if (token.getDate().getTime() + getTokenValiditySeconds() * 1000L < System.currentTimeMillis()) {
			throw new RememberMeAuthenticationException("Remember-me login has expired");
		}
		// Token also matches, so login is valid. Update the token value, keeping the
		// *same* series number.
		this.logger.debug(LogMessage.format("Refreshing persistent login token for user '%s', series '%s'",
				token.getUsername(), token.getSeries()));
		PersistentRememberMeToken newToken = new PersistentRememberMeToken(token.getUsername(), token.getSeries(),
				generateTokenData(), new Date());
		try {
			this.tokenRepository.updateToken(newToken.getSeries(), newToken.getTokenValue(), newToken.getDate());
			addCookie(newToken, request, response);
		}
		catch (Exception ex) {
			this.logger.error("Failed to update token: ", ex);
			throw new RememberMeAuthenticationException("Autologin failed due to data access problem");
		}
		return getUserDetailsService().loadUserByUsername(token.getUsername());
	}

View on GitHub (pinned to 96852e8860)

Solutions

  1. Have the user log in again; the exception correctly forces re-authentication and issues a fresh cookie.
  2. Increase tokenValiditySeconds (http.rememberMe().tokenValiditySeconds(...)) if a longer lifetime is acceptable for your security posture.
  3. Keep the validity setting stable across deploys, or expect existing cookies to expire when you shorten it.
  4. Synchronize server clocks (NTP) to avoid clock-skew-induced early expiry.

Example fix

// before
http.rememberMe().tokenValiditySeconds(3600); // 1h: users expire constantly
// after
http.rememberMe().tokenValiditySeconds(1209600); // 14 days (framework default)
Defensive patterns

Strategy: try-catch

Validate before calling

PersistentRememberMeToken t = repo.getTokenForSeries(series);
if (t != null && t.getDate().getTime() + validitySeconds * 1000L < System.currentTimeMillis()) {
    // expired: skip remember-me, go to login page
}

Try / catch

try {
    Authentication a = rememberMeServices.autoLogin(request, response);
} catch (RememberMeAuthenticationException e) {
    // expired cookie: proceed unauthenticated to the login page
}

Prevention

When it happens

Trigger: Presenting a structurally valid series/token pair whose last-used timestamp is older than getTokenValiditySeconds() (default 14 days) — e.g. the user returns after a long absence, or tokenValiditySeconds was shortened below the age of stored tokens.

Common situations: Users away longer than the validity window; deployments that reduced tokenValiditySeconds, instantly invalidating existing cookies; servers with clock skew (clock moved back/forward) prematurely expiring or extending tokens; expired series kept by a cleanup job that removed them server-side before the cookie aged out (leading to 617 instead).

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/9563796d0c068375. Report an issue: GitHub.