Featured image of post Authenticating HTTP requests with cookies from an embedded WebView2 browser in WPF

Authenticating HTTP requests with cookies from an embedded WebView2 browser in WPF

Automate HTTP calls to web pages or APIs by bypassing Cloudflare Turnstile or any captcha using authentication cookies from a web browser embedded in a WPF app.

Authenticating HTTP requests using cookies from an embedded browser in a desktop application can be useful for automating tasks on websites protected by Cloudflare Turnstile. This article demonstrates how to extract cookies from a WebView2 control and add them to the headers of an HttpRequestMessage in a WPF application.

To achieve this, we need to create a DelegatingHandler that holds a reference to our WebView2 control:

internal sealed class WebViewSetCookieHandler(WebView2 webView, HttpMessageHandler innerHandler) : DelegatingHandler(innerHandler)
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (request.RequestUri != null)
        {
            var cookieHeader = await this.GetCookieHeaderAsync(request.RequestUri).ConfigureAwait(false);

            if (!string.IsNullOrEmpty(cookieHeader))
            {
                request.Headers.Add("Cookie", cookieHeader);
            }
        }

        return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
    }

    private async Task<string> GetCookieHeaderAsync(Uri uri)
    {
        CookieContainer cookieContainer;

        if (webView.CheckAccess())
        {
            cookieContainer = await this.GetCookieContainerAsync(uri).ConfigureAwait(false);
        }
        else
        {
            var cookieContainerTask = webView.Dispatcher.InvokeAsync(() => this.GetCookieContainerAsync(uri)).Task.Unwrap();
            cookieContainer = await cookieContainerTask.ConfigureAwait(false);
        }

        return cookieContainer.GetCookieHeader(uri);
    }

    private async Task<CookieContainer> GetCookieContainerAsync(Uri uri)
    {
        var webViewCookies = await webView.CoreWebView2.CookieManager.GetCookiesAsync(uri.ToString()).ConfigureAwait(false);
        var cookieContainer = new CookieContainer();

        foreach (var cookie in webViewCookies)
        {
            cookieContainer.Add(cookie.ToSystemNetCookie());
        }

        return cookieContainer;
    }
}

When an HTTP request is sent, we ask the WebView2 control’s cookie manager to return the cookies for the request’s URL. These cookies are then converted into System.Net.Cookie objects and stored in a CookieContainer, which takes care of serializing them into a string for the request headers.

It is important to consider that the code may execute on a thread different from the UI thread. This is why we need to check whether the dispatcher is available for the current thread and use InvokeAsync to run the GetCookieContainerAsync method on the UI thread when necessary.

This handler can be used within a custom HTTP client once the WebView2 control has been initialized:

internal sealed partial class MainWindow
{
    private readonly HttpClient _httpClient;

    public MainWindow()
    {
        this.InitializeComponent();

        var setCookieHandler = new WebViewSetCookieHandler(this.MyWebView, HttpClientDefaults.PrimaryHandler);
        this._httpClient = new HttpClient(setCookieHandler);
    }
}

internal static class HttpClientDefaults
{
    public static readonly SocketsHttpHandler PrimaryHandler = new SocketsHttpHandler
    {
        PooledConnectionLifetime = TimeSpan.FromMinutes(2)
    };
}

The CoreWebView2 property of WebView2 is undefined until the EnsureCoreWebView2Async method has been called on the control. A good place to do this is by overriding the OnContentRendered method of the parent control.


Photo by Luis Quintero

Licensed under CC BY 4.0
Ko-fi donations Buy me a coffee