skip to content

How does TokenBasedRememberMeServices construct and validate its cookie?

level: middleimportance: must knowfreq 55%

answer

  1. username:expiry:algorithm:signature, Base64
  2. hash of username:expiry:password:key
  3. default SHA-256 (was MD5)
  4. password hash in signature -> pw change kills tokens
  5. stateless, no DB, replayable until expiry

basics

~20 s

It builds a signed cookie containing the username, an expiry time, and a hash of username+expiry+password+key. On return it recomputes the hash from the stored password and compares; if they match and it's not expired, the user is logged in. No database is needed.

solid answer

~40 s

TokenBasedRememberMeServices creates a stateless, self-contained cookie: it Base64-encodes `username:expiryTime:algorithm:signature`, where the signature is a hex hash (default SHA-256 in current versions; historically MD5) of `username:expiryTime:password:key`. Because the user's stored password hash is part of the signature, changing the password invalidates all outstanding remember-me tokens automatically. On a subsequent request the filter decodes the cookie, loads the user via UserDetailsService, recomputes the signature, and rejects the cookie if the hash differs, the token is expired, or the user no longer exists. Its big advantage is zero storage; its weaknesses are that a stolen cookie is valid until expiry and there is no revocation short of a password change. It requires access to the (hashed) password, so it doesn't fit external/OAuth identity providers.

code

java · 17 lines
java
// Explicit TokenBasedRememberMeServices (stateless hash cookie)
@Bean
RememberMeServices rememberMeServices(UserDetailsService uds) {
    var key = "change-me-in-prod";
    var rms = new TokenBasedRememberMeServices(key, uds);
    rms.setTokenValiditySeconds(1209600);       // 14 days
    rms.setMatchingAlgorithm(RememberMeTokenAlgorithm.SHA256);
    rms.setEncodingAlgorithm(RememberMeTokenAlgorithm.SHA256);
    return rms;
}

@Bean
SecurityFilterChain security(HttpSecurity http, RememberMeServices rms) throws Exception {
    http.formLogin(Customizer.withDefaults())
        .rememberMe(rm -> rm.rememberMeServices(rms).key("change-me-in-prod"));
    return http.build();
}

go deeper

for a junior

Know it's a signed cookie with no database.

for a middle

Recite the cookie fields and that the signature mixes username, expiry, password, and key.

for a senior

Discuss the revocation limitation, replay window, and password-change invalidation semantics.

for a principal

Weigh algorithm migration (encoding vs matching), HTTPS/cookie flags, and unsuitability for passwordless IdPs.

`TokenBasedRememberMeServices` is the **default** remember-me implementation and is entirely **stateless** — no server-side storage. **Cookie construction.** After a successful login (with opt-in), it builds a token string and Base64-encodes it into the cookie value. The logical structure is: ``` username : tokenExpiryTime : algorithmName : signatureHash ``` - `username` — the principal name. - `tokenExpiryTime` — an absolute expiry timestamp in milliseconds (now + validity). - `signatureHash` — a hex-encoded digest of `username + ":" + tokenExpiryTime + ":" + password + ":" + key`. Here `password` is the user's **stored (hashed) password** and `key` is the application secret. The digest algorithm defaults to **SHA-256** in current Spring Security (the older single-argument constructor used **MD5**); the algorithm name is embedded in the cookie so a deployment can migrate hashing algorithms while still validating older cookies (`setMatchingAlgorithm`). **Validation (autoLogin).** When `RememberMeAuthenticationFilter` finds no existing authentication, it calls `rememberMeServices.autoLogin()`: 1. Decode the Base64 cookie and split the fields. 2. Reject if the token is malformed or `tokenExpiryTime` is in the past (`token expired`). 3. Load the user with the configured `UserDetailsService` (throws if the user is gone/disabled). 4. Recompute the expected signature from the freshly loaded username, the expiry from the cookie, the *current* stored password, and the key. 5. Constant-time-compare with the cookie's signature; mismatch throws `InvalidCookieException`. 6. On success, return a `RememberMeAuthenticationToken` with the user's authorities. **Key security properties.** - **Password change = automatic invalidation.** Because the password hash feeds the signature, any password change breaks every outstanding token. This is the only built-in 'revocation' for this strategy. - **No revocation of individual devices.** You cannot log out one stolen cookie without changing the password (which kills all of them). - **Stolen cookie is replayable until expiry.** Anyone with the cookie is that user until `tokenExpiryTime`. Mitigate with HTTPS + `Secure`/`HttpOnly` cookie flags and a modest validity window. - **Requires the password hash.** Doesn't work with identity providers that don't expose a password (pure OAuth2/OIDC, SSO). **Configuration.** Provide `key`, `tokenValiditySeconds`, and a `UserDetailsService` (usually auto-detected). Choosing this strategy explicitly: ```java http.rememberMe(rm -> rm.key("secret").userDetailsService(uds)); ``` Omitting `tokenRepository` selects TokenBased by default; supplying one switches to persistent. **When to use.** Simple apps that want convenience without a token table, where the automatic password-change invalidation is acceptable and you don't need per-device revocation or theft detection.

  • Why does changing a user's password invalidate their remember-me token in this scheme?
    The signature hash includes the user's stored password. When the filter recomputes the hash using the new password, it no longer matches the cookie's hash, so validation fails.
  • What protects against tampering with the username or expiry inside the cookie?
    Those fields are inputs to the signature hash, which also mixes in the server-only secret key and the password. An attacker can't recompute a valid signature without both, so any edit is rejected.

saying these in an interview costs you the question

  • Claiming the cookie stores the raw password (it stores a hash-derived signature, and the compared value is the stored hashed password)
  • Saying TokenBased persists anything server-side
  • Thinking you can revoke a single stolen TokenBased cookie without a password change
  • Assuming it works with OAuth2/OIDC logins that have no local password

context