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

  1. 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).
  2. Check intermediaries (proxies, WAFs, CDNs) for rules that rewrite or strip the User-Agent header and disable them for CAS routes.
  3. 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.
  4. 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.
  5. 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

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


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)