[TIMOB-25755] Android: WebView should prompt user for client certificate if requested by server
GitHub Issue | n/a |
---|---|
Type | Bug |
Priority | Critical |
Status | Closed |
Resolution | Done |
Resolution Date | 2018-07-10T00:31:08.000+0000 |
Affected Version/s | Release 6.3.0 |
Fix Version/s | Release 7.3.0 |
Components | Android |
Labels | android, titanium, webview |
Reporter | Martin Williamson |
Assignee | Gary Mathews |
Created | 2018-02-06T09:44:11.000+0000 |
Updated | 2020-11-13T03:19:32.000+0000 |
Description
_*Edit:*_
_Original issue turned out to be a
WebView
hardware acceleration bug on Google's end based on the web page's content, which was worked-around by adding a border to the WebView
. However, we're keeping the code change where the WebView
will prompt the end-user for a client certificate via a dialog if requested by the web server._
*Original Post:*
The following page will not render in an Android Webview, however, it loads correctly the iOS webview and in all Android stock browsers.
[https://idp.unilever.com/adfs/ls/?client-request-id=9b52f9a3-756e-4cde-99bd-64515d1e274a&wa=wsignin1.0&wtrealm=urn%%3afederation%%3aMicrosoftOnline&wctx=LoginOptions%%3D3%%26estsredirect%%3d2%%26estsrequest%%3drQIIAa2Qv4vUQACFM7c_PNcVjsNCLOQK5arZJJNkNhkQjKeLJ3d4blhFQWQymbmNJDNxJln1_go7UQtFO-0sBRvLK_TqAy2sxEIs1co9wd7C9sHjve_rWWR1WteVIbbdyLzgM67RgMpZXmxr1VQDpkp7S-maFnq5t_Ti5_iJbD5tPHqbXbl5cvXSS3DhH9t2Em9u2LExXNe5kmtKmqbkOuF6ljP-Dpy-FQxThNjQhwx7PvRxNIQRyjBEYcSiEAcRo3QPgC8A7C90EloW6NdC0GhJFDW5IZKW3JCakYMh4g7cP0meQaF0SWvSSFNxloucZ49b_U2Vzu_GVZUkl1-3TgmMBeUOgkHmYehHQQpTwR04DCkWIhIhx_xz60TFa64Hpqay4PfP_iU-oNxr97-2-w4gi4uHlsBxsGL9aIPnnbmy9-LZtw8fL55_Gh95RR8uW7sdm1Fv_Zx3Z3p7zfUEupbi0Xhyb2LCu42a4OtXp-t6J95yRqPxjjrjEvdB9-hu1_retd4c_j-293vHkOOGcM7rBCtuQPyAIHTjNw2&cbcxt=&username=peter.stanley%%40unilever.com&mkt=&lc=](https://idp.unilever.com/adfs/ls/?client-request-id=9b52f9a3-756e-4cde-99bd-64515d1e274a&wa=wsignin1.0&wtrealm=urn%%3afederation%%3aMicrosoftOnline&wctx=LoginOptions%%3D3%%26estsredirect%%3d2%%26estsrequest%%3drQIIAa2Qv4vUQACFM7c_PNcVjsNCLOQK5arZJJNkNhkQjKeLJ3d4blhFQWQymbmNJDNxJln1_go7UQtFO-0sBRvLK_TqAy2sxEIs1co9wd7C9sHjve_rWWR1WteVIbbdyLzgM67RgMpZXmxr1VQDpkp7S-maFnq5t_Ti5_iJbD5tPHqbXbl5cvXSS3DhH9t2Em9u2LExXNe5kmtKmqbkOuF6ljP-Dpy-FQxThNjQhwx7PvRxNIQRyjBEYcSiEAcRo3QPgC8A7C90EloW6NdC0GhJFDW5IZKW3JCakYMh4g7cP0meQaF0SWvSSFNxloucZ49b_U2Vzu_GVZUkl1-3TgmMBeUOgkHmYehHQQpTwR04DCkWIhIhx_xz60TFa64Hpqay4PfP_iU-oNxr97-2-w4gi4uHlsBxsGL9aIPnnbmy9-LZtw8fL55_Gh95RR8uW7sdm1Fv_Zx3Z3p7zfUEupbi0Xhyb2LCu42a4OtXp-t6J95yRqPxjjrjEvdB9-hu1_retd4c_j-293vHkOOGcM7rBCtuQPyAIHTjNw2&cbcxt=&username=peter.stanley%%40unilever.com&mkt=&lc=)
Attachments
File | Date | Size |
---|---|---|
Android build log.txt | 2018-02-08T13:22:14.000+0000 | 269284 |
Android build log 6.3.txt | 2018-02-08T13:22:14.000+0000 | 301618 |
AndroidWVTest.zip | 2018-02-06T12:10:24.000+0000 | 5552829 |
Screenshot.png | 2018-03-29T22:25:51.000+0000 | 1200250 |
ti.webdialog-android-1.0.0-6XX.zip | 2018-02-08T05:14:31.000+0000 | 114749 |
Hello [~marchief], Thanks for sharing with us. Please provide a full sample testcode that regenerates the issue. Better to provide a sample app as an attachment here. We will test the issue in our environment. Also, provide the SDK and CLI version you are testing on. Thanks.
Axway Appcelerator Studio, build: 5.0.0.201712081732 Cli 7.0.1 SDK 6.3.0.G.A Sample Project attached, code below:
Thanks [~marchief]! [~ybanev] Would you mind taking a peak?
[~ybanev], I have not handled certificate challenges with an Android WebView before. I suppose the only work-around that'll work "today" is to use Ti.Platform.openURL() to display the webpage via the Android device's default web browser. http://docs.appcelerator.com/platform/latest/#!/api/Titanium.Platform-method-openURL The above works well on Android because pressing the Back button from the browser app will return the end-user back to the app.
Maybe https://github.com/appcelerator-modules/titanium-web-dialog is a good solution as well?
We need the solution in the app as it is part of an SSO solution so we need to grab the SAML for authentication that is generated after authentication has been performed and placed in a named div for the app to retrieve.
{quote}We need the solution in the app{quote} Sounds like a feature request to me. :) [~ybanev], can you see what our options are in making this work on Android 4.x please? There's no point in supporting that WebViewClient.onReceivedClientCertRequest() API since it's not supported on older Android OS versions. And let's avoid the "ignore SSL error" technique that devs use on stackoverflow since that's considered a security risk. Alternatively, we could look into switching our WebView implementation over to Google's "Chrome Custom Tab" implementation, but that's a much bigger change that I don't see us doing in the near future. https://developer.chrome.com/multidevice/android/customtabs
Accessing data through evaljs is a supported feature ;) and works perfectly well in iOS on the URL given. Ignoressl doesn't make any difference on the URL given in android.
Probably should reference this account for fixes as have enterprise support.
[~marchief], you may want to give the following module a try... https://github.com/appcelerator-modules/titanium-web-dialog This module uses a "Chrome Custom Tab" within your app to display web content instead of the Android OS' built-in Java "WebView". I haven't tried it for myself, but since you said that you're able to display this webpage via the device's Chrome browser, it should theoretically work via this module as well. And Google's "Chrome Custom Tabs" is supported on Android 4.x. _Edit: Thanks goes to [~hknoechel] for pointing this out above._
THe module says it's for 7.1 and we are only able to use 6.3 max due to modules currently.
[~jquick] After a bit more testing: - the problem does not exist on APIs 16-18. The page with Authentication warning is loaded which I assume is fine provided the expected certificate is not installed on the system. - on API 19 this specific request does not trigger any significant WebViewClient events before
onPageFinshed
that would help us work around the problem. Trying to get some clue from the native error if we can somehow avoid big changes in order to get it done.I tried the custom tab option, whilst this does load the page, the "load" event is not fired, therefore we can't read data returned via evalJS. Also the tab option does not load the URL directly, you have to be redirected through as it has issues with URL parameters.
[~gmatthews] We have tried that, it loads the URL but no events work and can't access the content for evalJS
[~marchief], we'll look into experimenting more with a week from now (after our 7.1.0.GA release). Unfortunately, nothing on the native Android side just automatically works. The web dialog module is the only work-around at the moment since it works via the installed Chrome browser app... and whatever works in that app will work in the web dialog. They share the same web cache and certificates and is often used for SSO. But unfortunately, it comes with its own limitations. We can improve it in the future, but it may not be possible to give it the same feature set that a WebView has (such as evalJS). We'll look into what our options are for WebView later. The trick is getting it to use the same certificate that the Chrome app is using to satisfy the server's certificate challenge. This may or may not involve adding a new WebView API for you to use to respond to the certificate challenge. And we may not be able to support this in Android 4.x due to limitations on Google's end. We'll see when we experiment with this later.
master: https://github.com/appcelerator/titanium_mobile/pull/9882
Here's an SDK build incorporating the above PR: [mobilesdk-7.2.0.v20180226131340-osx.zip](https://www.dropbox.com/s/lallawg0954fd9x/mobilesdk-7.2.0.v20180226131340-osx.zip?dl=1) You can use this to determine if the changes solve your issue. You may need to install the necessary certificate/s on your device first.
Using the above SDK (the webView runs so much smoother btw) when it reaches the idp page it asks for certificate storage access and then to choose a certificate, cancelling this gives the blank page as before. we are sourcing the certificate to see if this gets around the issue, but I am still not sure why the certificate is not needed when using the stock browser?
bq. but I am still not sure why the certificate is not needed when using the stock browser? [~marchief], the certificate must have been selected/installed by the stock browser app at some point. Is this a work phone you're using to test this? Are your sure IT at your workplace hasn't already installed a certificate to it? I'm thinking the answer must be yes. Note that when I copy-and-paste the URL you gave us to my desktop web browser, I too get a dialog asking me for a certificate, which I don't have.
We got the client to test this on their work phone, they are unable to locate the certificate on the device, even though the browsers on the device are ok. I have tried this from several machines and web browsers and none prompt for a certificate, the page just loads and the SSL (secure) lock shows correctly.
[~anvil_martin] Are you able to provide us with another URL we could use for testing? I'm not sure what https://idp.unilever.com/adfs/ls is meant to be showing, a login prompt? I receive
An error occurred
on all browsers I've used navigating to it.try this one [SSO link](https://idp.unilever.com/adfs/ls/?client-request-id=32c6a80d-2be5-4ceb-91bd-6414b19fc94b&wa=wsignin1.0&wtrealm=urn%3afederation%3aMicrosoftOnline&wctx=LoginOptions%3D3%26estsredirect%3d2%26estsrequest%3drQIIAa2QvWsUQRyGd3MfxOMCIgmIjSkUC5nbmf3OiOChEYk5LrIxYBqZ2_2NbtydWWZmz8T_QVBLG0EbsRC0CQQEsRBMlTqlIIg2lmLlRrC3sH3h5X2fp2fRc3eNqTR1nFrkBUxBuQMmpnlxR8m6GqSydNakMqxQJ3rH515-dL-c_776-F02_-bZp-uv7OV_bDvJcLTqDLUGZXIpLkuh6xJUAmqap_DBPns7TiGL_DREADEgf2nio9gPYoQZZoTEvs9wdGDbX237cKaTsLJwf80EtRJUMp1rKlgJmpqUHg1RMiB_kjxDXKqSGVoLXUGa8xyyp63-SE6au8OqSpLx29YZHoacAXZRkHlhsx1M0IQDRlHMQs6XeAwhfG6dqsCAGmjDRAE7l_4SH1EetPvf2n3cobOzjaWTrUXrZ9t-3mmUPZEvhq8vyNHD97unA71g7XecjZV7Y721mdRm-8bNesVb37m2HYzLrfrBRhQanl25Za6uGyzW7i9fjCl51J3b71o_utbesf9j-7A372LSmPUQwYskop5HSbj5Gw2&cbcxt=&username=peter.stanley%40unilever.com&mkt=&lc=)
I am having the same issue with this URL https://stripe.com/connect-account/legal.
[~turbomonkey] Thanks, that makes a nice test case
Are you seeing the same issue? I am building with Ti SDK 7.0.2 and Android SDK API 26.
Is there any update to this? Been over a week with no update
[~marchief], I'm not sure what else we can do here. The way [~gmathews] implemented it matches what Google recommends here... https://developer.android.com/reference/android/webkit/WebViewClient.html#onReceivedClientCertRequest(android.webkit.WebView,%20android.webkit.ClientCertRequest) Like I said before, the server is doing a certificate challenge and expects the WebView to respond with a client certificate. You can see this happening in your desktop browser when you click on the 2 offending URLs you gave us. My desktop browser displays a dialog asking for a client certificate. My Android phone's main Chrome browser app prompts me for a certificate too. Gary's change makes it do the same, because I'm pretty sure that's all we can do. So, let's take a step back here. Do you not want a dialog to prompt the end-user for a certificate? If that's the case, then the only solution is to change the web server side to not perform a certificate challenge.
We have had feedback from the clients IT team, seems that the Webview request is not SNI compliant, therefore they are rejecting the connection. {quote} I found that ADFS server is configured to reject any client connection where the client is not SNI Compliant. Can application team confirm if the application is sending server name in the client hello? {quote}
Also, just to test out if there is a difference in the HttpClient request i tried this
which does render the page (i.e. passes the client certificate checks) and presents the basic page.
[~marchief], something is still amiss here. Forget about the mobile and Titanium side. Why can't anyone on my end display the web page via their desktop web browsers? We can't display the web page in Chrome or Safari. If we can't do this, then there's nothing we can do on the mobile side. This is the root problem. What do we need to do to get a successful response from the server?
Use the link i put in the http request. When it asks you to install a certificate just click allow or cancel and the page should still load.
[~anvil_martin] What should the page look like after it loads? Because I see an
Authentication Error
Okay. I see. So, the client certificate request is the expected behavior then. If I hit "continue" in my desktop browser, then I see the attached login page here: !Screenshot.png|thumbnail! This is what we should be seeing on the mobile web view, right?
[~jquick] yes that is correct, when going to [This link](https://idp.unilever.com/adfs/ls/idpinitiatedsignon.aspx) you are asked for a certificate if your browser doesn't find one automatically or you are from an untrusted connection, but continuing doesn't stop you getting to the site, however from the webview it doesn't give you the option to accept / continue and only shows a blank page. Our client has done some digging and found that the webview on android is not presenting the server name (not SNI compliant) not sure if that makes a difference or not. However if you use the Ti.Network.createHTTPClient this returns the page without an issue, it just doesn't render properly.
Okay. So the issue here is Android's native WebView is not "generating" a client certificate. What [~gmathews] has implemented will display a dialog asking for a client certificate like how desktop browser's do it, but unfortunately that approach won't generate one. Hmm... _(FYI: How Gary is doing it follows Google's examples.)_
Quick update. The most common solution to this (other than displaying a dialog asking for a certificate) is to bundle a certificate with the app and use that as the client certificate. However, theoretically, we should be able to generate a client certificate and public key based on the digital signature applied to the APK. I'm going to experiment with this and see how it goes.
[~marchief], do you know if the web server accepts self-signed client certificates?
[~marchief], this appears to be a hardware acceleration bug on Google's end. It turns out the WebView is receiving the HTML content just fine (even though it's not sending a client certificate), but the content is not getting rendered when hardware acceleration (via OpenGL and the GPU) is enabled, which is the default setting on Android. Something in the HTML is triggering this naughty behavior. There is a work-around. If you add a border to the WebView, then this will trick the code into disabling hardware acceleration and the content of the page will then appear.
[~jquick] Thanks for all of your hard work, I can confirm that workaround fixes the issue. Also, it will work with a self-signed certificate. Thanks Martin
[~turbomonkey], the HTML content from your URL is triggering the same Google hardware acceleration bug [~marchief] ran into. I've confirmed that adding a border to the
WebView
will make it shows its content. Have a look at my code example above.[~jquick] the workaround works for me as well. Thanks for figuring it out and sharing!
Closing ticket, as suggested above.