private static AuthHandler directGrant(Vertx vertx, VertxEngineConfig apimanConfig, JsonObject authConfig,
            OAuth2FlowType flowType, String role) {
        return new AuthHandler() {

            @Override
            public void handle(RoutingContext context) {
                try {
                    String[] auth = Basic.decodeWithScheme(context.request().getHeader("Authorization"));
                    doBasic2Oauth(context, role, auth[0], auth[1]);
                } catch (RuntimeException e) {
                    handle400(context, e.getMessage());
                }
            }

            private void doBasic2Oauth(RoutingContext context, String role, String username, String password) {
                JsonObject params = new JsonObject()
                        .put("username", username)
                        .put("password", password);

                MultiSiteOAuth2ClientOptions options = buildClientOptions(authConfig);
                KeycloakDiscovery.discover(vertx, options, (AsyncResult<OAuth2Auth> result) -> {
                    if (result.succeeded()) {
                        OAuth2Auth oauth2 = result.result();
                        oauth2.rbacHandler(KeycloakRBAC.create(options));
                        oauth2.authenticate(params, tokenResult -> {
                            if (tokenResult.succeeded()) {
                                log.debug("OAuth2 Keycloak exchange succeeded.");
                                AccessToken token = (AccessToken) tokenResult.result();
                                token.isAuthorised(role, res -> {
                                    if (res.result()) {
                                        context.next();
                                    } else {
                                        String message = MessageFormat.format("User {0} does not have required role: {1}.", username, role);
                                        log.error(message);
                                        handle403(context, "insufficient_scope", message);
                                    }
                                });
                            } else {
                                String message = tokenResult.cause().getMessage();
                                log.error("Access Token Error: {0}.", message);
                                handle401(context, "invalid_token", message);
                            }
                        });
                    } else {
                        log.error("Failure during discovery", result.cause());
                        handle400(context, result.cause().getMessage());
                    }
                });
            }

            private void handle400(RoutingContext context, String message) {
                if (message != null) context.response().setStatusMessage(message);
                context.fail(400);
            }

            private void handle401(RoutingContext context, String error, String message) {
                String value = MessageFormat.format("Basic realm=\"{0}\" error=\"{1}\" error_message=\"{2}\"", "apiman-gw", error, message);
                context.response().putHeader("WWW-Authenticate", value);
                context.fail(401);
            }

            private void handle403(RoutingContext context, String error, String message) {
                String value = MessageFormat.format("Basic realm=\"{0}\" error=\"{1}\" error_message=\"{2}\"", "apiman-gw", error, message);
                context.response().putHeader("WWW-Authenticate", value);
                context.fail(403);
            }

            @Override
            public AuthHandler addAuthority(String authority) {
                return this;
            }

            @Override
            public AuthHandler addAuthorities(Set<String> authorities) {
                return this;
            }

            @Override
            public void parseCredentials(RoutingContext routingContext, Handler<AsyncResult<JsonObject>> handler) {}

            @Override
            public void authorize(User user, Handler<AsyncResult<Void>> handler) {}
        };
    }
@author Marc Savy {@literal <marc@rhymewithgravy.com>}
gateway/platforms/vertx3/vertx3/src/main/java/io/apiman/gateway/platforms/vertx3/api/auth/KeycloakOAuthFactory.java