title: How do I stop Apache HttpClient asking for credentials three times for a single request?
body: <p>I'm using Apache HttpClient to connect to a web server which is running Jetty and using WAFFLE for authentication. I'm having a problem where the server sends three <code>WWW-Authenticate</code> headers (because it does support three authentication schemes) but then on the client, <code>CredentialsProvider</code> is being asked three times for the same credentials.</p>
  
  <p>If I connect to the same server with the built-in Java <code>URL</code> class, it gets the resource without asking me for credentials.</p>
  
  <p>The HTTP client setup is fairly straight-forward:</p>
  
  <pre><code>RequestConfig requestConfig =
      HttpClientUtils.createDefaultRequestConfig();
  HttpClientBuilder builder = HttpClients.custom()
      .setDefaultCredentialsProvider(credentialsProvider)
      .setDefaultCookieStore(cookieStore)
      .setDefaultRequestConfig(requestConfig);
  return builder.build();
  </code></pre>
  
  <p>The <code>CredentialsProvider</code> is a custom one which delegates to our own framework for prompting the user for that information.</p>
  
  <pre><code>public class AdaptedCredentialsProvider
      implements CredentialsProvider
  {
      private final CredentialsProvider internal =
          new BasicCredentialsProvider();
          //I tried this too:
          //new SystemDefaultCredentialsProvider();
  
      private final CredentialsPrompt prompt;
  
      public AdaptedCredentialsProvider(CredentialsPrompt prompt) {
          this.prompt = prompt;
      }
  
      @Override
      public void setCredentials(AuthScope authscope,
                                 Credentials credentials) {
          internal.setCredentials(authscope, credentials);
      }
  
      @Override
      public Credentials getCredentials(@NotNull AuthScope authScope) {
          System.err.println("Asked for credentials, scheme: " +
                             authScope.getScheme());
  
          Credentials localCredentials =
              internal.getCredentials(authScope);
          if (localCredentials != null) {
              return localCredentials;
          }
  
          if (authScope.getHost() != null) {
              OurCredentials credentials = prompt.prompt(
                  authScope.getHost(),
                  authScope.getPort());
  
              if (credentials != null) {
                  switch (authScope.getScheme()) {
                      case "NEGOTIATE":
                      case "NTLM":
                          return new NTCredentials(
                              credentials.getUsername(),
                              new String(credentials.getPassword()),
                              null, null);
  
                      default:
                          return new UsernamePasswordCredentials(
                              credentials.getUsername(),
                              new String(credentials.getPassword()));
                  }
              }
          }
  
          return null;
      }
  
      @Override
      public void clear() {
          internal.clear();
      }
  }
  </code></pre>
  
  <p>A timeline of the requests and responses with the login requests interspersed is interesting to look at...</p>
  
  <pre><code>=== Initial request
  === Request
  GET /api/noop HTTP/1.1
  Host: 192.168.1.162:27443
  Connection: Keep-Alive
  Accept-Encoding: gzip,deflate
  === Response
  HTTP/1.1 401 Unauthorized
  Date: Mon, 27 Apr 2015 02:44:23 GMT
  Set-Cookie: JSESSIONID=1lsrefm3yzmmz15n8md7efdc7f;Path=/;Secure
  WWW-Authenticate: Negotiate
  WWW-Authenticate: NTLM
  WWW-Authenticate: Basic realm="Test"
  Cache-Control: must-revalidate,no-cache,no-store
  Content-Type: text/html; charset=ISO-8859-1
  Content-Length: 289
  
  === Asked for credentials for host: 192.168.1.162 - scheme: NEGOTIATE
  
  === GUI checking whether the credentials work...
  === Request
  GET /api/noop HTTP/1.1
  Host: 192.168.1.162:27443
  Connection: Keep-Alive
  Accept-Encoding: gzip,deflate
  === Response
  HTTP/1.1 401 Unauthorized
  Date: Mon, 27 Apr 2015 02:44:30 GMT
  Set-Cookie: JSESSIONID=ucsnvsvawp301injh6ijwbywe;Path=/;Secure
  WWW-Authenticate: Negotiate
  WWW-Authenticate: NTLM
  WWW-Authenticate: Basic realm="Test"
  Cache-Control: must-revalidate,no-cache,no-store
  Content-Type: text/html; charset=ISO-8859-1
  Content-Length: 289
  === Request
  GET /api/noop HTTP/1.1
  Host: 192.168.1.162:27443
  Connection: Keep-Alive
  Accept-Encoding: gzip,deflate
  Authorization: Basic &lt;REDACTED&gt;
  === Response
  HTTP/1.1 200 OK
  Date: Mon, 27 Apr 2015 02:44:30 GMT
  Set-Cookie: JSESSIONID=1la5wb3fje1fy1qxu14weqxaqm;Path=/;Secure
  Expires: Thu, 01 Jan 1970 00:00:00 GMT
  Content-Length: 0
  
  === Asked for credentials for host: 192.168.1.162 - scheme: NTLM
  
  === GUI checking whether the credentials work...
  === Request
  GET /api/noop HTTP/1.1
  Host: 192.168.1.162:27443
  Connection: Keep-Alive
  Accept-Encoding: gzip,deflate
  === Response
  HTTP/1.1 401 Unauthorized
  Date: Mon, 27 Apr 2015 02:44:33 GMT
  Set-Cookie: JSESSIONID=5pmtxxhinky91bzoj18yx6zkz;Path=/;Secure
  WWW-Authenticate: Negotiate
  WWW-Authenticate: NTLM
  WWW-Authenticate: Basic realm="Test"
  Cache-Control: must-revalidate,no-cache,no-store
  Content-Type: text/html; charset=ISO-8859-1
  Content-Length: 289
  === Request
  GET /api/noop HTTP/1.1
  Host: 192.168.1.162:27443
  Connection: Keep-Alive
  Accept-Encoding: gzip,deflate
  Authorization: Basic &lt;REDACTED&gt;
  === Response
  HTTP/1.1 200 OK
  Date: Mon, 27 Apr 2015 02:44:33 GMT
  Set-Cookie: JSESSIONID=16jhmw3fxuhsy1edjtpquiw564;Path=/;Secure
  Expires: Thu, 01 Jan 1970 00:00:00 GMT
  Content-Length: 0
  
  === Asked for credentials for host: 192.168.1.162 - scheme: BASIC
  
  === GUI checking whether the credentials work...
  === Request
  GET /api/noop HTTP/1.1
  Host: 192.168.1.162:27443
  Connection: Keep-Alive
  Accept-Encoding: gzip,deflate    
  === Response
  HTTP/1.1 401 Unauthorized
  Date: Mon, 27 Apr 2015 02:44:36 GMT
  Set-Cookie: JSESSIONID=2yjw3xcoerq5wdtqu1etxur0;Path=/;Secure
  WWW-Authenticate: Negotiate
  WWW-Authenticate: NTLM
  WWW-Authenticate: Basic realm="Test"
  Cache-Control: must-revalidate,no-cache,no-store
  Content-Type: text/html; charset=ISO-8859-1
  Content-Length: 289
  === Request
  GET /api/noop HTTP/1.1
  Host: 192.168.1.162:27443
  Connection: Keep-Alive
  Accept-Encoding: gzip,deflate
  Authorization: Basic &lt;REDACTED&gt;
  === Response
  HTTP/1.1 200 OK
  Date: Mon, 27 Apr 2015 02:44:36 GMT
  Set-Cookie: JSESSIONID=cvf6mvdagknk1mb893u7ylozb;Path=/;Secure
  Expires: Thu, 01 Jan 1970 00:00:00 GMT
  Content-Length: 0
  
  === Presumably here it has decided it doesn't support NEGOTIATE so
  === it's going with NTLM.
  === Request
  GET /api/noop HTTP/1.1
  Host: 192.168.1.162:27443
  Connection: Keep-Alive
  Accept-Encoding: gzip,deflate
  Authorization: NTLM &lt;REDACTED&gt;
  === Response
  HTTP/1.1 401 Unauthorized
  Date: Mon, 27 Apr 2015 02:44:36 GMT
  Set-Cookie: JSESSIONID=es21dv6pnktmqoxr9qs52n3e;Path=/;Secure
  Expires: Thu, 01 Jan 1970 00:00:00 GMT
  WWW-Authenticate: NTLM &lt;REDACTED&gt;
  Transfer-Encoding: chunked
  
  0
  === Request
  GET /api/noop HTTP/1.1
  Host: 192.168.1.162:27443
  Connection: Keep-Alive
  Accept-Encoding: gzip,deflate
  Authorization: NTLM &lt;REDACTED&gt;
  === Response
  HTTP/1.1 200 OK
  Date: Mon, 27 Apr 2015 02:44:36 GMT
  Set-Cookie: JSESSIONID=1nzdiyw90zun218drgkobi3y6g;Path=/;Secure
  Expires: Thu, 01 Jan 1970 00:00:00 GMT
  Content-Length: 0
  </code></pre>
  
  <p>It seems like it isn't even using the credentials it's asking for. Eventually it somehow breaks out of this weird cycle and then tries to use them.</p>
  
  <p>It eventually logs in, but how can I stop it asking for the same information three times? (I know I could roll this sort of thing into the <code>CredentialsProvider</code>, but I didn't have to do that with the other API and it seemed like an awkward way to work around it for this one.)</p>
  
  <p>In fact, how can I stop it asking for this information at all? Largely the point of using Negotiate in the first place is to avoid being prompted for a password, but this thing is always prompting me for a password and I don't think it should be.</p>
  
  <p><em>Update:</em> Half the problem is solved. I discovered that HttpClient <em>didn't</em> support single sign-on at all (bit misleading, IMO, to claim you support NTLM while not supporting the only purpose of using NTLM!) But version 4.4 has experimental support for it.</p>
  
  <p>The experimental support appears to let me login but I still get prompted for a password for the Basic. So I'm back to the question of how to disable this behaviour of asking the credentials provider for every single auth scheme. Ideally, it should just ask for the first one and then fall back to the next one in the sequence.</p>