Chapter 4. HTTP authentication

HttpClient provides full support for authentication schemes defined by the HTTP standard specification. HttpClient's authentication framework can also be extended to support non-standard authentication schemes such as NTLM and SPNEGO.

4.1. User credentials

Any process of user authentication requires a set of credentials that can be used to establish user identity. In the simplest form user crednetials can be just a user name / password pair. UsernamePasswordCredentials represents a set of credentials consisting of a security principal and a password in clear text. This implementation is sufficient for standard authentication schemes defined by the HTTP standard specification.

UsernamePasswordCredentials creds = new UsernamePasswordCredentials("user", "pwd");
System.out.println(creds.getUserPrincipal().getName());
System.out.println(creds.getPassword());
        

stdout >

user
pwd

NTCredentials is a Microsoft Windows specific implementation that includes in addition to the user name / password pair a set of additional Windows specific attributes such as a name of the user domain, as in Microsoft Windows network the same user can belong to multiple domains with a different set of authorizations.

NTCredentials creds = new NTCredentials("user", "pwd", "workstation", "domain");
System.out.println(creds.getUserPrincipal().getName());
System.out.println(creds.getPassword());

stdout >

DOMAIN/user
pwd

4.2. Authentication schemes

The AuthScheme interface represents an abstract challenge-response oriented authentication scheme. An authentication scheme is expected to support the following functions:

  • Parse and process the challenge sent by the target server in response to request for a protected resource.

  • Provide properties of the processed challenge: the authentication scheme type and its parameters, such the realm this authentication scheme is applicable to, if available

  • Generate authorization string for the given set of credentials and the HTTP request in response to the actual authorization challenge.

Please note authentication schemes may be stateful involving a series of challenge-response exchanges.

HttpClient ships with several AuthScheme implementations:

  • Basic:  Basic authentication scheme as defined in RFC 2617. This authentication scheme is insecure, as the credentials are transmitted in clear text. Despite its insecurity Basic authentication scheme is perfectly adequate if used in combination with the TLS/SSL encryption.

  • Digest.  Digest authentication scheme as defined in RFC 2617. Digest authentication scheme is significantly more secure than Basic and can be a good choice for those applications that do not want the overhead of full transport security through TLS/SSL encryption.

  • NTLM:  NTLM is a proprietary authentication scheme developed by Microsoft and optimized for Windows platforms. NTLM is believed to be more secure than Digest. This scheme is requires an external NTLM engine to be functional. For details please refer to the NTLM_SUPPORT.txt document included with HttpClient distributions.

4.3. HTTP authentication parameters

These are parameters that be used to customize HTTP authentication process and behaviour of individual authentication schemes:

  • 'http.protocol.handle-authentication':  defines whether authentication should be handled automatically. This parameter expects a value of type java.lang.Boolean. If this parameter is not set HttpClient will handle authentication automatically.

  • 'http.auth.credential-charset':  defines the charset to be used when encoding user credentials. This parameter expects a value of type java.lang.String. If this parameter is not set US-ASCII will be used.

4.4. Authentication scheme registry

HttpClient maintains a registry of available authentication scheme using AuthSchemeRegistry class. The following schemes are registered per default:

  • Basic:  Basic authentication scheme

  • Digest:  Digest authentication scheme

Please note NTLM scheme is NOT registered per default. The NTLM cannot be enabled per default due to licensing and legal reasons. For details on how to enable NTLM support please see this section.

4.5. Credentials provider

Credentials providers are intended to maintain a set of user credentials and to be able to produce user credentials for a particular authentication scope. Authentication scope consists of a host name, a port number, a realm name and an authentication scheme name. When registering credentials with the credentials provider one can provide a wild card (any host, any port, any realm, any scheme) instead of a concrete attribute value. The credentials provider is then expected to be able to find the closest match for a particular scope if the direct match cannot be found.

