user2844810
user2844810

Reputation: 995

Can't acess REST API with digest auth

I'm having a little trouble to fetch information from our REST API.

I'm using this android library http://loopj.com/android-async-http/ in its last version 1.4.6, I'm actually following the same steps from the tutorial, but the console always returns me that basic authentication challenge is expected. The API SERVER asks only for username and password but the security method is digest. Whenever I make a call to the server via the getPublicTimeline in the debug console it shows "Authentication error: basic authorization challenge expected, but not found", but I cannot find in the documentation how to do this.

Any idea anyone

Here's my Communication class

public class CommManager {
  private static final String BASE_URL = "https://example.com/api/";
  public static int timeOut = 15 * 1000;

  private static AsyncHttpClient client = new AsyncHttpClient();

  public static void get(String url, RequestParams params, AsyncHttpResponseHandler handler){
      Uri parsed = Uri.parse(getAbsoluteUrl(url));
      client.clearCredentialsProvider();
      client.setTimeout(timeOut);
      client.setCredentials(
            new AuthScope(parsed.getHost(), parsed.getPort() == -1 ? 80 : parsed.getPort()),
            new UsernamePasswordCredentials(
                    "user",
                    "mypass"
            )
    );
      client.get(getAbsoluteUrl(url), params, handler);
  }

  public static void post(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
      AsyncHttpClient client = new AsyncHttpClient();
      client.post(getAbsoluteUrl(url), params, responseHandler);
  }

  private static String getAbsoluteUrl(String relativeUrl) {
      return BASE_URL + relativeUrl;
  }
}

This is the method in my activity

public void getPublicTimeline(String query) throws JSONException {
    CommManager.get("module/method/" + query, null, new JsonHttpResponseHandler() {
        @Override
        public void onSuccess(int statusCode, Header[] headers, JSONArray timeline) {
            // Pull out the first event on the public timeline
            try {
                JSONObject firstEvent = (JSONObject) timeline.get(0);
                String tweetText = firstEvent.getString("text");

                // Do something with the response
                System.out.println(tweetText);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

//EDIT I've debugged as suggested and I think the problem is about how the library sends the credentials, when I do it via curl it sends a header called "Autorization" whereas when I'm calling from the library it sends a "WWW-Authenticate", still haven't found a solution. It still shows on the console Authentication error: basic authorization challenge expected, but not found

Upvotes: 0

Views: 1985

Answers (1)

user2844810
user2844810

Reputation: 995

It seems the library I was using doesn't fully support authentication that are not basic really well, after hours and hours of searching I found a solution I hope it helps someone else. Basically I used the HttpClient directly from apache.

First I added to my gradle file the components for the last version of the apache http client (I'm not really sure if this is necessary but I'll put it here anyways.

compile group: 'org.apache.httpcomponents' , name: 'httpclient-android' , version: '4.3.5'

This is my java class it has some returns since its in an AsyncTask, I'm just putting the doInBackground method which is explicit enough

@Override
protected String doInBackground(String... params) {
    HttpHost targetHost = new HttpHost(BASE_URL,443,"https");
    CredentialsProvider credsProvider = new BasicCredentialsProvider();
    credsProvider.setCredentials(AuthScope.ANY,new UsernamePasswordCredentials(DEFAULT_USER,DEFAUL_PASSWORD));

    AuthCache authCache = new BasicAuthCache();
    authCache.put(targetHost,new BasicScheme());

    final HttpClientContext context = HttpClientContext.create();
    context.setCredentialsProvider(credsProvider);
    context.setAuthCache(authCache);

    SSLContext sslContext = SSLContexts.createSystemDefault();
    SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);

    HttpClient client = HttpClientBuilder.create().setSSLSocketFactory(sslsf).build();
    try {
        HttpResponse response = client.execute(new HttpGet(BASE_URL), context);
        String json_string = EntityUtils.toString(response.getEntity());
        Log.i("JSON", json_string);
        int statusCode = response.getStatusLine().getStatusCode();
        Log.i("RESP",response.getEntity().getContent().toString());

        Log.i("STATUS", "" + statusCode);
        return response.toString();

    }catch(Exception e){
        e.printStackTrace();
    }
    return "";
}

The SSLContext is required because it causes a NULLPointerException if you don't add it. BASE_URL is the URL protected by Digest Auth.

I hope it helps someone

Upvotes: 2

Related Questions