{"record":{"id":"a21011f675ab55c9","repo":"apereo/cas","slug":"invalid-cookie-s-required-remote-address-s-does","errorCode":null,"errorMessage":"Invalid cookie %s Required remote address %s does not match %s","messagePattern":"Invalid cookie (.+?) Required remote address (.+?) does not match (.+?)","errorType":"exception","errorClass":"InvalidCookieException","httpStatus":null,"severity":"warning","filePath":"core/cas-server-core-cookie-api/src/main/java/org/apereo/cas/web/support/mgmr/DefaultCasCookieValueManager.java","lineNumber":127,"sourceCode":"\n        if (Stream.of(cookieValue, cookieClientLocationOrIp, cookieUserAgent).anyMatch(StringUtils::isBlank)) {\n            throw new InvalidCookieException(\"Invalid cookie %s. Required fields are empty\".formatted(cookieProperties.getName()));\n        }\n\n        val clientInfo = ClientInfoHolder.getClientInfo();\n        if (clientInfo == null) {\n            val message = \"Unable to match required remote address %s because client ip at time of cookie creation is unknown for cookie %s\"\n                .formatted(cookieProperties.getName(), cookieClientLocationOrIp);\n            LOGGER.warn(message);\n            throw new InvalidCookieException(message);\n        }\n\n        if (cookieProperties.isGeoLocateClientSession()) {\n            val clientLocationOrIp = getClientGeoLocation(clientInfo);\n            if (!cookieClientLocationOrIp.equals(clientLocationOrIp)) {\n                val message = \"Invalid cookie %s Required remote address %s does not match %s\"\n                    .formatted(cookieProperties.getName(), cookieClientLocationOrIp, clientLocationOrIp);\n                LOGGER.warn(message);\n                throw new InvalidCookieException(message);\n            }\n        } else {\n            val clientIpAddress = clientInfo.getClientIpAddress();\n            if (!cookieClientLocationOrIp.equals(clientIpAddress)) {\n                if (StringUtils.isBlank(cookieProperties.getAllowedIpAddressesPattern())\n                    || !RegexUtils.find(cookieProperties.getAllowedIpAddressesPattern(), clientIpAddress)) {\n                    val message = \"Invalid cookie %s. Required remote address %s does not match %s\"\n                        .formatted(cookieProperties.getName(), cookieClientLocationOrIp, clientIpAddress);\n                    LOGGER.warn(message);\n                    throw new InvalidCookieException(message);\n                }\n                LOGGER.debug(\"Required remote address [{}] does not match [{}], but it's authorized to proceed\",\n                    cookieClientLocationOrIp, clientIpAddress);\n            }\n        }\n\n        val agent = HttpRequestUtils.getHttpServletRequestUserAgent(request);","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/core/cas-server-core-cookie-api/src/main/java/org/apereo/cas/web/support/mgmr/DefaultCasCookieValueManager.java#L109-L145","documentation":"CAS binds cookies to the client's IP or geolocated location when created. On validation, if geoLocateClientSession is enabled and the stored location does not match the current resolved location, the cookie is rejected with InvalidCookieException. This prevents cookie theft/replay from a different client.","triggerScenarios":"cas.tgc GeoLocateClientSession enabled (or cookieProperties.isGeoLocateClientSession() true) and getClientGeoLocation(clientInfo) resolves a different location/IP than the one embedded in the cookie — client IP changed (mobile network, VPN, NAT pools), geo-IP database returns different results across nodes, or cookie replayed from another machine.","commonSituations":"User on roaming/mobile connection whose IP changed between requests; MaxMind/geoIP database versions differ across cluster nodes so the same IP geolocates differently; corporate proxy rotates egress IPs; developer tests across localhost vs actual IP.","solutions":["Disable geolocation session binding (set the relevant cookie geolocation property to false) if clients frequently change IPs","Ensure the same geo-IP database version/config is deployed to every CAS node","Investigate proxy/CDN headers (X-Forwarded-For) handling so client IP resolution is stable","If IP changes are legitimate, use allowedIpAddressesPattern to whitelist the client's range instead of exact matching"],"exampleFix":"// before\n// cas.tgc.pinnable-session-cookie.geo-locate-client-session=true\n// after\n// cas.tgc.pinnable-session-cookie.geo-locate-client-session=false","handlingStrategy":"validation","validationCode":"String storedIp = /* part 2 of compound cookie */;\nString currentIp = ClientInfoHolder.getClientInfo().getClientIpAddress();\nif (!storedIp.equals(currentIp)) { /* expect rejection unless allowedIpAddressesPattern covers it */ }","typeGuard":"boolean ipBindingIntact(RegisteredServiceCookieProperties p, String stored, String current) {\n    return stored.equals(current) || RegexUtils.find(p.getAllowedIpAddressesPattern(), current);\n}","tryCatchPattern":"try { obtainCookieValue(...); } catch (InvalidCookieException e) {\n    LOGGER.warn(\"Cookie IP binding failed; forcing fresh login\");\n}","preventionTips":["Disable geo-location binding for mobile/VPN populations","Deploy identical geo-IP databases cluster-wide","Configure allowedIpAddressesPattern for NAT/proxy ranges","Normalize X-Forwarded-For handling"],"tags":["cookie","ip-binding","geolocation","security"],"backgroundTag":"client-ip-mismatch","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}