WebView - Basic HTTP Authentication - How is everyone else doing this?

ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭
edited November 2018 in Xamarin.Forms

Every now and again you find a topic where you say "I know I'm not the first to ask this" but can't find any current conversation or resolution. This seems to be one of those. I'm not a web guy, so maybe this is something everyone knows through growing up in it but just seems to be uncovered for 2018 and Xamarin Forms >= 2.5

All I want to do is have my app open a web page that wants basic HTTP authentication.

There are countless articles from 2014 on doing this.
https://forums.xamarin.com/discussion/42918/how-to-access-protected-resource-using-xamarin-webview-basic-authentication
The implementation here is really straightforward with a nice username and password property you can set in XAML even.

And a good Nuget for an authenticating webview
https://github.com/TheRealAdamKemp/AuthenticatedWebView/issues/3

However all of these answers I'm finding for Xamarin forms less than 2.5. Those same bits of solution do not work with the WebView of 2.5 and later. When I say 'Does not work' I mean two things mostly:
1) The code makes use of obsoleted constructor and method syntax. That seems to have been an easy hurdle but maybe I'm wrong.
2) The method needed for passing the credentials (OnReceivedHttpAuthRequest) is never hit in XF3.3. So if the WebView doesn't run this override you never get to send the username/password.

I can't believe I'm the first person to want to do this basic function. Or felt pain when XF1.3 was replaced by 2.5 and it stopped working.
I have to believe I just don't know some basic thing that everyone else knows like "Use the blah blah control" or whatever. But I'm in this a week now and can't seem to make the working solutions I've found from 1.3 work in modern 3.3

Here's my updated version of code from the 2015 thread - maybe someone with more web experiences spots something obvious.

using System;
using Android.Content;
using Android.Webkit;
using Xamarin.Forms;
using WebView = Xamarin.Forms.WebView;

[assembly: ExportRenderer(typeof(AuthWebView), typeof(AuthWebViewRenderer))]
namespace SysUtilityPFX.Droid.Common
{
    public class AuthWebViewRenderer : Xamarin.Forms.Platform.Android.WebViewRenderer
    {
        AuthWebViewClient _authWebClient = null;

        public AuthWebViewRenderer(Context context) : base(context)
        {
            var breakpointable = 5;
        }

        /// <summary>Syntax obsolete since XamForms 2.5
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<WebView> e)
        {
            base.OnElementChanged(e);
            if (_authWebClient == null)
            {
                _authWebClient = new AuthWebViewClient("admin", "admin");//Testing to make it work then make it better afterwards.
                //_authWebClient = new AuthWebViewClient(AuthWebView.Username, AuthWebView.Password);//Error Object reference for static
            }
            Control.SetWebViewClient(_authWebClient);
        }
    }
    public class AuthWebViewClient : WebViewClient
    {
        private string Username
        {
            get;
        }
        private string Password
        {
            get;
        }
        public AuthWebViewClient(string username, string password)
        {
            Username = username;
            Password = password;
        }

        //Never hits breakpoint
        public override void OnReceivedHttpAuthRequest(global::Android.Webkit.WebView view, HttpAuthHandler handler, string host, string realm)
        {
            handler.Proceed(
                Username,
                Password);
        }

        [Obsolete("deprecated")]
        public override bool ShouldOverrideUrlLoading(global::Android.Webkit.WebView view, string url)
        {
            view.LoadUrl(url);
            return true;
        }
    }
}


/* References
    https://forums.xamarin.com/discussion/42918/how-to-access-protected-resource-using-xamarin-webview-basic-authentication

    https://www.ozkary.com/2018/06/xamarin-android-webview-authentication.html

    https://xamarinexplorer.com/2018/01/20/adding-authorization-header-in-web-view/
    iOS only

    https://github.com/TheRealAdamKemp/AuthenticatedWebView/issues/3
    XF 1.3 version works great.  Not in 2018 with XF3.3
*/

Best Answers

  • ClintStLaurentClintStLaurent US ✭✭✭✭✭
    Accepted Answer

    Found it. Found out why the HttpAuthRequest handler was never being hit.
    The site was causing an SslError event first, over its built-in self-signed self-generated certificate.
    If I catch that and tell the handler to .Proceed() anyway the next event is the AuthRequest where I can provide username and password. Then I am successfully getting the page.

