{ by david linsin }

August 24, 2009

Http Basic Authentication with Android

The Google App Engine infrastructure, I'm developing in my spare time, is meant to be used by an Android client. To give our users at least a vague feeling of security, we decided to use a Basic Authentication together with HTTPS. Apparently, Android 1.5 is shipping with Apache's HttpClient 4.0 Beta2, which has a pitfall, when it comes to Basic Authentication.

When you search for HttpClient and Basic Authentication, Google will most definitely send you to the official documentation of HttpClient 3.x, which shows you, how to do Basic Authentication in a preemptive way. That means, sending the client's credentials with every request, instead of waiting for a 401 Unauthorized response and only then sending the credentials. That's probably what you want to in the first place, because it saves your client a request.
HttpClient client = new HttpClient();
client.getParams().setAuthenticationPreemptive(true);
Credentials defaultcreds = new UsernamePasswordCredentials("username", "password");
client.getState().setCredentials(new AuthScope("myhost", 80, AuthScope.ANY_REALM), defaultcreds);

This sample code won't compile with HttpClient version 4. The method called setAuthenticationPreemptive is missing. The problem is, if you omit this very method call, the code still works, but the authentication is not preemptive. We missed this little detail and only noticed after a while, that every request was preceded by a 401 Unauthorized request/response cycle. That doubled the amount of requests we served.

The HttpClient 4 documentation shows how to do preemptive authentication with the new API. You need to implement a so called HttpRequestInterceptor:
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 (authState.getAuthScheme() == null) {
AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort());
Credentials creds = credsProvider.getCredentials(authScope);
if (creds != null) {
authState.setAuthScheme(new BasicScheme());
authState.setCredentials(creds);
}
}
}
};

It basically sets the Basic Authentication headers, before each requests and thus avoids the 401 response. In order for the interceptor to work, you need to add it to the request chain:
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.addRequestInterceptor(preemptiveAuth, 0);

You might also run into the problem of using the old HttpClient 3.x way of doing Basic Authentication. It does work, but it's not preemptive, which you might not notice right away. Save yourself some time and checkout the sample code provided by Apache.

Furthermore, I wasn't abel to find any official site, which states the version of HttpClient, used in Android 1.5. There are various sources, you might encounter, when searching Google, but I would like to see an official site, that states the version. A reason for Google not to reveal this information might be, that they adopted HttpClient and thus are not compatible anymore. However, to avoid mistakes and confusion, it would be nice to know, on which version the Android HttpClient is based on.

17 comments:

michael said...

I get basic authentication with something like this:

httpclient.getCredentialsProvider().setCredentials(
new AuthScope(null, -1),
new UsernamePasswordCredentials(user, password));

Harold said...

Hi david, i also stuck on this Problem. And i did not found a solution.

Can you tell me if you solved it?

I tried your apache code above, but the included http-Client in Android dont have the class
"HttpContext". I also tried the solution on this page

https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/httpclient/src/examples/org/apache/http/examples/client/ClientPreemptiveBasicAuthentication.java

but then the class AuthCache is missing.

David Linsin said...

Hi Harold,

as I mentioned in my post "Android 1.5 is shipping with Apache's HttpClient 4.0 Beta2", which has the class HttpContext. So you can use the code the 4.0 docs suggest.

I hope you'll get it working!

Harold said...

Hmmmm...very strange, now it is working. Thank you :)

Jason said...

Have you got a sample eclipse project demonstrating this?

I am pulling my hair out try to get it to work :)

Thanks,
-Jason

David Linsin said...

Sorry Jason, unfortunately I don't...

atasoyh said...

what is AuthCache on android??

stuaxo said...

It doesn't seem to compile on current android ... any chance of an update to make it work again?

David Linsin said...

@stuaox

I'll look into it and see if I can update it.

ejb developer said...

Dear Experts

michale,Harold,david

plz any one can share me the

complete authentication code in android i could not found any solution for this

and also implement your codes also but no success to till


thanks

aarifmkhan

ejb developer said...

Hi Experts,

I am trying to downlod the files from the private URL that needs to authenticate first, i am using the following code
Authenticator.setDefault(new Authenticator()
{
protected PasswordAuthentication getPasswordAuthentication(){
return new PasswordAuthentication("username","password".toCha rArray());
}});

but application hang on the function, same code works in Java but not in Android.

I tried also connection.("Authorization","Basic " +bytesofuserandpass );
function, but not get any success.

Please help me....

have also tried these above code before your codes .

thanks
aarifmkhan

Santhosh Horatti said...

Thanks very very much for this post. I wasted almost full day today without getting the solution. Again thanks for the post.

Moesio Medeiros said...

Very useful. Right what I was searching for.
We just can't forget to set credentials to client credentialProvider, kind of this:

Credentials credentials = new UsernamePasswordCredentials("user", "pass");
httpClient.getCredentialsProvider().setCredentials(new AuthScope(null, -1), credentials);

AuthScope can be more specific, but for me, I need to be that way.

Thank you, David.

Anonymous said...

This is an awesome post... saved my ass big time. Thanks!

andrew-st said...

Did you sent any headers?
And additional, how you pass login/password values? as post variables?
Thanks

Chris Rae said...

Man. I never leave comments on posts like this, but you have absolutely saved my day. If only I'd found this yesterday afternoon...

Anonymous said...

Hello everybody!
I try to send a sparql query from my android device to an EndPointURI remote server so i can get the results. EndPointURI demands requests for username and password.
My code
Query query = QueryFactory.create(queryString);
DefaultHttpClient httpclient = new DefaultHttpClient();
Credentials credentials = new UsernamePasswordCredentials("username", "password");
httpclient.getCredentialsProvider().setCredentials(new AuthScope(null, -1), credentials);

HttpRequestInterceptor preemptiveAuth = new HttpRequestInterceptor()
{
public void process( HttpRequest request, HttpContext context) {
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 (authState.getAuthScheme() == null) {
AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort());
Credentials creds = credsProvider.getCredentials(authScope);
if (creds != null) {
authState.setAuthScheme(new BasicScheme());
authState.setCredentials(creds);
}
}
}
};

But i still get HttpException 401 unauthorized.

Any ideas of what i am doing wrong?
Thank you in advance.


com_channels

  • mail(dlinsin@gmail.com)
  • jabber(dlinsin@gmail.com)
  • skype(dlinsin)

recent_postings

loading...