How to send HTTP requests to public API without access token on an authentication enabled Blazor Wasm App?
Tags: blazor
Published: 2020-06-19 14:30:21

---
title: How to send HTTP requests to public API without access token on an authentication enabled Blazor Wasm App?
published: true
description: I'll show you why "AccessTokenNotAvailableException" will occurred, and how to resolve it.  
tags: blazor
cover_image: https://raw.githubusercontent.com/sample-by-jsakamoto/Blazor-AllowNoAuthHttpRequest/master/.assets/fig1.png
---

# It's easy to build an authentication enabled Blazor Wasm App from the default project template.

One day, I started to build an authentication enabled Blazor WebAssembly App that is hosted on ASP.NET Core server.

I'm usually using Visual Studio on Windows OS for development Blazor App, so it is very easy to create an authentication enabled Blazor WebAssembly App from the project template.

![fig1](https://dev-to-uploads.s3.amazonaws.com/i/o1e2b9tln9uqpt75qvh6.png)

It works fine very well. 

The Web API endpoint which is annotated with `[Authorizaition]` attribute was protected from anonymous access, as I expected.

# Next, I added a public API endpoint.

Next step, I started to add a new feature to the app.

I tried to implement listing "Recently Updated" news feed about this web app.

I implemented the "Recently Updated" list to be generated on the server-side and provide it to the client-side via a Web API endpoint.

I thought this news feed should be public for every user even who is not signed-in.

Therefore, I didn't annotate the getting "Recently Updated" list API endpoint with `[Authorization]`, for allowing anonymous access.

```csharp
[ApiController]
[Route("[controller]")]
public class RecentlyUpdatesController : ControllerBase
{
  [HttpGet]
  public IEnumerable<string> Get()
  {
    ...
```

I tested that API by `cURL` command without any credentials, and it worked fine of course.

![fig2](https://dev-to-uploads.s3.amazonaws.com/i/ipy7mws44of01xod73wu.png)

# But... unhandled exception occurred!

The server-side implements looked like completed, so I started to implement the client-side.

I coded fetching the "Recently Updated" list from that public Web API using the "HttpClient" object that was injected via DI.

After did it, I tried to run the app.

Unfortunately and unexpectedly, I could not see the "Recently Updated" list on the screen, instead, I saw something error reports.

```
Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
  Unhandled exception rendering component: ''
  Microsoft.AspNetCore.Components.WebAssembly.Authentication.AccessTokenNotAvailableException: ''
  at Microsoft.AspNetCore.Components.WebAssembly.Authentication.AuthorizationMessageHandler.SendAsync(
    System.Net.Http.HttpRequestMessage request, 
    System.Threading.CancellationToken cancellationToken)
```

![fig3](https://raw.githubusercontent.com/sample-by-jsakamoto/Blazor-AllowNoAuthHttpRequest/master/.assets/fig1.png)

What happened!?

# The reason for this exception is...

the Blazor app which was created from the default project template with the "Authentication" option enabled **always** tries to attach an access token to any HTTP requests **whether the user is signed in or not.**

This is the reason that `AccessTokenNotAvailableException` was thrown.

# Solutions

I was thinking for a while, I came up with 3 solutions.

## Solution 1

Solution 1 is, configuring `AuthorizationMessageHanlder` to allow attaching an access token to only selected endpoints which are under the specified subpath.

If you want to choose this solution, at first, you have to rearrange the URLs of endpoints on the server-side (those you want to protect) to be under the same subpath (ex: "/authorized/...").

```csharp
[Authorize]
[ApiController]
//[Route("[controller]")]
[Route("authorized/[controller]")] // <- Change the URL of this API to be under the "authorized" subpath.
public class WeatherForecastController : ControllerBase
{
...
```

Second, you have to configure `AuthorizationMessageHandler` with custom options on the client-side.

You can do this in the `Main` method of the `Program` class of the client-side project.


```csharp
public static async Task Main(string[] args)
{
  var builder = WebAssemblyHostBuilder.CreateDefault(args);
  builder.RootComponents.Add<App>("app");

  // 👇 Add this conifiguration code.
  builder.Services.AddTransient<AuthorizationMessageHandler>(sp =>
  {
    // 👇 Get required services from DI.
    var provider = sp.GetRequiredService<IAccessTokenProvider>();
    var naviManager = sp.GetRequiredService<NavigationManager>();
      
    // 👇 Create a new "AuthorizationMessageHandler" instance,
    //    and return it after configuring it.
    var handler = new AuthorizationMessageHandler(provider, naviManager);
    handler.ConfigureHandler(authorizedUrls: new[] {
      // List up URLs which to be attached access token.
      naviManager.ToAbsoluteUri("authorized/").AbsoluteUri
    });
    return handler;
  });
  ...
```

Finally, you have to configure `HttpClientFactory` to using the `AuthorizationMessageHandler` that is configured by you.

```csharp
  ...
  // 👇 Use "AuthorizationMessageHandler" that is configured above 
  //    instead of "BaseAddressAuthorizationMessageHandler".
  builder.Services.AddHttpClient("BlazorWasmApp.ServerAPI", client => ...)
    // .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
    .AddHttpMessageHandler<AuthorizationMessageHandler>();

  // Supply HttpClient instances that include access tokens when making requests to the server project
  builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>()
    .CreateClient("BlazorWasmApp.ServerAPI"));
  ...
```

After doing this, the access token will be attached for only the URLs which are under the `"/authorized/..."` subpath! 👍

## Solution 2

Solution 2 is, explicitly getting a named HTTP client which is configured to attach an access token from "IHttpClientFactory", when sending HTTP requests to authorization required endpoint.

At first, register a plain `HttpCient` to the DI with just configured only "base address", instead of retrieving from an `IHttpClient` service, in the `Main` method of the `Program` class of client-side project.

```csharp
  public static async Task Main(string[] args)
  {
    ...
    // 👇 Register plain "HttpClient" servcie to DI.
    // builder.Services.AddTransient(sp => ...CreateClient("BlazorWasmApp.ServerAPI"));
    builder.Services.AddTransient(sp => new HttpClient { 
      BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
    });
    ...
```

And, get a `HttpClient` object from `IHttpClientFactory` with name, instead of getting it from DI directly, when accessing to a protected API endpoint.

```csharp
@* @inject HttpClient Http *@
@inject IHttpClientFactory HttpClientFactory
...
@code {
  protected override async Task OnInitializedAsync()
  {
    ...
    // 👇 Don't get a HttpClient from DI directly for accessing a proected API.
    // forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");

    // 👇 Instead, get a HttpCient from IHttpClientFactory service with name explicitly.
    var http = HttpClientFactory.CreateClient("BlazorWasmApp.ServerAPI");
    forecasts = await http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
}
```

## Solution 3

Solution 3 is another solution of opposite to "solution 2".

This solution explicitly gets a named HTTP client which is plain one from "IHttpClientFactory", when sending HTTP requests to anonymous access allowed endpoint.

To do this, register plain `HttpClient` to the `IHttpClientFactory` service with a name.

```csharp
public static async Task Main(string[] args)
{
  ...
  // 👇 Add a plain "HttpClient" with a name.
  builder.Services.AddHttpClient("BlazorWasmApp.AnonymousAPI", client => {
    client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress);
  });

  builder.Services.AddHttpClient("BlazorWasmApp.ServerAPI", ...)
      .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
  ...
```

And, get a `HttpClient` object from `IHttpClientFactory` with name, instead of getting it from DI directly, when accessing a public API endpoint.

```csharp
@* @inject HttpClient Http *@
@inject IHttpClientFactory HttpClientFactory
...
@code {
  protected override async Task OnInitializedAsync()
  {
    ...
    // 👇 Don't get a HttpClient from DI directly for accessing a public API.
    // RecentlyUpdates = await Http.GetFromJsonAsync<string[]>("RecentlyUpdates");

    // 👇 Instead, get a HttpCient from IHttpClientFactory service with name explicitly.
    var http = HttpClientFactory.CreateClient("BlazorWasmApp.AnonymousAPI");
    RecentlyUpdates = await http.GetFromJsonAsync<string[]>("RecentlyUpdates");
}
```

# Conclusion

The Blazor app which was created from the default project template with the "Authentication" option enabled always tries to attach an access token to any HTTP requests whether the user is signed in or not.

This implementation will cause `AccessTokenNotAvailableException` exception when the user is not signed-in even if the accessing is for an anonymously accessible endpoint.

You can avoid this exception by one of these solutions:

- **Solution 1.** - limit attaching the access token to only URLs under the specified subpath.
- **Solution 2.** - Get a configured `HttpClient` explicitly by name to access a protected API.
- **Solution 3.** - Get a plain `HttpClient` explicitly by name to access an anonymously accessible API.

The entire of my sample code is public on the GitHub repository of the following URL:

https://github.com/sample-by-jsakamoto/Blazor-AllowNoAuthHttpRequest

Happy coding! :)