How to access Bearer authenticated service from Android

android, apache-httpclient-4.x, authentication, http-headers, oauth

Solution

This can be a tad complex but I've had success with it so I'll try to give you the benefit of my experience.

You have to provide a couple of items

- An implementation of org.apache.http.auth.Credentials

- An implementation of org.apache.http.auth.AuthSchemeFactory

Your Credentials implementation should be something similar to the following:

import java.security.Principal;

import org.apache.http.auth.BasicUserPrincipal;
import org.apache.http.auth.Credentials;

public class TokenCredentials implements Credentials {
    private Principal userPrincipal;

    public TokenCredentials(String token) {
        this.userPrincipal = new BasicUserPrincipal(token);
    }

    @Override
    public Principal getUserPrincipal() {
        return userPrincipal;
    }

    @Override
    public String getPassword() {
        return null;
    }

}

Then you need to implement the AuthSchemeFactory:

import org.apache.http.Header;
import org.apache.http.HttpRequest;
import org.apache.http.auth.AUTH;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthSchemeFactory;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.ContextAwareAuthScheme;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.message.BufferedHeader;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.CharArrayBuffer;

public class BearerAuthSchemeFactory implements AuthSchemeFactory {

    @Override
    public AuthScheme newInstance(HttpParams params) {
        return new BearerAuthScheme();
    }

    private static class BearerAuthScheme implements ContextAwareAuthScheme {
        private boolean complete = false;

        @Override
        public void processChallenge(Header header) throws MalformedChallengeException {
            this.complete = true;
        }

        @Override
        public Header authenticate(Credentials credentials, HttpRequest request) throws AuthenticationException {
            return authenticate(credentials, request, null);
        }

        @Override
        public Header authenticate(Credentials credentials, HttpRequest request, HttpContext httpContext)
                throws AuthenticationException {
            CharArrayBuffer buffer = new CharArrayBuffer(32);
            buffer.append(AUTH.WWW_AUTH_RESP);
            buffer.append(": Bearer ");
            buffer.append(credentials.getUserPrincipal().getName());
            return new BufferedHeader(buffer);
        }

        @Override
        public String getSchemeName() {
            return "Bearer";
        }

        @Override
        public String getParameter(String name) {
            return null;
        }

        @Override
        public String getRealm() {
            return null;
        }

        @Override
        public boolean isConnectionBased() {
            return false;
        }

        @Override
        public boolean isComplete() {
            return this.complete;
        }
    }
}

The next step is getting HttpClient to accept it as a valid scheme:

    HttpContext httpContext = new BasicHttpContext();

    AuthSchemeRegistry authSchemeRegistry = new AuthSchemeRegistry();
    authSchemeRegistry.register("Bearer", new BearerAuthSchemeFactory());
    httpContext.setAttribute(ClientContext.AUTHSCHEME_REGISTRY, authSchemeRegistry);
    AuthScope sessionScope = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, "Bearer");

    Credentials credentials = new TokenCredentials (token);
    CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
    credentialsProvider.setCredentials(sessionScope, credentials);
    httpContext.setAttribute(ClientContext.CREDS_PROVIDER, credentialsProvider);

I typically would create these on a per context basis and hang on to the context over a period of time.

Additional documentation can be found here. I would recommend staying away from "pre-emptive" authentication and let the famework do its job in handling a 401 challenge.

If you'd like to see what I'm talking about, turn up the logging in HTTP client so that you can trace the wire conversation - you'll see the initial request come back with a challenge, then the client will utilize the credentials provider to locate the appropriate credentials and send the request with the appropriate challenge response for the scheme we've defined.

Good luck!

Problem

I am going to use a service that is using Bearer authentication. I tried to fetch it from Android in vain. Here's my code. ``` String mytoken = "some token I am sure is right"; HttpClient witClient = new DefaultHttpClient(); Uri.Builder b = Uri.parse("www.somewebsite.com").buildUpon(); b.appendQueryParameter("q", "some query string"); String finalurl = b.build().toString(); HttpGet request = new HttpGet(new URI(finalurl)); request.setHeader("Authorization", "Bearer "+mytoken); HttpResponse response = witClient.execute(request); ``` The server would return me an error saying authentication is needed. Obviously the header is dropped somehow. ``` 11-22 21:50:42.180: W/DefaultRequestDirector(3408): Authentication error: Unable to respond to any of these challenges: {bearer=Www-Authenticate: Bearer realm="OAuth required"} ``` where's wrong

Original source