DefaultHttpHandler is deprecated, HttpURLConnection does not support NTLM and NTLM seems to be the only well-supported protocol by ASP.NET MCV websites. So, what\'s left to do?<
Okay so the first thing I did was to import the JCIFS library. Its a jar you download from that link.
The next thing I needed to do was to import the JCIFSEngine class and NTLMSchemeFactory class into your project.
Then finally this method builds your HTTPClient:
//I know it is deprecated but there is no other way to implement NTLM thus far.
private static DefaultHttpClient setupHttpClient (String username, String password) {
DefaultHttpClient httpclient = new DefaultHttpClient();
// register ntlm auth scheme
httpclient.getAuthSchemes().register("ntlm", new NTLMSchemeFactory());
httpclient.getCredentialsProvider().setCredentials(
// Limit the credentials only to the specified domain and port
new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT),
// Specify credentials, most of the time only user/pass is needed
new NTCredentials(username, password, "", "")
);
return httpclient;
}
My connections are done with Retrofit so I just attach this HTTPClient to retrofit using the following line:
retrofitAdapterBuilder.setClient(new ApacheClient(setupHttpClient(username, password)));
This worked for me thus far even-though it is really bad that Android has no native support for this.
EDIT: Adding an Okhttp implementation. This is an old class and we are no longer using it as we decided to drop NTLM support, but it used to work. Please keep in mind that we connect to servers that can have basic/windows/azure authentication so we need to do multiple checks which you might not need to do.
First create a class which implements the Authenticator interface from okhttp. Then in the authenticate() method do the following:
@Override
public Request authenticate(Route route, okhttp3.Response response) throws IOException {
if (response == null || response.headers() == null || response.headers().toString().isEmpty()) {
return null;
}
final List authHeaders = response.headers().values("WWW-Authenticate");
if (authHeaders.contains("NTLM")) {
return response.request().newBuilder().header("Authorization", "NTLM " + mNtlmInitialChallenge).build();
} else if (checkIsBasicAuth(authHeaders)) {
String credentials = mUsername + ":" + mPassword;
final String basic = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
return response.request().newBuilder().header("Authorization", basic).build();
}
if (mChallengeSent) {
return null;
} else {
mChallengeSent = true;
String ntlmFinalChallenge = null;
try {
ntlmFinalChallenge = mNtlmEngine.generateType3Msg(mUsername, mPassword, mDomain, "android-device", authHeaders.get(0).substring(5));
} catch (Exception e) {
e.printStackTrace();
}
return response.request().newBuilder().header("Authorization", "NTLM " + ntlmFinalChallenge).build();
}
}
To generate the mNtlmInitialChallenge create the following method:
private String getInitialNtlmChallenge() {
String initialChallenge = null;
try {
initialChallenge = mNtlmEngine.generateType1Msg(null, null);
} catch (Exception e) {
e.printStackTrace();
}
return initialChallenge;
}
The mNtlmEngine class I copied over from the JCIFS library manually so I could remove the dependency.
Some notes:
The code is not the cleanest as this was a POC.
Yes we used the dreaded mVariable notation, we are not using it anymore.
You are going to have to play around with this implementation until it works for you.
To attach the authenticator to okhttp just do client.authenticator(new NtlmAuthenticator(username, password, ""));
You are probably going to need to implement some failure mechanism.