Answers

  • JoeMankeJoeManke USMember ✭✭✭✭✭
    edited November 2018

    I wish I'd known about OnReceivedHttpAuthRequest when I was messing with WebView renderers and things that required auth. I just cheated by overriding ShouldInterceptRequest and passing the URL down to my ViewModel, which did the query using HttpClient, where I knew how to add an auth header, and threw it back up to the renderer. There were a lot of task.Result calls, it was a bad time.

    So I don't know if that will help you at all, but I did want to let you know that I made a couple enhancements to the WebView renderer/client process in Xamarin.Forms 3.3.0 via pull request #3780. Override WebViewRenderer.GetWebViewClient() and subclass FormsWebViewClient for your AuthWebViewClient and then you won't have to worry later why your Navigating and Navigated events are never getting fired.

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    I appreciate the attention on this @JoeManke

    @JoeManke said:
    Override WebViewRenderer.GetWebViewClient()

    That's not the exact same method I see available to override.

    and subclass FormsWebViewClient for your AuthWebViewClient

    and I see a corresponding difference here

    and then you won't have to worry later why your Navigating and Navigated events are never getting fired.

    Those have been working. I've been using them to keep up an indicator of the page still loading. Its the OnReceivedHttpAuthRequest that isn't raising.

    but I did want to let you know that I made a couple enhancements to the WebView renderer/client process in Xamarin.Forms 3.3.0 via pull request #3780.

    I don't know what I need to do to make use of that.

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    Just an observation from the ticket you linked
    @JoeManke
    Just an observation from the ticket you linked. I'm sure you know FAR more about this stuff than I do. Which is only adding to my confusion about what I'm reading since much of this stuff seems to be obsolete or different from the work you're doing today.

    In the Android WebViewRenderer, the Navigating event is fired from the ShouldOverrideUrlLoading(AWebView view, string url) method in the WebClient, which seems to get hit

    That method signature is marked as obsolete(deprecated in favor of an IWebResourceRequest replaceing the string
    public override bool ShouldOverrideUrlLoading(global::Android.Webkit.WebView view, IWebResourceRequest request)

    So I have this updated versions

            [Obsolete("deprecated")]
            public override bool ShouldOverrideUrlLoading(global::Android.Webkit.WebView view, string url)
            {
                view.LoadUrl(url);
                return true;
            }
    
            public override bool ShouldOverrideUrlLoading(global::Android.Webkit.WebView view, IWebResourceRequest request)
            {
                view.LoadUrl(request.Url.ToString());
                return true;
            }
    
  • JoeMankeJoeManke USMember ✭✭✭✭✭

    Are you sure you have 3.3.0 installed? The new method and type show up for me with 3.3.0-pre2 or higher.

    About ShouldOverrideUrlLoading, check out the official Android documentation.

    • Note: Do not call WebView.loadUrl(String) with the request's URL and then return true. This unnecessarily cancels the current load and starts a new load with the same URL. The correct way to continue loading a given URL is to simply return false, without calling WebView.loadUrl(String).
    • Note: This method is not called for POST requests.
    • Note: This method may be called for subframes and with non-HTTP(S) schemes; calling WebView.loadUrl(String) with such a URL will fail.

    But if I recall correctly, you do need both overrides if you're targeting Android versions lower than API 24 (Android 7.0).

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    @JoeManke said:
    Are you sure you have 3.3.0 installed? The new method and type show up for me with 3.3.0-pre2 or higher.

    Son of a .... I never even thought to check since it is policy here that all the apps are kept on the same level by a designated person who does the updates to all when approved. And sure enough this app isn't updated.

    {sigh} Will update the solution then update the thread here.

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    Confirmation the new code and renderer are working using an unsecured page.

    However when going to the page requiring authentication I see the handlers for Navigating and Navigated do their job (background changes color while loading then back again when done).
    But no content is displayed and the OnReceivedHttpAuthRequest still doesn't hit its breakpoint.

    At this point I'm stripped down to simple code. Am I missing something? I should still be trying to handle the HTTPAuthRequest from the page to send the username and password, right?

        public class AuthWebViewRenderer : Xamarin.Forms.Platform.Android.WebViewRenderer
        {
            public AuthWebViewRenderer(Context context) : base(context)
            {
                Console.WriteLine("INFORMATION > CONSTRUCTED");
            }
            protected override WebViewClient GetWebViewClient()
            {
                return new SSLWebViewClient(this);
            }
        }
    
    
    
        public class SSLWebViewClient : FormsWebViewClient
        {
            public SSLWebViewClient(WebViewRenderer renderer) : base(renderer)
            {
            }
    
            public override void OnReceivedHttpAuthRequest(global::Android.Webkit.WebView view,
                HttpAuthHandler handler, string host, string realm)
            {
                handler.Proceed(
                    "admin",
                    "admin");//fast test and if works then implement properties
    
                //handler.Proceed(
                //    Username,
                //    Password);
            }
        }
    
  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭
    Accepted Answer

    Found it. Found out why the HttpAuthRequest handler was never being hit.
    The site was causing an SslError event first, over its built-in self-signed self-generated certificate.
    If I catch that and tell the handler to .Proceed() anyway the next event is the AuthRequest where I can provide username and password. Then I am successfully getting the page.

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    Thanks for that @AlexanderLobanov
    The derived renderer that I created using the @adamkemp nuget mentioned earlier has been treating me well for the last couple months... Once I started handling the web error that was being returned (duh, smack myself on the head).

    Since this working in the production app its not going to get pulled out without cause.

    But I'll be sure to take a look at the nuget you mentioned, in a personal research project. Its always good to have more tools in the toolbox.

  • DmitriyBardayDmitriyBarday USMember

    @AlexanderLobanov it seems like SKLn-Rad doesn't accept PR for Xam.Plugin.WebView anymore. :(

Sign In or Register to comment.