{"record":{"id":"72ba75fa045ed3d2","repo":"apache/pulsar","slug":"error-decoding-jwt","errorCode":"ERROR_DECODING_JWT","errorMessage":"Invalid token: cannot be null","messagePattern":"Invalid token: cannot be null","errorType":"exception","errorClass":"AuthenticationException","httpStatus":null,"severity":"error","filePath":"pulsar-broker-auth-oidc/src/main/java/org/apache/pulsar/broker/authentication/oidc/AuthenticationProviderOpenID.java","lineNumber":291,"sourceCode":"            log.error().exception(e).log(\"Exception while retrieving role from JWT\");\n            return null;\n        }\n    }\n\n    /**\n     * Convert a JWT string into a {@link DecodedJWT}\n     * The benefit of using this method is that it utilizes the already instantiated {@link JWT} parser.\n     * WARNING: this method does not verify the authenticity of the token. It only decodes it.\n     *\n     * @param token - string JWT to be decoded\n     * @return a decoded JWT\n     * @throws AuthenticationException if the token string is null or if any part of the token contains\n     *         an invalid jwt or JSON format of each of the jwt parts.\n     */\n    DecodedJWT decodeJWT(String token) throws AuthenticationException {\n        if (token == null) {\n            incrementFailureMetric(AuthenticationExceptionCode.ERROR_DECODING_JWT);\n            throw new AuthenticationException(\"Invalid token: cannot be null\");\n        }\n        try {\n            return jwtLibrary.decodeJwt(token);\n        } catch (JWTDecodeException e) {\n            incrementFailureMetric(AuthenticationExceptionCode.ERROR_DECODING_JWT);\n            throw new AuthenticationException(\"Unable to decode JWT: \" + e.getMessage());\n        }\n    }\n\n    /**\n     * Authenticate the parameterized JWT.\n     *\n     * @param token - a nonnull JWT to authenticate\n     * @return a fully authenticated JWT, or AuthenticationException if the JWT is proven to be invalid in any way\n     */\n    private CompletableFuture<DecodedJWT> authenticateToken(String token) {\n        if (token == null) {\n            incrementFailureMetric(AuthenticationExceptionCode.ERROR_DECODING_JWT);","sourceCodeStart":273,"sourceCodeEnd":309,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker-auth-oidc/src/main/java/org/apache/pulsar/broker/authentication/oidc/AuthenticationProviderOpenID.java#L273-L309","documentation":"decodeJWT in AuthenticationProviderOpenID requires a non-null token string. If the raw JWT passed in from authenticateToken is null, it throws AuthenticationException(ERROR_DECODING_JWT) before attempting any decoding. This is a guard against absent credentials rather than a malformed-token failure.","triggerScenarios":"authenticateToken() is invoked with a null token — typically when the client sent no Authorization header/credential at all (getCredential() returned null) and the value was passed straight to decodeJWT.","commonSituations":"Client connects without any authentication data configured (missing authParams/auth plugin on the client); a proxy strips the Authorization header; null passed in unit tests or code paths that don't first check for anonymous/unauthenticated connections when the broker allows none.","solutions":["Configure the client with a valid OpenID Connect token (auth plugin AuthToken and the JWT in authParams) so the broker receives a non-null credential","Ensure the client actually sends the 'Authorization: Bearer <jwt>' header (check proxies/LBs that may strip it)","On the broker side, reject or handle null credentials earlier (e.g., check authenticationData.hasDataFromPeer() before calling authenticateToken) with a clearer error"],"exampleFix":"// before\ndecodedJwt = decodeJWT(authenticationData.getCredential());\n// after\nString token = authenticationData.getCredential();\nif (token == null || token.isEmpty()) {\n    throw new AuthenticationException(\"No credential provided by client\");\n}\ndecodedJwt = decodeJWT(token);","handlingStrategy":"type-guard","validationCode":"String cred = authenticationData.getCredential();\nif (cred == null || cred.isEmpty()) {\n    throw new AuthenticationException(\"Client supplied no token\");\n}","typeGuard":"boolean hasToken(Object authData) {\n    return authData instanceof AuthenticationDataSource ads\n        && ads.hasDataFromPeer()\n        && ads.getCredential() != null\n        && !ads.getCredential().isEmpty();\n}","tryCatchPattern":"try {\n    return decodeJWT(token);\n} catch (AuthenticationException e) {\n    if (e.getMessage().contains(\"cannot be null\")) {\n        log.debug(\"No JWT supplied by peer; rejecting as unauthenticated\");\n        return null;\n    }\n    throw e;\n}","preventionTips":["Configure the client auth plugin (AuthenticationToken) with a real JWT before connecting","Check proxies/load balancers preserve the Authorization header","Reject null credentials early with a clearer message before deeper parsing","Enable client-side auth logging to confirm the header is actually sent"],"tags":["auth","oidc","jwt","null"],"backgroundTag":"missing-credentials","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"}