{"record":{"id":"1948c6966db9cbbc","repo":"apache/pulsar","slug":"audiences-in-token-object-not-contains-this","errorCode":null,"errorMessage":"Audiences in token: [${object}] not contains this broker: ${audience}","messagePattern":"Audiences in token: \\[(.+?)\\] not contains this broker: (.+?)","errorType":"exception","errorClass":"AuthenticationException","httpStatus":null,"severity":"error","filePath":"pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProviderToken.java","lineNumber":254,"sourceCode":"\n            if (audienceClaim != null) {\n                Object object = jwt.getBody().get(audienceClaim);\n                if (object == null) {\n                    throw new JwtException(\"Found null Audience in token, for claimed field: \" + audienceClaim);\n                }\n\n                if (object instanceof Collection) {\n                    Collection<String> audiences = (Collection<String>) object;\n                    // audience not contains this broker, throw exception.\n                    if (audiences.stream().noneMatch(audienceInToken -> audienceInToken.equals(audience))) {\n                        incrementFailureMetric(ErrorCode.INVALID_AUDIENCES);\n                        throw new AuthenticationException(\"Audiences in token: [\"\n                                + String.join(\", \", audiences) + \"] not contains this broker: \" + audience);\n                    }\n                } else if (object instanceof String) {\n                    if (!object.equals(audience)) {\n                        incrementFailureMetric(ErrorCode.INVALID_AUDIENCES);\n                        throw new AuthenticationException(\n                                \"Audiences in token: [\" + object + \"] not contains this broker: \" + audience);\n                    }\n                } else {\n                    // should not reach here.\n                    incrementFailureMetric(ErrorCode.INVALID_AUDIENCES);\n                    throw new AuthenticationException(\"Audiences in token is not in expected format: \" + object);\n                }\n            }\n\n            var expiration = jwt.getBody().getExpiration();\n            var tokenRemainingDurationMs = expiration != null ? expiration.getTime() - new Date().getTime() : null;\n            authenticationMetricsToken.recordTokenDuration(tokenRemainingDurationMs);\n            return jwt;\n        } catch (JwtException e) {\n            if (e instanceof ExpiredJwtException) {\n                authenticationMetricsToken.recordTokenExpired();\n            }\n            incrementFailureMetric(ErrorCode.INVALID_TOKEN);","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProviderToken.java#L236-L272","documentation":"AuthenticationProviderToken throws this AuthenticationException in authenticateToken when the JWT's audience claim (tokenAudienceClaim, if configured) does not contain the audience value this broker expects (tokenAudience). The broker verifies that the token was issued specifically for it; a token whose aud claim lists other audiences is rejected even though its signature is valid.","triggerScenarios":"A client authenticates with a token whose aud claim is a single string not equal to the broker's configured tokenAudience, or whose audience collection contains no entry equal to it, while authenticationProviderAudienceClaim/tokenAudience is configured on the broker.","commonSituations":"A token minted for another service (e.g. a different broker cluster or component) is reused against this broker; the broker's tokenAudience was changed or set after the token was issued; the issuer used a singular aud claim that doesn't match the broker's expected audience string.","solutions":["Re-issue the token including this broker's tokenAudience value in the aud claim (e.g. with pulsar tokens create --audience <brokerAudience>).","Check the broker's tokenAudience setting in broker.conf/standalone.conf and make it match the audience used by your token issuer.","If audience validation is not needed, remove the tokenAudienceClaim/tokenAudience configuration so the check is skipped.","Decode the token (e.g. jwt.io) to inspect the actual aud claim and compare it to the broker's audience string for typos or trailing whitespace."],"exampleFix":"// before: token issued without matching audience\npulsar tokens create --secret-key $SECRET --subject my-role\n// after\npulsar tokens create --secret-key $SECRET --subject my-role --audience \"urn:my-broker\"","handlingStrategy":"validation","validationCode":"// Decode token payload (no libs needed with jjwt on classpath):\nString[] parts = token.split(\"\\\\.\");\nString payload = new java.util.Base64.getUrlDecoder().decode(parts[1]);\norg.json.JSONObject claims = new org.json.JSONObject(payload);\nObject aud = claims.opt(\"aud\"); // or your configured audienceClaim\nboolean ok = (aud instanceof String && audience.equals(aud))\n    || (aud instanceof org.json.JSONArray && ((org.json.JSONArray) aud).toList().contains(audience));\nif (!ok) { throw new IllegalStateException(\"token audience does not include broker audience \" + audience); }","typeGuard":"static boolean hasValidAudience(Object claimValue, String expected) {\n    if (claimValue instanceof String) return expected.equals(claimValue);\n    if (claimValue instanceof Collection<?>) return ((Collection<?>) claimValue).contains(expected);\n    return false;\n}","tryCatchPattern":"try {\n    String role = authProvider.authenticate(authData);\n} catch (AuthenticationException e) {\n    if (e.getMessage().contains(\"not contains this broker\")) {\n        log.warn(\"Token audience mismatch; re-issue token with audience {}\", brokerAudience);\n    }\n}","preventionTips":["Always mint tokens with the --audience flag matching the broker's tokenAudience.","Keep one source of truth for audience strings shared between issuer and broker config.","Inspect tokens with jwt.io or a decoder before deploying them.","Document per-component audiences so tokens are not reused across services."],"tags":["jwt","authentication","audience","broker"],"backgroundTag":"jwt-audience-mismatch","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"}