{"record":{"id":"ed16a07fe1258ef2","repo":"apache/pulsar","slug":"invalid-token-string-missing-attributes","errorCode":null,"errorMessage":"Invalid token string, missing attributes","messagePattern":"Invalid token string, missing attributes","errorType":"exception","errorClass":"AuthenticationException","httpStatus":null,"severity":"error","filePath":"pulsar-broker-auth-sasl/src/main/java/org/apache/pulsar/broker/authentication/SaslRoleToken.java","lineNumber":192,"sourceCode":"    @Override\n    public String toString() {\n        return token;\n    }\n\n    /**\n     * Parses a string into an authentication token.\n     *\n     * @param tokenStr string representation of a token.\n     *\n     * @return the parsed authentication token.\n     *\n     * @throws AuthenticationException thrown if the string representation could not be parsed into\n     * an authentication token.\n     */\n    public static SaslRoleToken parse(String tokenStr) throws AuthenticationException {\n        Map<String, String> map = split(tokenStr);\n        if (!map.keySet().equals(ATTRIBUTES)) {\n            throw new AuthenticationException(\"Invalid token string, missing attributes\");\n        }\n        long expires = Long.parseLong(map.get(EXPIRES));\n        SaslRoleToken token = new SaslRoleToken(map.get(USER_ROLE), map.get(SESSION));\n        token.setExpires(expires);\n        return token;\n    }\n\n    /**\n     * Splits the string representation of a token into attributes pairs.\n     *\n     * @param tokenStr string representation of a token.\n     *\n     * @return a map with the attribute pairs of the token.\n     *\n     * @throws AuthenticationException thrown if the string representation of the token could not be broken into\n     * attribute pairs.\n     */\n    private static Map<String, String> split(String tokenStr) throws AuthenticationException {","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker-auth-sasl/src/main/java/org/apache/pulsar/broker/authentication/SaslRoleToken.java#L174-L210","documentation":"javax.naming.AuthenticationException thrown by SaslRoleToken.parse when the split token string does not contain exactly the three expected attributes u (userRole), i (session) and e (expires). parse compares the parsed key set against ATTRIBUTES and rejects any string that is missing attributes, has extra/unknown attributes, or duplicates a key (e.g. two 'u=' entries collapse to one key).","triggerScenarios":"Calling SaslRoleToken.parse(tokenStr) with a string whose '&'-separated key set != {\"u\",\"e\",\"i\"} — e.g. a truncated token like \"u=bob&e=123\" missing i=, a token with an unexpected extra attribute, a duplicate key that collapses the map, or a non-token string such as an empty string or a different auth payload.","commonSituations":"Token truncated or corrupted in transit/storage (e.g. cut-and-paste, query-string handling dropping part of it); client sends a token produced by a different/older token format or a different authentication provider; token string stored/retrieved incorrectly (URL-encoding mangling); passing the whole HTTP header value including scheme instead of just the token.","solutions":["Ensure the token string is the exact output of SaslRoleToken.toString() (form u=<role>&i=<session>&e=<expires>) and is not truncated or re-encoded (check URL decode/encode round-trips)","Verify client and broker use compatible versions so the token format (attribute keys u/i/e) matches; regenerate the token after upgrades","Log the received tokenStr (carefully — it is sensitive) and diff its key set against {u,e,i} to identify the missing/extra attribute","Handle AuthenticationException in the caller and reject the request, prompting the client to re-authenticate and obtain a fresh token"],"exampleFix":"// before\nSaslRoleToken token = SaslRoleToken.parse(headerValue); // header may include scheme\n// after\nString tokenStr = headerValue;\nif (tokenStr.startsWith(\"Bearer \")) {\n    tokenStr = tokenStr.substring(\"Bearer \".length());\n}\nif (tokenStr.matches(\"^u=[^&]*&i=[^&]*&e=[0-9]+$\")) {\n    SaslRoleToken token = SaslRoleToken.parse(tokenStr);\n} else {\n    throw new AuthenticationException(\"Invalid token string, missing attributes\");\n}","handlingStrategy":"validation","validationCode":"// run before SaslRoleToken.parse(tokenStr)\nstatic boolean looksLikeToken(String tokenStr) {\n    if (tokenStr == null) return false;\n    Set<String> keys = new HashSet<>();\n    for (String part : tokenStr.split(\"&\")) {\n        int eq = part.indexOf('=');\n        if (eq <= 0) return false;\n        keys.add(part.substring(0, eq));\n    }\n    return keys.equals(new HashSet<>(Arrays.asList(\"u\", \"i\", \"e\")));\n}","typeGuard":null,"tryCatchPattern":"try {\n    SaslRoleToken token = SaslRoleToken.parse(tokenStr);\n    if (token.isExpired()) {\n        throw new AuthenticationException(\"Token expired; re-authenticate\");\n    }\n} catch (AuthenticationException e) {\n    if (e.getMessage().contains(\"missing attributes\")) {\n        // reject request, ask client to obtain a fresh token\n    }\n    throw e;\n}","preventionTips":["Transmit the token exactly as produced by toString(); avoid extra URL encoding/decoding round-trips that corrupt '&' or '='","Only round-trip tokens between toString() and parse(); don't hand-build token strings","Keep broker and client on compatible versions of the token format","Trim any header scheme/prefix before parsing"],"tags":["sasl","token","parsing","authentication"],"backgroundTag":"malformed-auth-token","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}