{"record":{"id":"010e89959f061d64","repo":"halo-dev/halo","slug":"cookie-token-was-not-base64-encoded-value-was","errorCode":null,"errorMessage":"Cookie token was not Base64 encoded; value was '{}'","messagePattern":"Cookie token was not Base64 encoded; value was '(.+?)'","errorType":"validation","errorClass":"InvalidCookieException","httpStatus":null,"severity":"warning","filePath":"application/src/main/java/run/halo/app/security/authentication/rememberme/TokenBasedRememberMeServices.java","lineNumber":309,"sourceCode":"    }\n\n    protected long calculateExpireTime(ServerWebExchange exchange, Authentication authentication) {\n        var tokenLifetime = rememberMeCookieResolver.getCookieMaxAge().toSeconds();\n        return Instant.now().plusSeconds(tokenLifetime).toEpochMilli();\n    }\n\n    protected String[] decodeCookie(String cookieValue) throws InvalidCookieException {\n        int paddingCount = 4 - (cookieValue.length() % 4);\n        if (paddingCount < 4) {\n            char[] padding = new char[paddingCount];\n            Arrays.fill(padding, '=');\n            cookieValue += new String(padding);\n        }\n        String cookieAsPlainText;\n        try {\n            cookieAsPlainText = new String(Base64.getDecoder().decode(cookieValue.getBytes()));\n        } catch (IllegalArgumentException ex) {\n            throw new InvalidCookieException(\"Cookie token was not Base64 encoded; value was '\" + cookieValue + \"'\");\n        }\n        String[] tokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, DELIMITER);\n        for (int i = 0; i < tokens.length; i++) {\n            tokens[i] = URLDecoder.decode(tokens[i], StandardCharsets.UTF_8);\n        }\n        return tokens;\n    }\n\n    /**\n     * Inverse operation of decodeCookie.\n     *\n     * @param cookieTokens the tokens to be encoded.\n     * @return base64 encoding of the tokens concatenated with the \":\" delimiter.\n     */\n    protected String encodeCookie(String[] cookieTokens) {\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < cookieTokens.length; i++) {\n            sb.append(URLEncoder.encode(cookieTokens[i], StandardCharsets.UTF_8));","sourceCodeStart":291,"sourceCodeEnd":327,"githubUrl":"https://github.com/halo-dev/halo/blob/d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8/application/src/main/java/run/halo/app/security/authentication/rememberme/TokenBasedRememberMeServices.java#L291-L327","documentation":"decodeCookie() Base64-decodes the raw cookie value (after re-padding to a multiple of 4). If Base64.getDecoder().decode throws IllegalArgumentException, the cookie is not valid Base64 and an InvalidCookieException is thrown. This is the first validation gate before the payload is split into tokens.","triggerScenarios":"A remember-me cookie value containing non-Base64 characters, stripped padding that the re-pad logic cannot fix (e.g., truncated mid-group), or a value that was never Base64-encoded (a cookie set by another application on the same domain/name). Reached for every auto-login attempt via autoLogin -> decodeCookie.","commonSituations":"Another app overwrote the cookie name; a reverse proxy stripped characters; the cookie was hand-edited; switching cookie-encoding schemes between versions.","solutions":["Clear the bad cookie in the browser (the server also cancels it via handleError -> cancelCookie) and log in again.","Confirm no other application shares the remember-me cookie name/path.","Check that a reverse proxy is not URL-rewriting or truncating the cookie value.","Ensure encodeCookie/decodeCookie pair is from the same Halo version on all nodes."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// The framework already handles this in handleError -> cancelCookie:\n.onErrorResume(InvalidCookieException.class, ex -> {\n    log.debug(\"Non-Base64 remember-me cookie rejected\");\n    return Mono.empty();\n})","preventionTips":["Do not set other applications' cookies under the same name/path as Halo's remember-me cookie.","Verify proxies preserve the full Base64 cookie value verbatim.","Use the same encode/decode implementation across all nodes.","If migrating cookie formats, expire old cookies deliberately rather than letting them fail."],"tags":["security","authentication","cookie","remember-me","base64"],"backgroundTag":null,"analyzedSha":"d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8","analyzedAt":"2026-08-14T00:18:38.915Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}