{"record":{"id":"e49d32365fc8559f","repo":"apache/pulsar","slug":"unrecognized-sasl-gssapi-server-callback","errorCode":null,"errorMessage":"Unrecognized SASL GSSAPI Server Callback.","messagePattern":"Unrecognized SASL GSSAPI Server Callback\\.","errorType":"exception","errorClass":"UnsupportedCallbackException","httpStatus":null,"severity":"error","filePath":"pulsar-broker-auth-sasl/src/main/java/org/apache/pulsar/broker/authentication/PulsarSaslServer.java","lineNumber":160,"sourceCode":"            log.error().exception(e).log(\"response: Failed to evaluate client token\");\n            throw new AuthenticationException(e.getMessage());\n        }\n    }\n\n    static class SaslServerCallbackHandler implements CallbackHandler {\n        Pattern allowedIdsPattern;\n\n        public SaslServerCallbackHandler(Pattern pattern) {\n            this.allowedIdsPattern = pattern;\n        }\n\n        @Override\n        public void handle(Callback[] callbacks) throws UnsupportedCallbackException {\n            for (Callback callback : callbacks) {\n                if (callback instanceof AuthorizeCallback) {\n                    handleAuthorizeCallback((AuthorizeCallback) callback);\n                } else {\n                    throw new UnsupportedCallbackException(callback, \"Unrecognized SASL GSSAPI Server Callback.\");\n                }\n            }\n        }\n\n        private void handleAuthorizeCallback(AuthorizeCallback ac) {\n            String authenticationID = ac.getAuthenticationID();\n            String authorizationID = ac.getAuthorizationID();\n            if (!authenticationID.equals(authorizationID)) {\n                ac.setAuthorized(false);\n                log.info().attr(\"authenticationID\", authenticationID).attr(\"authorizationID\", authorizationID)\n                        .log(\"Forbidden access to client\");\n                return;\n            }\n            if (!allowedIdsPattern.matcher(authenticationID).matches()) {\n                ac.setAuthorized(false);\n                log.info()\n                    .attr(\"authenticationID\", authenticationID)\n                    .attr(\"property\", SaslConstants.JAAS_CLIENT_ALLOWED_IDS)","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker-auth-sasl/src/main/java/org/apache/pulsar/broker/authentication/PulsarSaslServer.java#L142-L178","documentation":"UnsupportedCallbackException thrown by PulsarSaslServer.SaslServerCallbackHandler.handle when the SASL framework presents a Callback type the handler does not recognize. This server-side handler only supports AuthorizeCallback, which is what the GSSAPI mechanism supplies; any other callback type (e.g. NameCallback, PasswordCallback from a mechanism mismatch) reaches the default branch and throws.","triggerScenarios":"SaslServer.evaluateResponse (via PulsarSaslServer.response) triggers the underlying SaslServer's callback handler with a callback other than AuthorizeCallback — typically when the negotiated/configured mechanism is not plain GSSAPI (e.g. SASL mechanism negotiation picks DIGEST-MD5/PLAIN which request NameCallback/PasswordCallback), or a custom Sasl factory injects extra callbacks.","commonSituations":"Client and broker disagree on SASL mechanism (client configured for a mechanism requiring name/password callbacks while the server was built for GSSAPI); a custom SaslServerFactory is on the classpath adding callbacks; broker code modified to create the SaslServer with additional mechanism properties.","solutions":["Ensure the SaslServer is created strictly for the GSSAPI mechanism (Sasl.createSaslServer(\"GSSAPI\", ...) as done in PulsarSaslServer.createSaslServer) and the client negotiates GSSAPI/Kerberos only","Check client SASL config (jaas.conf / mechanism properties) so it does not fall back to PLAIN or DIGEST-MD5 against this broker","If you extended the handler for other mechanisms, add instanceof branches for the expected callback types (NameCallback, PasswordCallback, etc.) before the throw","Remove/verify any custom SaslServerFactory or security.provider registrations that could alter the callback list"],"exampleFix":"// before\nif (callback instanceof AuthorizeCallback) {\n    handleAuthorizeCallback((AuthorizeCallback) callback);\n} else {\n    throw new UnsupportedCallbackException(callback, \"Unrecognized SASL GSSAPI Server Callback.\");\n}\n// after\nif (callback instanceof AuthorizeCallback) {\n    handleAuthorizeCallback((AuthorizeCallback) callback);\n} else if (callback instanceof NameCallback) {\n    ((NameCallback) callback).setName(\"\");\n} else {\n    throw new UnsupportedCallbackException(callback, \"Unrecognized SASL GSSAPI Server Callback.\");\n}","handlingStrategy":"try-catch","validationCode":"// confirm the client negotiates GSSAPI only\nif (!saslMechanism.equals(\"GSSAPI\")) {\n    throw new IllegalArgumentException(\"Server supports GSSAPI only; got \" + saslMechanism);\n}","typeGuard":"boolean isSupportedCallback(Callback c) {\n    return c instanceof AuthorizeCallback;\n}","tryCatchPattern":"try {\n    return AuthData.of(saslServer.evaluateResponse(token.getBytes()));\n} catch (SaslException e) {\n    Throwable cause = e;\n    while ((cause = cause.getCause()) != null) {\n        if (cause instanceof UnsupportedCallbackException) {\n            throw new AuthenticationException(\"SASL mechanism/callback mismatch; client must use GSSAPI\");\n        }\n    }\n    throw new AuthenticationException(e.getMessage());\n}","preventionTips":["Keep client and broker SASL mechanism configuration aligned (both Kerberos/GSSAPI)","Do not register custom Sasl factories or providers that add callbacks to the GSSAPI exchange","When extending the handler to new mechanisms, add explicit instanceof branches for every callback the mechanism requests","Test SASL handshake end-to-end after any JDK upgrade, as GSSAPI callback lists can vary"],"tags":["sasl","kerberos","callback","authentication"],"backgroundTag":"unsupported-sasl-callback","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"}