Need help creating Digest authentication for Android

GeekedOut picture GeekedOut · Feb 28, 2012 · Viewed 8.4k times · Source

I have this code so far:

private class DownloadWebPageTask extends AsyncTask<String, Void, String> 
{
        @Override
        protected String doInBackground(String... theParams) 
        {
            String myUrl = theParams[0];
            String myEmail = theParams[1];
            String myPassword = theParams[2];

            HttpPost post = new HttpPost(myUrl);
            post.addHeader("Authorization","Basic "+ Base64.encodeToString((myEmail+":"+myPassword).getBytes(), 0 ));
            ResponseHandler<String> responseHandler = new BasicResponseHandler();

            String response = null;

            try 
            {
                    response = client.execute(post, responseHandler);
                InputStream content = execute.getEntity().getContent();

                BufferedReader buffer = new BufferedReader(
                            new InputStreamReader(content));
                    String s = "";
                    while ((s = buffer.readLine()) != null) 
                    {
                        response += s;
                    }
                } 
                catch (Exception e) 
                {
                    e.printStackTrace();
                }

            return response;
        }


        @Override
        protected void onPostExecute(String result) 
        {
            }

}

This code does not compile because I am running into confusion at the point of:

                response = client.execute(post, responseHandler);
                InputStream content = execute.getEntity().getContent();

I got that code from tinkering with various examples, and not sure what Object the client is supposed to be, and whether the first line will just get me the server response, or I have to go the route of getting the InputStream and reading the server response in?

Please help me understand how to do this correctly.

Thank you!

Answer

F&#233;lix Queiruga picture Félix Queiruga · Jul 10, 2014

I have managed to use Digest authentication using OkHttp. In this code sample I also use Dagger and Robospice-retrofit. What I did was creating an OkHttp Authenticator and assign it to my custom OkHttp client.

The authenticator class implements an authenticate method that will be called whenever the server encounters a 401 error and expects an Authorization header back (if it expects Proxy-Authorization you should implement the authenticateProxy method.

What it basically does is wrapping calls to the HttpClient DigestScheme and make it usable for OkHttp. Currently it does not increase the nc counter. This could cause problems with your server as it could be interpreted as a replay attack.

public class DigestAuthenticator implements com.squareup.okhttp.Authenticator {
    @Inject DigestScheme mDigestScheme;
    @Inject org.apache.http.auth.Credentials mCredentials;

    @Override
    public Request authenticate(Proxy proxy, Response response) throws IOException {
        String authHeader = buildAuthorizationHeader(response);
        if (authHeader == null) {
            return null;
        }
        return response.request().newBuilder().addHeader("Authorization", authHeader).build();
    }

    @Override
    public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
        return null;
    }

    private String buildAuthorizationHeader(Response response) throws IOException {
        processChallenge("WWW-Authenticate", response.header("WWW-Authenticate"));
        return generateDigestHeader(response);
    }

    private void processChallenge(String headerName, String headerValue) {
        try {
            mDigestScheme.processChallenge(new BasicHeader(headerName, headerValue));
        } catch (MalformedChallengeException e) {
            Timber.e(e, "Error processing header " + headerName + " for DIGEST authentication.");
        }
    }

    private String generateDigestHeader(Response response) throws IOException {
        org.apache.http.HttpRequest request = new BasicHttpRequest(
                response.request().method(),
                response.request().uri().toString()
        );

        try {
            return mDigestScheme.authenticate(mCredentials, request).getValue();
        } catch (AuthenticationException e) {
            Timber.e(e, "Error generating DIGEST auth header.");
            return null;
        }
    }
}

The authenticator will then be used in an OkHttpClient built with a provider:

public class CustomClientProvider implements Client.Provider {
    @Inject DigestAuthenticator mDigestAuthenticator;

    @Override
    public Client get() {
        OkHttpClient client = new OkHttpClient();
        client.setAuthenticator(mDigestAuthenticator);
        return new OkClient(client);
    }
}

Finally the client is set to the RetrofitRobospice server in the function createRestAdapterBuilder:

public class ApiRetrofitSpiceService extends RetrofitJackson2SpiceService {
    @Inject Client.Provider mClientProvider;

    @Override
    public void onCreate() {
        App.get(this).inject(this);
        super.onCreate();
        addRetrofitInterface(NotificationRestInterface.class);
    }

    @Override
    protected String getServerUrl() {
        return Constants.Url.BASE;
    }

    @Override
    protected RestAdapter.Builder createRestAdapterBuilder() {
        return super.createRestAdapterBuilder()
                .setClient(mClientProvider.get());
    }
}