16

I have researched and researched and researched this until I've gone grey and bald. How on earth do I get a webview to work for a site that needs http basic authentication over an https connection on api level 8+

I have the following code

    String email = Util.getEmail(this);
    String pwd = Util.getPassword(this);
    webview.getSettings().setJavaScriptEnabled(true);
    webview.setHttpAuthUsernamePassword(Config.SERVER_BASE_URL, "Application", email, pwd);

//      webview.setWebViewClient(new MobileWebViewClient(this));
    webview.loadUrl(url);

As you can see I did have a web view client (Now commented out) that overrides the onReceivedHttpAuthRequest method which looks like this

@Override
public void onReceivedHttpAuthRequest (WebView view, HttpAuthHandler handler, String host, String realm){
    String email = Util.getEmail(wvContext);
    String pwd = Util.getPassword(wvContext);
    if(!pwd.equalsIgnoreCase("-1") && !pwd.equalsIgnoreCase("-1")){
       handler.proceed(email, pwd);
    }
}

This was used without the webview.setHttpAuthUsernamePassword and works fine except that it means 2 requests are issued to the website - The first gets a 401 and then the client kicks in with the authorisation stuff This is fine for a small amount of website traffic but halving the amount of traffic (currently averaging 49 requests p/m) is the name of the game right now!

I read that I can pre-emptively supply the credentials by using

webview.setHttpAuthUsernamePassword(Config.SERVER_BASE_URL, "Application", email, pwd);

However this just results in Http Basic: Access denied errors The server base url constant is the domain name for the site i.e. https://example.com (without the page) the actual url is https://example.com/some_pages. It makes no difference whether I use the full url or the domain. I have checked the realm and I have that correct and I have used just empty strings providing only email and password. Could this be something to do with the fact that the site is using https? My code seems to work fine on my dev box without https but that may be a red herring.!

The only stack overflow questions that seem to cover my requirements have not got accepted answers and the docs are no help that I can see.

I now have such a large dent in my head from banging it against a brick wall that I am thinking of getting a square hat.

Please if anyone can give me the solution to this I will be forever in your debt! I might even e-Mail you an KitKat

jamesc
  • 12,423
  • 15
  • 74
  • 113
  • I take it [this question and its accepted answer](http://stackoverflow.com/questions/1968416/how-to-do-http-authentication-in-android) doesn't solve your problem? – Amos M. Carpenter Jan 24 '12 at 04:23
  • @aaamos, It may well solve my problem but how would I apply that to a WebView? – jamesc Jan 24 '12 at 04:43
  • Have you tryed download the webpage with a httppost and then supply it to a webview? – Warpzit Jan 24 '12 at 08:49
  • Can't you just do https://email:password@example.com/some_page and construct the url dynamically? (You may have to encode email though)...this could be potential security problem, but you can try it out. – Edison Jan 24 '12 at 08:51
  • Why do you test !pwd.equalsIgnoreCase("-1") twice? – Reuben Scratton Jan 24 '12 at 10:30
  • @ReubenScratton Good catch, one is supposed to be an eMail check, thankyou for pointing that out – jamesc Jan 25 '12 at 03:10

3 Answers3

18

This simple example abuses a page on HttpWatch since it's more fun with a working public example.

The resource in question, https://www.httpwatch.com/httpgallery/authentication/authenticatedimage/default.aspx?randomgarbage uses basic auth over HTTPS and can be loaded without authentication failure like this (tested using Android 2.3.7):

    WebView v = ...; // Your webview goes here. 
    try {
        HashMap<String, String> map = new HashMap<String, String>();
        // This test service takes the username "httpwatch" and a random
        // password. Repeating a password can lead to failure, so we create
        // a decently random one using UUID.
        String usernameRandomPassword = "httpwatch:" + UUID.randomUUID().toString();
        String authorization = "Basic " + Base64.encodeToString(usernameRandomPassword.getBytes("UTF-8"), Base64.NO_WRAP);
        map.put("Authorization", authorization);
        v.loadUrl("https://www.httpwatch.com/httpgallery/authentication/authenticatedimage/default.aspx?" + System.currentTimeMillis(), map);
    } catch (UnsupportedEncodingException e) {}

This works on ICS and Gingerbread. Don't have access to anything older than that, but loadUrl(String, Map<String,String>) was introduced in API level 8, so I don't see why it shouldn't work for that to.

Clarification for Nappy:

To support authentication for subsequent requests you supply a WebViewClient and do the following:

    webView.setWebViewClient(new WebViewClient(){
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url, <your map containing the Authorization header>);
            return true;
        }
    });
