apereo/cas · error · InvalidCookieException
Invalid cookie . Required user-agent does not match
Error message
Invalid cookie <name>. Required user-agent <cookieAgent> does not match <agent>
What it means
CAS validates that the User-Agent header presented when reading a compound cookie matches the User-Agent that was captured when the cookie was created. The cookie value embeds the original agent string; if the incoming request's agent differs, the cookie is presumed stolen or replayed from another client and InvalidCookieException is thrown to reject it.
Solutions
- Ensure the client sends the exact same User-Agent header on every request that carries the CAS cookie (pin it in API clients, e.g. curl -A).
- Check intermediaries (proxies, WAFs, CDNs) for rules that rewrite or strip the User-Agent header and disable them for CAS routes.
- Investigate why the agent differs between cookie creation and use — log both values; if the cookie was legitimately moved between devices, force re-authentication instead.
- If agent binding is too strict for your environment, reduce cookie security by lowering the cookie security policy (e.g. disable user-agent binding in cas.cookie.* properties / getCookie().setBrowsers()), accepting the weaker security.
- Clear the stale cookie and re-authenticate to mint a fresh one bound to the current agent.
Example fix
// before: client sends differing agents per request curl -H 'User-Agent: curl/8.0' https://cas/cas/v1/tickets -d '...' curl https://cas/cas/... -b TGC=... # no User-Agent -> mismatch // after: pin the same agent everywhere curl -H 'User-Agent: MyApp/1.0' https://cas/cas/v1/tickets -d '...' curl -H 'User-Agent: MyApp/1.0' https://cas/cas/... -b TGC=...
Defensive patterns
Strategy: try-catch
Validate before calling
String ua = HttpRequestUtils.getHttpServletRequestUserAgent(request);
String expected = /* agent stored when cookie was issued */;
if (ua == null || !ua.equals(expected)) {
// skip obtainCookieValue / force re-auth
} Try / catch
try {
String value = cookieValueManager.obtainCookieValue(cookie, request);
} catch (InvalidCookieException e) {
// clear cookie and redirect to login
} Prevention
- Send a fixed, non-empty User-Agent header from all non-browser clients.
- Audit proxies/WAFs for User-Agent rewriting on CAS routes.
- Avoid copying session cookies between devices or browsers.
- Keep browser auto-update behavior in mind; user-agent binding makes sessions agent-version-bound by design.
When it happens
Trigger: Calling obtainValueFromCompoundCookie (via CasCookieValueManager.obtainCookieValue) when the request's User-Agent header does not exactly equal the agent string stored in the compound cookie — e.g. the client upgraded its browser, rotated behind different proxies that rewrite User-Agent, the header is missing/null, or the cookie was copied to another device.
Common situations: Reverse proxies or CDNs (Cloudflare, ModSecurity) stripping or normalizing User-Agent; browser auto-updates mid-session; load balancers spreading requests across clients with different agents; API clients forgetting to send the same User-Agent on every call; cookies shared between curl and a browser.
Related errors
- Invalid cookie . Required fields are empty
- Invalid cookie . Required user-agent does not match
- Request does not specify a user-agent
- Unable to match required remote address
- Invalid cookie Required remote address does not match
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/ecd8852d34a6fe00.
Report an issue: GitHub.
Appendix: source
Thrown at core/cas-server-core-cookie-api/src/main/java/org/apereo/cas/web/support/mgmr/DefaultCasCookieValueManager.java:150
if (!cookieClientLocationOrIp.equals(clientIpAddress)) {
if (StringUtils.isBlank(cookieProperties.getAllowedIpAddressesPattern())
|| !RegexUtils.find(cookieProperties.getAllowedIpAddressesPattern(), clientIpAddress)) {
val message = "Invalid cookie %s. Required remote address %s does not match %s"
.formatted(cookieProperties.getName(), cookieClientLocationOrIp, clientIpAddress);
LOGGER.warn(message);
throw new InvalidCookieException(message);
}
LOGGER.debug("Required remote address [{}] does not match [{}], but it's authorized to proceed",
cookieClientLocationOrIp, clientIpAddress);
}
}
val agent = HttpRequestUtils.getHttpServletRequestUserAgent(request);
if (!cookieUserAgent.equals(agent)) {
val message = "Invalid cookie %s. Required user-agent %s does not match %s"
.formatted(cookieProperties.getName(), cookieUserAgent, agent);
LOGGER.warn(message);
throw new InvalidCookieException(message);
}
return cookieValue;
}
}
View on GitHub (pinned to e7288fc434)