HttpClient can work with any physical representation of a credentials provider that implements the CredentialsProvider interface. The default CredentialsProvider implementation called BasicCredentialsProvider is a simple implementation backed by a java.util.HashMap.

CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
    new AuthScope("somehost", AuthScope.ANY_PORT), 
    new UsernamePasswordCredentials("u1", "p1"));
credsProvider.setCredentials(
    new AuthScope("somehost", 8080), 
    new UsernamePasswordCredentials("u2", "p2"));
credsProvider.setCredentials(
    new AuthScope("otherhost", 8080, AuthScope.ANY_REALM, "ntlm"), 
    new UsernamePasswordCredentials("u3", "p3"));

System.out.println(credsProvider.getCredentials(
    new AuthScope("somehost", 80, "realm", "basic")));
System.out.println(credsProvider.getCredentials(
    new AuthScope("somehost", 8080, "realm", "basic")));
System.out.println(credsProvider.getCredentials(
    new AuthScope("otherhost", 8080, "realm", "basic")));
System.out.println(credsProvider.getCredentials(
    new AuthScope("otherhost", 8080, null, "ntlm")));

stdout >

[principal: u1]
[principal: u2]
null
[principal: u3]

4.6. HTTP authentication and execution context

HttpClient relies on the AuthState class to keep track of detailed information about the state of the authentication process. HttpClient creates two instances of AuthState in the course of HTTP request execution: one for target host authentication and another one for proxy authentication. In case the target server or the proxy require user authentication the respective AuthScope instance will be populated with the AuthScope, AuthScheme and Crednetials used during the authentication process. The AuthState can be examined in order to find out what kind of authentication was requested, whether a matching AuthScheme implementation was found and whether the credentials provider managed to find user credentials for the given authentication scope.

In the course of HTTP request execution HttpClient adds the following authentication related objects to the execution context:

  • 'http.authscheme-registry':  AuthSchemeRegistry instance representing the actual authentication scheme registry. The value of this attribute set in the local context takes precedence over the default one.

  • 'http.auth.credentials-provider':  CookieSpec instance representing the actual credentials provider. The value of this attribute set in the local context takes precedence over the default one.

  • 'http.auth.target-scope':  AuthState instance representing the actual target authentication state. The value of this attribute set in the local context takes precedence over the default one.

  • 'http.auth.proxy-scope':  AuthState instance representing the actual proxy authentication state. The value of this attribute set in the local context takes precedence over the default one.

The local HttpContext object can be used to customize the HTTP authentication context prior to request execution or examine its state after the request has been executed:

HttpClient httpclient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpGet httpget = new HttpGet("http://localhost:8080/"); 
HttpResponse response = httpclient.execute(httpget, localContext);

AuthState proxyAuthState = (AuthState) localContext.getAttribute(
    ClientContext.PROXY_AUTH_STATE);
System.out.println("Proxy auth scope: " + proxyAuthState.getAuthScope());
System.out.println("Proxy auth scheme: " + proxyAuthState.getAuthScheme());
System.out.println("Proxy auth credentials: " + proxyAuthState.getCredentials());
AuthState targetAuthState = (AuthState) localContext.getAttribute(
    ClientContext.TARGET_AUTH_STATE);
System.out.println("Target auth scope: " + targetAuthState.getAuthScope());
System.out.println("Target auth scheme: " + targetAuthState.getAuthScheme());
System.out.println("Target auth credentials: " + targetAuthState.getCredentials());

4.7. Preemptive authentication

HttpClient does not support preemptive authentication out of the box, because if misused or used incorrectly the preemptive authentication can lead to significant security issues, such as sending user credentials in clear text to an unauthorized third party. Therefore, users are expected to evaluate potential benefits of preemptive authentication versus security risks in the context of their specific application environment and are required to add support for preemptive authentication using standard HttpClient extension mechanisms such as protocol interceptors.

