{"record":{"id":"2902b77cc54b56ba","repo":"apereo/cas","slug":"invalid-cookie-s-required-fields-are-empty","errorCode":null,"errorMessage":"Invalid cookie %s. Required fields are empty","messagePattern":"Invalid cookie (.+?)\\. Required fields are empty","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":111,"sourceCode":"\n    @Override\n    protected String obtainValueFromCompoundCookie(final String value, final HttpServletRequest request) {\n        val cookieParts = Splitter.on(String.valueOf(COOKIE_FIELD_SEPARATOR)).splitToList(value);\n\n        val cookieValue = cookieParts.getFirst();\n        if (!cookieProperties.isPinToSession()) {\n            LOGGER.trace(\"Cookie session-pinning is disabled for cookie [{}]. Returning cookie value as it was provided\", cookieProperties.getName());\n            return cookieValue;\n        }\n\n        if (cookieParts.size() != COOKIE_FIELDS_LENGTH) {\n            throw new InvalidCookieException(\"Invalid cookie %s. Required fields are missing\".formatted(cookieProperties.getName()));\n        }\n        val cookieClientLocationOrIp = cookieParts.get(1);\n        val cookieUserAgent = cookieParts.get(2);\n\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            }","sourceCodeStart":93,"sourceCodeEnd":129,"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#L93-L129","documentation":"DefaultCasCookieValueManager builds compound cookie values (value, client location/ip, user-agent) and validates each part when reading the cookie back. InvalidCookieException is thrown when any required field in the decoded compound cookie is blank, meaning the cookie was corrupted, truncated, or forged. This protects CAS from accepting cookies missing binding data used for anti-cloning checks.","triggerScenarios":"obtainValueFromCompoundCookie splits the decrypted compound cookie value and any of the parts (value, client location/ip, user-agent) is blank — typically after manual cookie editing, partial cookie writes, encryption-key changes causing garbage decode, or cookies created by a different CAS version with a different compound format.","commonSituations":"Developer changed cas.tgc.crypto.encryption.key/signing.key between deployments so old cookies decode to malformed strings; user copied a TGC cookie from another browser or machine; a proxy or custom filter rewrote/truncated the Cookie header; testing with hand-crafted cookies.","solutions":["Clear the browser cookie and re-authenticate; stale/malformed cookies are simply rejected","Verify cas.tgc.crypto signing/encryption keys are identical across all CAS nodes in the cluster","Ensure all CAS nodes run the same CAS version so the compound cookie format matches","Check for proxies/filters modifying the Cookie header; inspect the raw cookie contents","If cookies are consistently rejected, disable pinnable/compound cookie fields you do not need (e.g. disable client binding checks so fewer fields are required)"],"exampleFix":"// before: keys differ per node causing undecodable cookies\n// cas.tgc.crypto.encryption.key=AAA...\n// cas.tgc.crypto.encryption.key=BBB... (node 2)\n// after: same keys everywhere\n// cas.tgc.crypto.encryption.key=AAA... (all nodes)\n// cas.tgc.crypto.signing.key=CCC... (all nodes)","handlingStrategy":"validation","validationCode":"String[] parts = compoundValue.split(\":\");\nif (parts.length < 3 || Stream.of(parts).anyMatch(StringUtils::isBlank)) {\n    // fail fast: clear cookie and force re-authentication\n}","typeGuard":"boolean hasRequiredFields(String[] parts) {\n    return parts != null && parts.length >= 3 && Stream.of(parts).noneMatch(StringUtils::isBlank);\n}","tryCatchPattern":"try { manager.obtainCookieValue(...); } catch (InvalidCookieException e) {\n    LOGGER.warn(\"Rejecting cookie\", e);\n    cookieGrantingCookieBuilder.remove(); // clear and re-authenticate\n}","preventionTips":["Keep crypto keys identical across all CAS nodes","Clear cookies after upgrading CAS versions","Don't hand-edit or copy TGC cookies between browsers","Check proxies don't modify the Cookie header"],"tags":["cookie","session","security","invalid-cookie"],"backgroundTag":"invalid-cookie","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"}