0

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?

Johannes
  • 6,490
  • 10
  • 59
  • 108

2 Answers2

1

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

Sergey Beryozkin
  • 688
  • 1
  • 4
  • 9
  • Note you don't need to use path-based authentication unless you have more than one mechanism, having `@Authenticated` on the endpoint class which needs to be secured is enough – Sergey Beryozkin Nov 10 '22 at 11:11
  • I have more than one authentication and I also need to change authentication methods based on configuration. – Johannes Nov 11 '22 at 11:32
  • I am staring at the link contents and your answer and I do not understand what I am missing here. Can you give me another hint? – Johannes Nov 11 '22 at 11:40
  • Sure, you can ignore `getCredentialTransport` method returning `Uni`, and no need to override `getPriority` for now, `getCredentialTypes` should likely return `io.quarkus.security.identity.request.TokenAuthenticationRequest`, but as far as path based selection is concerned, `getCredentialTransport` has to have `webhook` passed as a 2nd param to `HttpCredentialTransport` constructor, the first param describes the carrier of the credentials, it is not essential, choose the enum which is close enough. Perhaps try to get it working without the path based selection first – Sergey Beryozkin Nov 14 '22 at 10:22
  • Is there an implementation guide? All I can find is https://quarkus.io/guides and that does not help me at all. I can see your comments and I attempt getting things to work as you suggest but I feel like my java/quarkus gap is too big. – Johannes Nov 14 '22 at 13:22
0

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.

Johannes
  • 6,490
  • 10
  • 59
  • 108
  • I think it makes sense to have such discussions directly in Quarkus to avoid some misunderstandings. I'm sorry, I'm not sure what did you expect by delegating to default interface methods. StackOverflow is best suited to concrete advices/answers. So rather than making such generalizations, please do come to us to Quarkus Discussions and will help there – Sergey Beryozkin Nov 17 '22 at 18:52