This is an example of a simple protocol interceptor that preemptively introduces an instance of BasicScheme to the execution context, if no authentication has been attempted yet. Please note that this interceptor must be added to the protocol processing chain before the standard authentication interceptors.

HttpRequestInterceptor preemptiveAuth = new HttpRequestInterceptor() {
    
    public void process(
            final HttpRequest request, 
            final HttpContext context) throws HttpException, IOException {
        
        AuthState authState = (AuthState) context.getAttribute(
                ClientContext.TARGET_AUTH_STATE);
        CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(
                ClientContext.CREDS_PROVIDER);
        HttpHost targetHost = (HttpHost) context.getAttribute(
                ExecutionContext.HTTP_TARGET_HOST);
        
        // If not auth scheme has been initialized yet
        if (authState.getAuthScheme() == null) {
            AuthScope authScope = new AuthScope(
                    targetHost.getHostName(), 
                    targetHost.getPort());
            // Obtain credentials matching the target host
            Credentials creds = credsProvider.getCredentials(authScope);
            // If found, generate BasicScheme preemptively
            if (creds != null) {
                authState.setAuthScheme(new BasicScheme());
                authState.setCredentials(creds);
            }
        }
    }
    
};

DefaultHttpClient httpclient = new DefaultHttpClient();
// Add as the very first interceptor in the protocol chain
httpclient.addRequestInterceptor(preemptiveAuth, 0);

4.8. NTLM Authentication

Currently HttpClient does not provide support for the NTLM authentication scheme out of the box and probably never will. The reasons for that are legal rather than technical. However, NTLM authentication can be enabled by using an external NTLM engine such as JCIFS library developed by the Samba project as a part of their Windows interoperability suite of programs. For details please refer to the NTLM_SUPPORT.txt document included with HttpClient distributions.

4.8.1. NTLM connection persistence

NTLM authentication scheme is significantly more expensive in terms of computational overhead and performance impact than the standard Basic and Digest schemes. This is likely to be one of the main reasons why Microsoft chose to make NTLM authentication scheme stateful. That is, once authenticated, the user identity is associated with that connection for its entire life span. The stateful nature of NTLM connections makes connection persistence more complex, as for the obvious reason persistent NTLM connections may not be re-used by users with a different user identity. The standard connection managers shipped with HttpClient are fully capable of managing stateful connections. However, it is critically important that logically related requests within the same session use the same execution context in order to make them aware of the current user identity. Otherwise, HttpClient will end up creating a new HTTP connection for each HTTP request against NTLM protected resources. For detailed discussion on stateful HTTP connections please refer to this section.

As NTLM connections are stateful it is generally recommended to trigger NTLM authentication using a relatively cheap method, such as GET or HEAD, and re-use the same connection to execute more expensive methods, especially those enclose a request entity, such as POST or PUT.

DefaultHttpClient httpclient = new DefaultHttpClient();

NTCredentials creds = new NTCredentials("user", "pwd", "myworkstation", "microsoft.com");
httpclient.getCredentialsProvider().setCredentials(AuthScope.ANY, creds);

HttpHost target = new HttpHost("www.microsoft.com", 80, "http");

// Make sure the same context is used to execute logically related requests
HttpContext localContext = new BasicHttpContext();

// Execute a cheap method first. This will trigger NTLM authentication
HttpGet httpget = new HttpGet("/ntlm-protected/info");
HttpResponse response1 = httpclient.execute(target, httpget, localContext);
HttpEntity entity1 = response1.getEntity();
if (entity1 != null) {
    entity1.consumeContent();
}

// Execute an expensive method next reusing the same context (and connection)
HttpPost httppost = new HttpPost("/ntlm-protected/form");
httppost.setEntity(new StringEntity("lots and lots of data"));
HttpResponse response2 = httpclient.execute(target, httppost, localContext);
HttpEntity entity2 = response2.getEntity();
if (entity2 != null) {
    entity2.consumeContent();
}