Jens
  • 16,853
  • 4
  • 55
  • 52
  • Thank you for your answer, It certainly works but I am worried that the credentials would appear in the browsers url which is absolutely not what I need for this particular solution. So I need to know if there is anyway that the credentials could 'leak' in a way that the user would be able to see them? – jamesc Jan 25 '12 at 11:00
  • 1
    The Map is a set of HTTP headers that will be included in the response - basically it tricks the WebView to include the header that would be sent if you went through with the initial failed request and *then* responded with valid credentials. - so it's not part of the URL at all. – Jens Jan 25 '12 at 11:11
  • That's great! Can the same be applied to a DefaultHTTPClient? – jamesc Jan 25 '12 at 12:31
  • Preemptive authentication with the DefaultHttpClient is also possible, for instance it was asked and answered [here](http://stackoverflow.com/questions/2960425/android-httpclient-getting-a-file-with-preemptive-authentication) in a similar fashion to this fix. – Jens Jan 25 '12 at 12:34
  • How does this work for subsequent requests? For example filling a form and submit, can you "inject" those authentication headers there too? Otherwise its only half of a full workaround... – Nappy Feb 13 '12 at 12:51
  • Yup, you can inject in subsequent requests also (i'll add a how-to in the answer). – Jens Feb 13 '12 at 13:40
  • Hi guys, any clue about my issue regarding authentication in a webview? : http://stackoverflow.com/questions/19256509/how-do-i-resolve-the-authentication-message-that-keeps-popping-up-in-a-webview/19256608?noredirect=1#comment28533391_19256608 –  Oct 09 '13 at 18:42
1

for handling authorization on your site, just override the following method in your webViewClient and add username and password in its handler.

@Override
        public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
            handler.proceed(StringUtils.AUTH_NAME,StringUtils.AUTH_PASS);
        }
1

It will work for https URL.In this if we are getting Untrusted_cer then we will ignore it

 webview.setWebViewClient(new WebViewClient(){
        @Override
        public void onReceivedHttpAuthRequest(WebView view,
                HttpAuthHandler handler, String host, String realm) {
            super.onReceivedHttpAuthRequest(view, handler, host, realm);
        }

        @Override
        public void onReceivedSslError(WebView view,
                SslErrorHandler handler, SslError error) {
            super.onReceivedSslError(view, handler, error);
            if(error.getPrimaryError()==SslError.SSL_UNTRUSTED){
                handler.proceed();
            }else{
                handler.proceed();
            }
        }

    });

I have no idea about second problem

Tofeeq Ahmad
  • 11,935
  • 4
  • 61
  • 87
  • I'm not sure yet, I'm testing things out and both your solution combined with @Jens solution appear to be exactly what I need but I'm struggling with one last thing. I'm just about to add a comment to Jens answer about this. How would I split the bounty between you two? – jamesc Jan 25 '12 at 10:56
  • @jamesw: i will say give it to me :) – Tofeeq Ahmad Jan 25 '12 at 11:02
  • Hey james ,,i found discussion on preemptive http basic authorization.so have a look this link,,http://code.google.com/p/android-xmlrpc/issues/detail?id=6 – Tofeeq Ahmad Jan 25 '12 at 11:13
  • Well, since you had issue with preemptive authentication and *not* SSL certs I'd say that this answer is quite unrelated ;) – Jens Jan 25 '12 at 11:13
  • @Jens: he has mention about https url.i think you have not read about it clearly.Normally its not possible to run https url in webview.We have to avoid sslerror – Tofeeq Ahmad Jan 25 '12 at 11:15
  • If the SSL certificate on the server is a proper **non-self-signed** it normally shouldn't fail unless someone is messing with the client to server communication. Accepting any cert in that case is an open and shut case for a Man-in-the-middle attack vector. – Jens Jan 25 '12 at 11:17
  • i have check in all https case, it will not work in any case without accepting Untrusted_certificate.There is no case of Man-in-the-middle attack vector as you are directly communicating to server and accepting certificate from server.So i am pretty sure and confident about it – Tofeeq Ahmad Jan 25 '12 at 11:21
  • I wouldn't recommend trying that on an unsecured/untrusted WiFi with anything important. In almost all cases users are not going to be on the same local net as the server - i.e. - anyone can sign a certificate for your web server and substitute their own server / read your communication. SSL is only secure if you *either* use a trusted certificate *or* actually verify the certificate locally when the client complains that the server gave you an untrusted cert. Just calling #proceed() is a security hole, simple matter of fact. – Jens Jan 25 '12 at 11:26
  • @jamesw: i will suggest you to have patience if still your question did not get right answer. then decide to award bounty – Tofeeq Ahmad Jan 25 '12 at 11:37