quarkusio / quarkus

Quarkus: Supersonic Subatomic Java.
https://quarkus.io
Apache License 2.0
13.54k stars 2.61k forks source link

Support NTLM-authentication for REST clients #39902

Open AnastasiiaL opened 5 months ago

AnastasiiaL commented 5 months ago

Description

While NTLM authentication is very outdated, some legacy systems are still using it, and integrating with those is a real challenge at the moment.

In our use case we use the reactive rest client, and it doesn't offer a way to do this authentication out of the box. Checking the documentation of the underlying eclipse.microprofile I couldn't figure the way to do that either. Afaik there's also no support for proxy with NTLM-authentication.

While we are able to get a response from the NTLM protected server using plain Java and setting the global Authenticator state to use the constructed PasswordAuthentication I can't figure a way to integrate that into the quarkus rest client.

If there's no plan to support NTLM authentication, maybe someone can still share ideas on how to make that work with what's currently available? All ideas are appreciated.

Implementation ideas

No response

quarkus-bot[bot] commented 5 months ago

/cc @cescoffier (rest-client), @geoand (rest-client)

geoand commented 5 months ago

In high level terms, what needs to be done in order to pass the proper authentication information?

sberyozkin commented 5 months ago

Probably something similar to Kerberos Client: https://docs.quarkiverse.io/quarkus-kerberos/dev/index.html#_client_support

https://github.com/quarkiverse/quarkus-kerberos/blob/main/client/src/main/java/io/quarkiverse/kerberos/client/KerberosClientRequestFilter.java

sberyozkin commented 5 months ago

I wonder if the NTLM proxy option should be supported at the Vert.x web client level, NTLM was done for the mail client: https://github.com/vert-x3/vertx-mail-client/issues/159

AnastasiiaL commented 5 months ago

@geoand on a very high level it would be nice to have something like what was done here https://github.com/quarkusio/quarkus/issues/38565 - so one can specify @ClientNTLMAuth(username=“${username}”, password=“${password}”) and those credentials will be used whenever the server asks for authentication by returning an WWW-Authenticate: NTLM-header.

In practice the implementation is trickier than the basic auth, as it involves 3 step process with exchanging the challenge. This blog has a decent concise description of the process and the example of implementation using bare java: https://schoeffm.github.io/posts/ntlm-authentication-in-java/.

So we can do this to get a response:

public class FooClient {

         String username = "";
         String password = "";
         String url = "";

    public static void main(String[] args) throws Exception {
        Authenticator.setDefault(new NtlmAuthenticator(username, password));
        InputStream stream = new URI(uri).toURL().openConnection().getInputStream();
        String response = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
        System.out.println("Received response : " + response);
        }
    }

    static class NtlmAuthenticator extends Authenticator {
        private PasswordAuthentication passwordAuthentication;

        NtlmAuthenticator(final String username, final String password) {
            this.passwordAuthentication = new PasswordAuthentication(username, password.toCharArray());
        }

        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            boolean ntlm = this.getRequestingScheme().trim().equalsIgnoreCase("NTLM");
            return ntlm ? this.passwordAuthentication : null;
        }
    }
}

but we would like to be able to do this:


@Path("/bar")
@RegisterRestClient(configKey = "foo-api")
@ClientNTLMAuth(username=“${username}”, password=“${password}”)
public interface FooClient {

    @GET
    @Path("/foobar")
    @Produces(MediaType.APPLICATION_JSON)
    FooBar retrieveFooBar();

}

if the later is not possible, I could use any advice on how to use the bare java implementation as a proxy to be able to get the response.

geoand commented 5 months ago

Thanks for the info.

We defininitely won't be adding that to the core repo, but I assume that doing something like @sberyozkin shows for Kerberos would work.