Problem


I was rewriting an old Angular application to Blazor Web Assembly a couple of days ago. The App is an internal tool, which uses a backend API that has Windows Authentication. In Angular, we used to add withCredentials: true to HTTP interceptor to make it work. The most equivalent method in .NET is typically adding UseDefaultCredentials = true in HttpClient. However, Blazor WASM will gives you an exception for this as indicated in this StackOverflow thread: WASM: System.PlatformNotSupportedException: System.Net.Http.HttpClientHandler is not supported on the current platform. 

The majority of internet search result so far tell you it's not possible to call Windows Authentication API from Blazor. Really? Let's see how to make it work with a few lines of code. 

Solution


Approach A

The original StackOverflow thread has 2 answers currently. The answer by ca8msm works, but the cost is not to use HttpClient, that we need to do everything manually in order to add BrowserRequestCredentials.Include to the request header. This approach sacrifices what we love about the HttpClient, but gives you ultimate control over the http request details.

string APIURL = "https://localhost:44390/api/models";

// create request object and pass windows authentication credentials
var request = new HttpRequestMessage(HttpMethod.Get, APIURL);
request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);

// send the request and convert the results to a list
var httpResponse = await Http.SendAsync(request);
models = await httpResponse.Content.ReadFromJsonAsync<myModel[]>();

Approach B

I come up with a better approach, which can keep the HttpClient we already like.

First, add a custom DelegatingHandler, this is used for adding BrowserRequestCredentials.Include to each request.

using Microsoft.AspNetCore.Components.WebAssembly.Http;

public class CredentialsMessageHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
        return base.SendAsync(request, cancellationToken);
    }
}

Second, create your typed client as usual. I prefer typed client, you may also use HttpClientFactory, it doesn't matter. You can find more information about how to make http request in best practice in this document.

public class CardClient
{
    private readonly HttpClient http;

    public CardClient(HttpClient http)
    {
        // ...
        this.http = http;
    }

    // ...
}

Finally, register the client in DI, with the CredentialsMessageHandler we wrote in the first step.

builder.Services.AddHttpClient<CardClient>(client => client.BaseAddress = new Uri(ApiEndpoint.FullUrl)).AddHttpMessageHandler<CredentialsMessageHandler>();

Now, you can still use the HttpClient the way you always been using it with Windows Authentication enabled API!

BTW, Why I am not answering this in StackOverflow? Because I lost access to my account years ago :(