Search code examples
javakeycloakquarkusvert.xreactive

Quarkus - Keycloak client in ServerRequestFilter in reactive context


I'm using Quarkus with quarkus-resteasy-reactive and the quarkus-keycloak-admin-client-reactive extensions. I'm building a ServerRequestFilter that queries keycloak for a user attribute, but even though I'm using the reactive client, I'm getting BlockingNotAllowedException when calling its methods.

Here's the filter:

public class Filters {
    @Inject
    Keycloak keycloak;

    @ServerRequestFilter
    public Uni<Response> filter(ContainerRequestContext requestContext) {
        return Uni.createFrom().item(() -> 
                keycloak.realm("my-realm")
                        .users()
                        .search("user-that-i-get-from-context")
                        .stream()
                        .findFirst()
                        .orElseThrow()
                        .firstAttribute("the-attribute")).map(attr -> {
                            if (attr.equals("some-value")){
                                return null;
                            }
                            return Response.status(403).build();
                        });
    }
}

I've tried adding runSubscriptionOn(Infrastructure.getDefaultWorkerPool()) to the Uni but the error persists.

Making the requests blocking is not an option, as I want my endpoints to be reactive.

I suspect the problem is somewhere in the context of the filter and in the injection of the Keycloak client, but I've been unable to pinpoint the problem


Solution

  • Unfortunately, KeycloakClient uses classic ResteasyClient blocking and not ResteasyReactiveClient.

    I solved it using Vertx like this:

    import io.vertx.mutiny.core.Vertx;
    
    public class Filters {
    
        @Inject Keycloak keycloak;
        @Inject Vertx vertx;
    
        @ServerRequestFilter
        public Uni<Response> filter(ContainerRequestContext requestContext) {
            return vertx.getOrCreateContext().executeBlocking(
                Uni.createFrom().emitter(emitter -> {
                    var attr = keycloak.realm("my-realm")
                            .users()
                            .search("user-that-i-get-from-context")
                            .stream()
                            .findFirst()
                            .orElseThrow()
                            .firstAttribute("the-attribute");
    
                    if ("some-value".equals(attr)) {
                        emitter.complete(null);
                    } else {
                        emitter.complete(Response.status(403).build());
                    }
                }));
        }
    }