0

Is there a way to implement preemptive basic authentication with Apache HttpClient 5.2 using a HttpRequestInterceptor similar to how it's done here (accepted response) for Apache HttpClient 4?

We use Apache HttpClient both directly and as the RestTemplate underlying Http client. Preemptive auth used to work with HttpClient 4 using HttpRequestInterceptor (see the accepted response for the question I linked above), but we can't reuse the same code as the AuthScheme class is now gone.

I tried a couple of things but neither worked (see below). Every first request gets a 401 from the server, the client retries with basic auth and gets a successful response. This is not the behaviour I want though, the first request should add the Authorisation header with Basic auth as it used to.

@Component
public class HttpClientPreemptiveAuthInterceptor implements HttpRequestInterceptor {

    @Override
    public void process(HttpRequest httpRequest, EntityDetails entityDetails, HttpContext httpContext) throws IOException, HttpException {

        // Apparently, we have two options:
        // - Set HttpContext AuthCache with Basic auth for the target.
        // - Copy org.apache.hc.client5.http.ContextBuilder#preemptiveBasicAuth()
        // The above method is available for HttpClientContext creation. Not sure why it doesn't set AuthCache though,
        // but instead adds an entry to the AuthExchange map of the given http context.

        HttpClientContext httpClientContext = (HttpClientContext) httpContext;

        // get the target host from the http context
        RouteInfo routeInfo = httpClientContext.getHttpRoute();
        if (routeInfo != null) {
            HttpHost targetHost = routeInfo.getTargetHost();
            AuthExchange authExchange = httpClientContext.getAuthExchange(targetHost);

            if (authExchange.getAuthScheme() == null) {
                CredentialsProvider credentialsProvider = httpClientContext.getCredentialsProvider();
                Credentials credentials = credentialsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort()), httpClientContext);
                if (credentials == null) {
                    throw new HttpException("No credentials provided for preemptive authentication");
                }
                BasicScheme authScheme = new BasicScheme();
                authScheme.initPreemptive(credentials);
                //authExchange.select(authScheme);
                //httpClientContext.setAuthExchange(targetHost, authExchange);
                DefaultSchemePortResolver schemePortResolver = DefaultSchemePortResolver.INSTANCE;
                httpClientContext.resetAuthExchange(RoutingSupport.normalize(targetHost, schemePortResolver), authScheme);
            }

        }


        /** Second approach
        // get the target host from the http context
        RouteInfo routeInfo = httpClientContext.getHttpRoute();
        if (routeInfo != null) {
            HttpHost targetHost = routeInfo.getTargetHost();
            // add Basic Auth for the target host (credentials for this host will be selected from the CredentialsProvider)
            httpClientContext.resetAuthExchange(targetHost, new BasicScheme());
        }
         **/

        // Third approach
        // Create AuthCache instance
        // final AuthCache authCache = new BasicAuthCache();
        // Generate BASIC scheme object and add it to the local auth cache
        // authCache.put(targetHost, new BasicScheme());
        // httpClientContext.setAuthCache(new BasicAuthCache());
    }
M. Deinum
  • 115,695
  • 22
  • 220
  • 224
dascolagi
  • 107
  • 2
  • 12
  • What does happen? Does it go into the if branches? Isn't it possible to use the sample on the Apache HttpClient website? Or don't use the `HttpClient` directly and use the `BasicAuthInterceptor` for the `RestTemplate` to set the header. – M. Deinum Jul 12 '23 at 13:42
  • [This sample here](https://www.springcloud.io/post/2022-08/httpclient5/#gsc.tab=0) sets a `request-id` header before performing the actual request via request interceptors. Replacing `request-id` with `Authorization` and the long value with the actual basic-auth authorization string shouldn't be to difficult I guess. Not sure though if there might be any further hurdles using HttpClient 5.2. – Roman Vottner Jul 12 '23 at 13:51
  • @M.Deinum yeah it goes into the if branches. If a inspect the httpClientCOntext instance at the end of the process method, it seems to have everything it needs. The authExchange with basicScheme is there, host and credentials seem OK, still doesn't work. The Authorization header is not there for the first request. – dascolagi Jul 12 '23 at 14:16

0 Answers0