mutinyquarkus-reactivequarkus-oidc

Is it possible to chain Smallrye Uni<T> on the OIDC client


I'm attempting to add offline token (API) access to an application. I'm looking to take an "Authorization: API $REFRESH_TOKEN" header and sending it through the oidc client to convert to an access/bearer token before having the bearer token used against Keycloak authorization services.

My problem is how do I deal with needing the Uni<Tokens> response from the oidc client without blocking and returning the Uni<SecurityIdentity> on the authenticate call. The calls that I am doing work, but I have to add a 2 second sleep, which obviously is not what I want. I often struggle with reactive programming. Can I subscribe the Uni<SecurityIdentity> to the Uni<Tokens>, or chain them.

Code

@Alternative
@Priority(1)
@ApplicationScoped
public class DexAuthenticationMechanism implements HttpAuthenticationMechanism {

    @Inject
    OidcAuthenticationMechanism oidc;

    @Inject
    OidcClients oidcClients;

    ExecutorService executor = Executors.newFixedThreadPool(10, r -> {
        return new Thread(r, "CUSTOM_TASK_EXECUTION_THREAD");
    });


    @Override
    public Uni<SecurityIdentity> authenticate(RoutingContext context, IdentityProviderManager identityProviderManager) {
        String authHeader = context.request().headers().get(HttpHeaders.AUTHORIZATION);
        String[] authSplit = authHeader.split("\\s+");
        if (authSplit.length == 2) {
            if ("BEARER".equalsIgnoreCase(authSplit[0])) {
                return oidc.authenticate(context, identityProviderManager);
            } else if ("BASIC".equalsIgnoreCase(authSplit[0])) {
                String accessToken = "";
                OidcClient basic = oidcClients.getClient("basic");
                context.request().headers().set(HttpHeaders.AUTHORIZATION, "Bearer "+accessToken);
                oidc.authenticate(context, identityProviderManager);
            } else if ("API".equalsIgnoreCase(authSplit[0])) {
                OidcClient api = oidcClients.getClient("api");
                Uni<Tokens> tokensUni = api.refreshTokens(authSplit[1]);
                tokensUni.runSubscriptionOn(executor)
                        .subscribe().with(
                            item -> context.request().headers().set(HttpHeaders.AUTHORIZATION, "Bearer " + item.getAccessToken())
                            , fail -> fail.printStackTrace()
                        );
                System.out.println(context.request().headers().get(HttpHeaders.AUTHORIZATION));
                //DON'T DO THIS
                try {
                    Thread.sleep(2000);
                }catch (Exception e){}
                System.out.println("After invoke");
                System.out.println(context.request().headers().get(HttpHeaders.AUTHORIZATION));
                return oidc.authenticate(context, identityProviderManager);
            } else {
                System.out.println("Cheese and crackers");
            }
        } else{
            System.out.println("Double cheese and crackers");
        }
        return Uni.createFrom().failure(new AuthenticationFailedException());
    }

Config

#quarkus.oidc-client.default
quarkus.oidc-client.enabled=true
quarkus.oidc-client.auth-server-url=${keycloakAuthServerUrl}/realms/${keycloakRealm}
quarkus.oidc-client.discovery-enabled=true
quarkus.oidc-client.client-id=${keycloakResource}
quarkus.oidc-client.credentials.secret=${keycloakCredentialsSecret}
quarkus.oidc-client.grant.type=client
###quarkus.oidc-client.proxy.host=http://${squidProxyHost}
###quarkus.oidc-client.proxy.port=${squidProxyPort}
#end quarkus.oidc-client.default

#quarkus.oidc-client.default
quarkus.oidc-client."api".enabled=true
quarkus.oidc-client."api".auth-server-url=${keycloakAuthServerUrl}/realms/${keycloakRealm}
quarkus.oidc-client."api".discovery-enabled=true
quarkus.oidc-client."api".client-id=${keycloakResource}
quarkus.oidc-client."api".credentials.secret=${keycloakCredentialsSecret}
quarkus.oidc-client."api".grant.type=refresh
###quarkus.oidc-client.proxy.host=http://${squidProxyHost}
###quarkus.oidc-client.proxy.port=${squidProxyPort}
#end quarkus.oidc-client.default

Is there a way update the RoutingContext being used on the "return oidc.authenticate(context, identityProviderManager);" such that it will subscribe for the authorization header update?


Solution

  • So after reviewing the tags and changing smallrye to mutiny, I almost immediately found and example How can I perform via mutiny sequential calls of services?.

    return tokensUni.chain(() -> oidc.authenticate(context, identityProviderManager));

    If anyone knows how to flag this to the other answer directly let me know.