quarkus icon indicating copy to clipboard operation
quarkus copied to clipboard

Add RoutingContext to SecurityIdentity for mTLS authentication

Open lorenzobenvenuti opened this issue 3 years ago • 6 comments

Description

Hi,

I'm trying to access the current request in a SecurityIdentityAugmentor. Since injecting RoutingContext doesn't seem to work (I tried with and without @ActivateRequestContext), I'm wondering if RoutingContext can be passed in a SecurityIdentity attribute. I see it's already done in OidcIdentityProvider (here) and MpJwtValidator (here). For my use case, I'd like to have the same behavior for mTLS authentication (X509IdentityProvider). If you think it's valuable, I can try to put an MR together.

Thanks,

lorenzo

Implementation ideas

MtlsAuthenticationMechanism is already adding the RequestContext to the AuthenticationRequest attributes, I think it only needs to be forwarded to the SecurityIdentity attributes in X509IdentityProvider:

X509Certificate certificate = request.getCertificate().getCertificate();
RoutingContext routingContext = HttpSecurityUtils.getRoutingContextAttribute(request);
if (routingContext != null) {
    builder.addAttribute(RoutingContext.class.getName(), routingContext);
}
QuarkusSecurityIdentity.Builder builder = QuarkusSecurityIdentity.builder()
                .setPrincipal(certificate.getSubjectX500Principal())
                .addCredential(request.getCertificate())               
return Uni.createFrom().item(builder.build());

lorenzobenvenuti avatar Sep 30 '22 15:09 lorenzobenvenuti

/cc @sberyozkin

quarkus-bot[bot] avatar Sep 30 '22 15:09 quarkus-bot[bot]

Hi @lorenzobenvenuti Sure, please do it

sberyozkin avatar Sep 30 '22 16:09 sberyozkin

It turns out that implementing this feature is harder than I thought: I didn't notice that X509IdentityProvider is defined in the quarkus-security module and it doesn't have access to RoutingContext and HttpSecurityUtils. I thought I could move attribute handling to HttpAuthenticator (that way RoutingContext will be added to SecurityIdentity attributes for all the HTTP-based authentication mechanism) but since the augmentors are invoked at the IdentityProvider level, it won't solve my issue.

I can think of different solutions, but TBH I don't like any of them.

  • Option 1: define the mappings between AuthenticationRequest and SecurityIdentity somewhere; for example, introducing an AttributeMapper interface that declares a Map<String, String> getAttributeMappings() method. X509IdentityProvider could inject a Instance<AttributeMapper> and, if present, copy all the attributes defined in the mappings. Then, the HTTP extension could add an AttributeMapper implementation to the context, mapping quarkus.http.routing.context to RoutingContext.class.getName().

  • Option 2: same as above, but passing the mappings in an attribute. MtlsAuthenticationMechanism can add a quarkus.security.attributes attribute to the AuthenticationRequest, containing a Map<String,String>, and X509IdentityProvidercan read that attribute and copy the other attributes to theSecurityIdentity` instance

  • Option 3, really ugly hack: read/write the attribute using strings to avoid requiring an explicit dependency, i.e.

      /* In X509IdentityProvider */
      QuarkusSecurityIdentity.builder()
        /* ... */
        .addAttribute(
          "io.vertx.ext.web.RoutingContext",
          request.getAttribute("quarkus.http.routing.context")
        )
        /* ... */
    

    Just kidding, I refuse to write this code :-D

For options 1 and 2, attributes could also be copied in IdentityProviderManager to share the logic across all the identity providers.

Thoughts?

Thanks,

lorenzo

lorenzobenvenuti avatar Oct 04 '22 07:10 lorenzobenvenuti

Another (hack-ish) option: add this bean to the vertx-http module

@Priority(1000)
public class RoutingContextAwareX509IdentityProvider extends X509IdentityProvider {

    @Override
    public Uni<SecurityIdentity> authenticate(CertificateAuthenticationRequest request, AuthenticationRequestContext context) {
        final Uni<SecurityIdentity> authenticate = super.authenticate(request, context);
        return authenticate.onItem().transform(
            it -> QuarkusSecurityIdentity.builder(it)
                    .addAttribute(RoutingContext.class.getName(), HttpSecurityUtils.getRoutingContextAttribute(request))
                    .build()
        );
    }

}

This is an HTTP-oriented implementation of X509IdentityProvider that will be invoked before X509IdentityProvider because of the highest priority.

lorenzobenvenuti avatar Oct 10 '22 10:10 lorenzobenvenuti

@lorenzobenvenuti Hi, sorry for a delay, so may be the simplest is for your application to ship such a custom provider ?

sberyozkin avatar Oct 11 '22 12:10 sberyozkin

Hi @sberyozkin yes, actually that's what we're doing right now. I was just wondering if someone else could benefit from moving this class to the framework, but probably it's a pretty unique use case (mTLS + need to access an header) and it's not worth the effort: if someone else needs something similar they can just write their own IdentityProvider.

Thanks,

lorenzo

lorenzobenvenuti avatar Oct 11 '22 12:10 lorenzobenvenuti