Blazor Adding HttpClientHandler to add Jwt to HTTP Header on requests

Matthew Flynn picture Matthew Flynn · Aug 22, 2019 · Viewed 7k times · Source

I am using the Visual Studio 2019 and .Net Core 3.0.0-preview-7 with the standard Blazor Client, Server and Shared templates.

In the application our server side WebApi application will always require a JWT token to be present in the header for authorization.

From looking at the following

Make HTTP requests using IHttpClientFactory in ASP.NET Core

I created the following handler;

public class JwtTokenHeaderHandler : DelegatingHandler
{
    private readonly ILocalStorageService _localStorage;

    public JwtTokenHeaderHandler(ILocalStorageService localStorage)
    {
        _localStorage = localStorage;
    }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        if (!request.Headers.Contains("bearer"))
        {
            var savedToken = await _localStorage.GetItemAsync<string>("authToken");

            if (!string.IsNullOrWhiteSpace(savedToken))
            {
                request.Headers.Add("bearer", savedToken);
            }
        }

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

Where I use Blazored.LocalStorage to get the saved token from localstorage and add it to the header.

Now, at this point I am not sure what to do as if I add the following to the Blazor.Client Startup.cs;

services.AddTransient<JwtTokenHeaderHandler>();
services.AddHttpClient("JwtTokenHandler")
    .AddHttpMessageHandler<JwtTokenHeaderHandler>();

I get the error message;

'IServiceCollection' does not contain a definition for 'AddHttpClient' and no accessible extension method 'AddHttpClient' accepting a first argument of type 'IServiceCollection' could be found (are you missing a using directive or an assembly reference?)

Can anyone direct me to what I am doing wrong here?

Answer

enet picture enet · Aug 22, 2019

@Matthew Flynn, currently you can't use IHttpClientFactory on client-side Blazor.

And you don't have to derive from HttpMessageHandler (DelegatingHandler). It has already been done by Blazor. The following is n extension class to extend the functionality of the HttpClient service to enable the ability to add the Jwt token to the header of the request message...

ServiceExtensions.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Security.Claims;
    using System.Text;
    using System.Text.Json.Serialization;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Components;
    using Microsoft.Extensions.DependencyInjection;

 public static class ServiceExtensions
    {    
     public static async Task<T> GetJsonAsync<T>(this HttpClient httpClient, string url, AuthenticationHeaderValue authorization)
            {
                var request = new HttpRequestMessage(HttpMethod.Get, url);
                request.Headers.Authorization = authorization;

                var response = await httpClient.SendAsync(request);
                var responseBytes = await response.Content.ReadAsByteArrayAsync();
                return JsonSerializer.Parse<T>(responseBytes, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
            }
}

The following show how to call an endpoint on your Web Api, passing the Jwt token which is read from the localStorage. (incidentally, none of these versions is secured with data protection)

Index.razor

@page "/"

@inject ILocalStorageService localStorage
@inject HttpClient Http

<div class="mdc-card main-content-card">
    <h1 class="@MdcTypography.H4">Hello, world!</h1>

    Welcome to your new app.
</div>

// Razor content to display emloyees come here.....

@code {
Employee[] employees;

    protected override async Task OnInitAsync()
    {
        var token = await localStorage.GetTokenAsync();
        employees = await Http.GetJsonAsync<Employee[]>(
            "api/employees",
            new AuthenticationHeaderValue("Bearer", token));
    }
}

Hope this works... If not, and you can't solve thr errors, come here and tell the community about it...