Task
Task

Reputation: 3686

Implement custom 401 handling for a WebBrowser control

As per this article, I've extended the System.Windows.Forms.WebBrowser class to implement custom error-handling. Mostly, it works.

The problem comes when the browser gets a "401 Unauthorized" response. That kind of response causes the WebBrowser control to display the standard Username / Password dialog. The NavigateError event isn't fired until that dialog is cancelled.

So what can I do to capture the 401 response and handle it in my own custom way?

I assumed there would be something I could do, such as that which I do to capture the NavigateError event, and handle those my own way but I haven't seen anything.

Edit: Solution Found!
The important steps are:
1. The WebBrowser control must first be navigated to a non-secure page ("about:blank" is the typical URL used) in order to avoid KB 320153
2. The host for the WebBrowser control must implement IOleClientSite, IServiceProvider, and IAuthenticate.
3. IServiceProvider.QueryService must handle the IAuthenticate service request with the IAuthenticate implementation, all other service requests can be handled with the INET_E_DEFAULT_ACTION response.
4. IAuthenticate.Authenticate is your custom authentication handler.

Upvotes: 1

Views: 2894

Answers (2)

Darlene
Darlene

Reputation: 808

I found that to be able to navigate the site without the Authorization Header getting lost or removed I had to do the following otherwise for each new page the user was prompted again. This solution also does not require the user:password@site syntax to be enabled.

    private bool _redirected = false;
    private const string BaseUrl = @"http://mySite";

    private void Navigate()
    {
        var helpUrl = BaseUrl;
        var authHeader = GetAuthHeader();

        _docWindow.Browser.Navigate(helpUrl, string.Empty, null, authHeader);           
        _docWindow.Browser.Navigating += Browser_Navigating;

    }

    private string GetAuthHeader()
    {
        byte[] authData = UnicodeEncoding.UTF8.GetBytes(_userName + ":" + _password);
        string authHeader = "Authorization: Basic " + Convert.ToBase64String(authData);
        return authHeader;
    }

    void Browser_Navigating(object sender, System.Windows.Navigation.NavigatingCancelEventArgs e)
    {            
        if (_redirected)
        {
            _redirected = false;
            return;
        }
        var newPage = BaseUrl + e.Uri.AbsolutePath;

        e.Cancel = true;
        _redirected = true;
        _docWindow.Browser.Navigate(newPage, string.Empty, null, GetAuthHeader());
    }

Upvotes: 0

Sheng Jiang 蒋晟
Sheng Jiang 蒋晟

Reputation: 15271

implement IAuthenticate and IAuthenticateEx on your webbrowser host. Basically, your IOleClientSite implementation needs to responde IServiceProvider.QueryService, and return an IAuthenticate(Ex) interface (not the managed one, the native one returned from Marshal.GetComInterfaceForObject) when the service is IID_IAuthenticate. For unrecognized service requests, QueryService should return INET_E_DEFAULT_ACTION.

I don't think the WPF webbrowser has extension points for its IOleClientSite implementation. You can try host a Winform webbrowser class which has an overriden CreateWebBrowserSiteBase virtual method that provides the IAuthenticate(Ex) implementation, or write a webbrowser wrapper from the ground up.

This may not work in a Citrix session.

Upvotes: 4

Related Questions