11

I am using an HttpsUrlConnection with Basic Authentication by using an Authenticator and setting a default Authenticator object like this:

Authenticator.setDefault(new Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication("user", "userpass"
            .toCharArray());
    }
});

When I access my web-service the connection calls my getPasswordAuthentication() method to get the credentials and sends this to the web-server. This works allright as long as the password is correct. :)

However, it just happened that someone changed the basic authentication password on the web-server and then my request did not return.

I debugged it and what happens is that my call to getInputStream() never returns. The HttpsUrlConnection does get a 401 response and reacts to this internally by getting the same credentials again. But since I only provided one user and password this will fail again (and again...).

So my question is: How can I prevent this and where is there a hook to react to a wrong password (resp. a 401 response) so I can show an appropriate error message and cancel the request?

Here is an extract of the stack trace of the methods that are called repeatingly on HttpsUrlConnection:

1: MyOwnHttpConnection$3.getPasswordAuthentication() line: 99   
2: Authenticator.requestPasswordAuthentication(InetAddress, int, String, String, String) line: 162  
3: HttpsURLConnectionImpl$HttpsEngine(HttpURLConnectionImpl).getAuthorizationCredentials(String) line: 1205 
4: HttpsURLConnectionImpl$HttpsEngine(HttpURLConnectionImpl).processAuthHeader(String, String) line: 1178   
5: HttpsURLConnectionImpl$HttpsEngine(HttpURLConnectionImpl).processResponseHeaders() line: 1118    
6: HttpsURLConnectionImpl$HttpsEngine(HttpURLConnectionImpl).retrieveResponse() line: 1044  
7: HttpsURLConnectionImpl$HttpsEngine(HttpURLConnectionImpl).getInputStream() line: 523 
8: HttpsURLConnectionImpl.getInputStream() line: 283    
Jens
  • 6,243
  • 1
  • 49
  • 79
  • 1
    possible duplicate of [Android, HttpURLConnection and handling bad credentials](http://stackoverflow.com/questions/7061211/android-httpurlconnection-and-handling-bad-credentials) – yanchenko Dec 16 '12 at 16:09

2 Answers2

5

I solved this problem by abstracting request/response logic away into a MyRequest class. This allows me to have a request-scoped variable that can tell my Authenticator whether it should make a request using a specified username and password, or whether it should stop retrying (by returning null). It looks somewhat like the following (consider this pseudocode)

public class MyRequest
{
    private boolean alreadyTriedAuthenticating = false;
    private URL url;

    ...

    public void send()
    {
        HttpUrlConnection connection = (HttpUrlConnection) url.openConnection();
        Authenticator.setDefault(new Authenticator() {
            protected PasswordAuthentication getPasswordAuthentication() {
                if (!alreadyTriedAuthenticating)
                {
                    alreadyTriedAuthenticating = true;
                    return new PasswordAuthentication(username, password.toCharArray());
                }
                else
                {
                    return null;
                }
            }
            InputStream in = new BufferedInputStream(connection.getInputStream());

            ...

    }
}
jsadler
  • 589
  • 4
  • 18
  • exactly! thanks for pointing this out! its as per the answer that closed http://code.google.com/p/android/issues/detail?id=7058 the Authenticator is supposed to be asking the user for a *new* set of credentials because the previously supplied ones didn't work. If the authenticator is just using pre-set creds then it needs to keep track of it the previous attempt failed. – Maks Apr 01 '14 at 04:17
3

I wish I knew the proper answer to this, because I ran into the exact same problem. I couldn't find a way to handle the authentication error, or even get notified about it.

I ended up having to use HttpClient instead.

HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(loginUrl);
String authString = (userName+":"+password);
get.addHeader("Authorization", "Basic " + 
    Base64.encodeToString(authString.getBytes(),Base64.NO_WRAP));
HttpResponse response = client.execute(get);

BufferedReader r = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
nynexman4464
  • 1,760
  • 1
  • 17
  • 19
  • Seems to be a good alternative though. I have not changed anything in my code. I only make sure no one changes the password without notifying me... :-) – Jens Aug 30 '12 at 10:19