apache/pulsar · error · AuthenticationException

Authentication has not completed

Error message

Authentication has not completed

What it means

AuthenticationStateOpenID.getAuthRole() returns the authenticated role extracted from the validated JWT, which is stored only after authenticateAsync() successfully validates the token. If getAuthRole() is called before authentication has completed (role is still null), the state throws AuthenticationException("Authentication has not completed"). It is a lifecycle/ordering guard: the broker framework (or custom code) asked for the authenticated identity too early in the connection handshake.

Source

Thrown at pulsar-broker-auth-oidc/src/main/java/org/apache/pulsar/broker/authentication/oidc/AuthenticationStateOpenID.java:54

    private AuthenticationDataSource authenticationDataSource;
    private volatile String role;
    private final SocketAddress remoteAddress;
    private final SSLSession sslSession;
    private volatile long expiration;

    AuthenticationStateOpenID(
            AuthenticationProviderOpenID provider,
            SocketAddress remoteAddress,
            SSLSession sslSession) {
        this.provider = provider;
        this.remoteAddress = remoteAddress;
        this.sslSession = sslSession;
    }

    @Override
    public String getAuthRole() throws AuthenticationException {
        if (role == null) {
            throw new AuthenticationException("Authentication has not completed");
        }
        return role;
    }

    @Deprecated
    @Override
    public AuthData authenticate(AuthData authData) throws AuthenticationException {
        // This method is not expected to be called and is subject to removal.
        throw new AuthenticationException("Not supported");
    }

    @Override
    public CompletableFuture<AuthData> authenticateAsync(AuthData authData) {
        final String token = new String(authData.getBytes(), UTF_8);
        this.authenticationDataSource = new AuthenticationDataCommand(token, remoteAddress, sslSession);
        return provider
                .authenticateTokenAsync(authenticationDataSource)
                .thenApply(jwt -> {

View on GitHub (pinned to 820761864e)

Solutions

  1. Ensure the authentication handshake completes before reading the role: check state.isComplete() first, and only then call getAuthRole().
  2. Make sure the client is actually sending its token (Authorization: Bearer <token> / authData) so authenticateAsync() runs and sets the role.
  3. If orchestrating manually, wait for the CompletableFuture returned by authenticateAsync() to complete before calling getAuthRole().
  4. Verify token validation is not failing silently upstream (check broker logs for JWT/JWKS/issuer errors that leave role null).

Example fix

// before
String role = authState.getAuthRole();
// after
if (authState.isComplete()) {
    String role = authState.getAuthRole();
} else {
    // wait for authenticateAsync() to finish or reject the connection
}
Defensive patterns

Strategy: validation

Validate before calling

if (authState.isComplete()) {
    String role = authState.getAuthRole();
} else {
    // defer: wait for authenticateAsync() to finish before reading the role
}

Type guard

boolean hasAuthRole(AuthenticationState state) {
    return state.isComplete(); // role != null for AuthenticationStateOpenID
}

Try / catch

try {
    String role = authState.getAuthRole();
} catch (AuthenticationException e) {
    // authentication not finished: re-run handshake or reject connection
}

Prevention

When it happens

Trigger: Calling getAuthRole() on an AuthenticationStateOpenID instance before authenticateAsync() has completed successfully — e.g. calling it during the initial connection setup before the client sent its token, calling it after a failed or still-pending token validation future, or calling it synchronously while the async validation is still in flight.

Common situations: Broker plugins or protocol handlers that call getAuthRole() before checking AuthenticationState.isComplete(); a client that connects but never sends a Bearer token, so validation never runs; slow IdP discovery/JWKS fetch delaying completion while the broker polls for the role; a failed JWT validation leaving role null and then a retry path reading the role anyway.

Understand the failure class

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/6dd3c41ada4f1ee1. Report an issue: GitHub.