{"record":{"id":"8fd99eb71c81de46","repo":"apache/pulsar","slug":"need-to-authenticate-to-perform-the-request-8fd99e","errorCode":null,"errorMessage":"Need to authenticate to perform the request","messagePattern":"Need to authenticate to perform the request","errorType":"http","errorClass":"RestException","httpStatus":403,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java","lineNumber":343,"sourceCode":"    protected CompletableFuture<Void> validateAdminAccessForTenantAsync(\n            PulsarService pulsar, String clientAppId,\n            String originalPrincipal, String tenant,\n            AuthenticationDataSource authenticationData) {\n            log.debug()\n                    .attr(\"tenant\", tenant)\n                    .attr(\"authenticated\", (isClientAuthenticated(clientAppId)))\n                    .attr(\"role\", clientAppId)\n                    .log(\"check admin access on tenant\");\n                return pulsar.getPulsarResources().getTenantResources().getTenantAsync(tenant)\n                .thenCompose(tenantInfoOptional -> {\n                    if (tenantInfoOptional.isEmpty()) {\n                        throw new RestException(Status.NOT_FOUND, \"Tenant does not exist\");\n                    }\n                    TenantInfo tenantInfo = tenantInfoOptional.get();\n                    if (pulsar.getConfiguration().isAuthenticationEnabled() && pulsar.getConfiguration()\n                            .isAuthorizationEnabled()) {\n                        if (!isClientAuthenticated(clientAppId)) {\n                            throw new RestException(Status.FORBIDDEN, \"Need to authenticate to perform the request\");\n                        }\n                        validateOriginalPrincipal(clientAppId, originalPrincipal);\n                        if (pulsar.getConfiguration().getProxyRoles().contains(clientAppId)) {\n                            AuthorizationService authorizationService =\n                                    pulsar.getBrokerService().getAuthorizationService();\n                            return authorizationService.isTenantAdmin(tenant, clientAppId, tenantInfo,\n                                            authenticationData)\n                                .thenCompose(isTenantAdmin -> {\n                                    if (!isTenantAdmin) {\n                                            return authorizationService.isSuperUser(clientAppId, authenticationData)\n                                                .thenCombine(authorizationService.isSuperUser(originalPrincipal,\n                                                             authenticationData),\n                                                     (proxyAuthorized, originalPrincipalAuthorized) -> {\n                                                         if (!proxyAuthorized || !originalPrincipalAuthorized) {\n                                                             throw new RestException(Status.UNAUTHORIZED,\n                                                                     String.format(\"Proxy not authorized to access \"\n                                                                                     + \"resource (proxy:%s,original:%s)\"\n                                                                             , clientAppId, originalPrincipal));","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java#L325-L361","documentation":"If both authentication and authorization are enabled, validateAdminAccessForTenantAsync requires an authenticated client identity. When clientAppId is null/blank (unauthenticated request), it throws RestException with HTTP 403 'Need to authenticate to perform the request' — you must authenticate before tenant-admin authorization can be evaluated.","triggerScenarios":"Calling a tenant-admin endpoint without credentials while broker has authenticationEnabled=true and authorizationEnabled=true; client sends no/malformed Authorization header so the auth plugin yields no role; anonymous access to a tenant-scoped endpoint.","commonSituations":"curl/scripts missing the auth token; expired token rejected in a way that leaves no principal; misconfigured client auth plugin (wrong provider/parameters); broker auth enabled after clients were built without auth support.","solutions":["Send valid credentials (e.g. Authorization: Bearer <token>) with the request","Fix the client's auth plugin configuration (provider, token/credentials file, issuer URL)","Check broker logs/authentication plugin to see why no role was derived from the presented credentials","If the endpoint is intentionally open, adjust broker authenticationEnabled — not recommended for admin APIs"],"exampleFix":"// before\ncurl http://broker:8080/admin/v2/tenants/mytenant\n// after\ncurl -H \"Authorization: Bearer $ADMIN_TOKEN\" http://broker:8080/admin/v2/tenants/mytenant","handlingStrategy":"try-catch","validationCode":"// client-side pre-check: PulsarAdmin built without Authentication will send no credentials\nif (pulsarAdmin.getClientConfig() == null || !hasAuthPlugin(pulsarAdmin)) {\n    throw new IllegalStateException(\"PulsarAdmin must be configured with an Authentication provider\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    admin.tenants().getTenant(tenant);\n} catch (PulsarAdminException e) {\n    if (e.getStatusCode() == 403) {\n        // refresh credentials / reconfigure the auth plugin\n    }\n    throw e;\n}","preventionTips":["Always configure an Authentication provider on PulsarAdmin in secured clusters","Refresh/rotate tokens before expiry in long-running admin jobs","Check broker authenticationEnabled/authorizationEnabled flags match your client setup"],"tags":["authentication","rest-api","authorization","pulsar"],"backgroundTag":"unauthenticated-request","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}