How to configure a quarkus custom HttpAuthenticationMechanism?

995 Views Asked by At

I need a static mechanism to verify my sender knows a static token. That token is hard coded into the sending system.

My API has an endpoint /webhook where I need to have that be verified.

This guides/security-customization gives an example on how to implement a custom mechanism, so I implemented this:

@Singleton
public class FixedTokenAuthenticationMechanism implements HttpAuthenticationMechanism {

    @Override
    public Uni<SecurityIdentity> authenticate(RoutingContext context, IdentityProviderManager identityProviderManager) {
        String authHeader = context.request().headers().get("magic_header");
        if (authHeader == "magic_value")
        {
            return Uni.createFrom().optional(Optional.empty());
        }
        else
        {
            return Uni.createFrom().optional(Optional.empty());
        }
    }

    @Override
    public Uni<ChallengeData> getChallenge(RoutingContext context) {
        return null;
    }

    @Override
    public Set<Class<? extends AuthenticationRequest>> getCredentialTypes() {
        return Collections.singleton(AuthenticationRequest.class);
    }

    @Override
    public Uni<Boolean> sendChallenge(RoutingContext context) {
        return HttpAuthenticationMechanism.super.sendChallenge(context);
    }

    @Override
    public HttpCredentialTransport getCredentialTransport() {
        return HttpAuthenticationMechanism.super.getCredentialTransport();
    }

    @Override
    public Uni<HttpCredentialTransport> getCredentialTransport(RoutingContext context) {
        return HttpAuthenticationMechanism.super.getCredentialTransport(context);
    }

    @Override
    public int getPriority() {
        return HttpAuthenticationMechanism.super.getPriority();
    }
}

I do not know how to configure this to be used in the application properties.

There seems to be a configuration for path-specific-authentication-mechanisms which I can not seem to make work.

what would I need to configure in aplication.properties to use my not so secure security mechanism for the /webhook endpoint?

2

There are 2 best solutions below

5
On

Right now this implementation is incomplete, but as far as path-based authentication is concerned, you need to have an alias like webhook that you can refer to from the configuration, see https://github.com/quarkusio/quarkus/blob/main/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/BasicAuthenticationMechanism.java#L198 (note, basic is a qualifier) as well the rest of the class on how to implement the custom mechanism. Delegating to the default interface implementation will likely not work

1
On

User: Sergey Beryozkin got me on the right Track.

package org.eon.webhookingestor.receiver;

import io.quarkus.arc.Priority;
import io.quarkus.oidc.IdTokenCredential;
import io.quarkus.security.AuthenticationFailedException;
import io.quarkus.security.identity.IdentityProviderManager;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.security.identity.request.AuthenticationRequest;
import io.quarkus.security.identity.request.TokenAuthenticationRequest;
import io.quarkus.security.runtime.QuarkusPrincipal;
import io.quarkus.security.runtime.QuarkusSecurityIdentity;
import io.quarkus.vertx.http.runtime.security.ChallengeData;
import io.quarkus.vertx.http.runtime.security.HttpAuthenticationMechanism;
import io.quarkus.vertx.http.runtime.security.HttpCredentialTransport;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.web.RoutingContext;

import javax.enterprise.inject.Alternative;
import javax.inject.Singleton;
import java.util.Collections;
import java.util.Set;

@Alternative
@Singleton
@Priority(900)
public class MagicAuthenticationMechanism implements HttpAuthenticationMechanism {

    //related docs:
    // https://quarkus.io/guides/security#security-architecture
    // https://quarkus.io/guides/security-customization
    //related so: https://stackoverflow.com/questions/73120309/quarkus-returning-empty-body-response-for-unauthorize
    // related on so:
    // https://stackoverflow.com/questions/74384603/how-to-configure-a-quarkus-custom-httpauthenticationmechanism
    // https://stackoverflow.com/questions/73120309/quarkus-returning-empty-body-response-for-unauthorize

    @Override
    public Uni<SecurityIdentity> authenticate(RoutingContext context, IdentityProviderManager identityProviderManager) {
        String magic_key = "magic_header";
        String header_value = context.request().headers().get(magic_key);
        String magic_value = "magic_value";
        if ( !magic_value.equals(header_value))
        {
            return Uni.createFrom().failure(new AuthenticationFailedException());
        }

        return Uni.createFrom().item(
                QuarkusSecurityIdentity.builder()
                        .setPrincipal(new QuarkusPrincipal(magic_key))
                        .addCredential(new IdTokenCredential(header_value))
                        .build());
    }

    @Override
    public Uni<ChallengeData> getChallenge(RoutingContext context) {
        //https://quarkus.io/guides/security-customization#dealing-with-more-than-one-httpauthenticationmechanism
        //If no credentials are provided then the mechanism specific challenge is created
        throw new RuntimeException ("no credentials were provided");
    }

    @Override
    public Set<Class<? extends AuthenticationRequest>> getCredentialTypes() {
        //needs to inherit from io.quarkus.security.identity.request.AuthenticationRequest
        return  Collections.singleton(TokenAuthenticationRequest.class);
    }

    @Override
    public Uni<Boolean> sendChallenge(RoutingContext context) {
        // a failed authentication will check if a challenge should be sent
        return Uni.createFrom().item(Boolean.FALSE);
    }

    @Override
    public Uni<HttpCredentialTransport> getCredentialTransport(RoutingContext context) {
        return Uni.createFrom().item(new HttpCredentialTransport(HttpCredentialTransport.Type.OTHER_HEADER, "magic"));
    }

    @Override
    public int getPriority() {
        return 900;
    }
}

with the configuration:

quarkus.http.auth.permission.magic.paths=/webhook
quarkus.http.auth.permission.magic.policy=authenticated
quarkus.http.auth.permission.magic.auth-mechanism=magic

Results in a magic authentication that works on my machine.

At this point I would advice against implementing and using it since the quarkus authentication is entirely unpredictable to me and maybe you if you read this post.

Excessive Testing of all endpoints is advised.