Share via

Configuring .NET 10 Blazor WASM program.cs to use an HTTP Delegate to attach JWT Security Token

Bob N 0 Reputation points
2026-06-08T19:04:39.7466667+00:00

My knowledge of JWT security tokens until several weeks ago was nil. After a bit of reading, I was able to retrieve a security token from a 'login' server, and able to use that token within Blazor, and with the addition of an AuthenticationStateProvider it would correctly display the user name and have the <AuthorizedView> behaving as well. After reading further, it looked like using an Http delegate handler was the correct approach use to attach the token and handle refresh as needed. As proof of concept, I was able to basically cut/paste some code to insert the delegate handler -- no action taken yet -- and have it pass along the request. Again, so far, so good. I dug a bit further for an example that attached the token (see https://blog.stackademic.com/build-a-secure-blazor-webassembly-app-with-asp-net-core-10-and-jwt-authentication-ba341350868b)

I thought I was done -- however I could not get the code provided at that site to actually use the HttpDelegate. After a bit of fruitless tinkering, I realized my knowledge of all the 'setup' in Program.cs (AddScoped, AddTransient, HttpMessageHandler) were all beyond my depth of understanding, in particular how constructor for the DelegeHandler receives it's parameters. In my code where it is actually invoked, the constructor looks like:

        public SecureHttpDelegateHandler(ILogger<SecureHttpDelegateHandler> logger) {  _logger = logger; }

And the delegate is called and can be seen in the log trace

     protected override async Task<HttpResponseMessage>SendAsync( HttpRequestMessage request, CancellationToken cancellationToken)
     {
         try
         {
             _logger.LogInformation("Before HTTP request");
             var result = await base.SendAsync(request, cancellationToken);
             result.EnsureSuccessStatusCode();
             _logger.LogInformation("After HTTP request");
             return result;
         }
         catch (Exception e)
         {
             _logger.LogError(e, "HTTP request failed");
             throw;
         }
     }

When I try and switch over to the version of the handler that is passed to token storage and the service to retrieve and refresh the tokens:

      public SecureHttpDelegateHandler(TokenStorage tokenStorage, AuthService auth) { _tokenStorage = tokenStorage; _auth = auth;  }

I immediately get the error

AggregateException_ctor_DefaultMessage (Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: XXX.Services.SecureHttpDelegateHandler Lifetime: Transient ImplementationType: XX.Services.SecureHttpDelegateHandler': CannotResolveService, XXX.Services.AuthService, XX.Services.SecureHttpDelegateHandler))

After much unsuccessful tinkering, I have realized my depth of knowledge on what has to be done in program.cs (and it what order) has come up short.

What I currently have that does not work is below. Please advise on where I have gone awry -- much of this is beyond my current understanding.

Thanks!

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("https://jsonplaceholder.typicode.com") });
builder.Services.AddTransient<SecureHttpDelegateHandler>();
builder.Services.AddHttpClient("XXXAPI", httpClient =>
{
    httpClient.BaseAddress = new Uri("https://jsonplaceholder.typicode.com");
})
.AddHttpMessageHandler<SecureHttpDelegateHandler>();

builder.Services.AddScoped<TokenStorage>();
builder.Services.AddScoped<AuthenticationStateProvider, AppAuthenticationStateProvider>();
builder.Services.AddScoped<PostService>();
builder.Services.AddScoped(sp=> sp.GetRequiredService<IHttpClientFactory>().CreateClient("XXXPI"));


builder.Services.AddAuthorizationCore();

var app = builder.Build();

await app.RunAsync();

Developer technologies | .NET | Blazor
0 comments No comments

2 answers

Sort by: Most helpful
  1. Damien Pham (WICLOUD CORPORATION) 1,755 Reputation points Microsoft External Staff Moderator
    2026-06-09T04:19:34.82+00:00

    Hello @Bob N ,

    Thank you for sharing.

    I can see why this feels confusing. The immediate problem is not JWT itself; it is the dependency injection setup.

    AddHttpMessageHandler<SecureHttpDelegateHandler>() tells IHttpClientFactory to create that handler through dependency injection. That means every constructor dependency of SecureHttpDelegateHandler must be registered in the service container.

    In your failing version, the handler requires AuthService:

    public SecureHttpDelegateHandler(TokenStorage tokenStorage, AuthService auth)
    {
        _tokenStorage = tokenStorage;
        _auth = auth;
    }
    

    However, AuthService is not registered in the code shown, which matches the exception you posted.

    For this setup, the registrations should look more like this:

    var builder = WebAssemblyHostBuilder.CreateDefault(args);
    
    builder.RootComponents.Add<App>("#app");
    builder.RootComponents.Add<HeadOutlet>("head::after");
    
    builder.Services.AddScoped<TokenStorage>();
    builder.Services.AddScoped<AuthService>();
    builder.Services.AddScoped<AuthenticationStateProvider, AppAuthenticationStateProvider>();
    builder.Services.AddScoped<PostService>();
    
    builder.Services.AddTransient<SecureHttpDelegateHandler>();
    
    builder.Services.AddHttpClient("XXXAPI", client =>
    {
        client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com");
    })
    .AddHttpMessageHandler<SecureHttpDelegateHandler>();
    
    builder.Services.AddScoped(sp =>
        sp.GetRequiredService<IHttpClientFactory>().CreateClient("XXXAPI"));
    
    builder.Services.AddAuthorizationCore();
    
    var app = builder.Build();
    await app.RunAsync();
    

    There are two specific issues in your sample:

    1. AuthService needs to be registered so the handler can be constructed.
    2. CreateClient("XXXPI") does not match the registered client name, which is "XXXAPI". The names must match exactly.

    Microsoft's IHttpClientFactory guidance explains that delegating handlers are used as outgoing request middleware, and the Blazor WebAssembly security guidance shows the recommended pattern of registering an authorization handler and attaching it with AddHttpClient(...).AddHttpMessageHandler(...):

    If you are using the built-in Blazor/OIDC authentication flow, I would also consider using AuthorizationMessageHandler or BaseAddressAuthorizationMessageHandler, because that is the framework-supported pattern for attaching access tokens to outgoing requests:

    I hope this helps. If you found my response helpful or informative, I would greatly appreciate it if you could follow this guidance or provide feedback.  

    Thank you.  

     

    Was this answer helpful?


  2. AI answer

    2026-06-08T19:05:07.0466667+00:00

    SecureHttpDelegateHandler can only be constructed if all of its constructor parameters are registered in the DI container before the handler itself is registered. The error indicates that AuthService (and possibly TokenStorage) is not resolvable when the handler is created.

    In Blazor WebAssembly, DelegatingHandler-based token handlers are typically wired using AuthorizationMessageHandler / BaseAddressAuthorizationMessageHandler and IAccessTokenProvider, but the same DI rules apply: every dependency in the handler constructor must be registered.

    Key points for the shown setup:

    1. Register dependencies before the handler
    2. Ensure the handler is registered with the correct lifetime
    3. Ensure the named HttpClient is created correctly

    A corrected ordering for the registrations would look like this:

    var builder = WebAssemblyHostBuilder.CreateDefault(args);
    builder.RootComponents.Add<App>("#app");
    builder.RootComponents.Add<HeadOutlet>("head::after");
    
    // Base HttpClient (if needed)
    builder.Services.AddScoped(sp => new HttpClient
    {
        BaseAddress = new Uri("https://jsonplaceholder.typicode.com")
    });
    
    // Register services used by the handler FIRST
    builder.Services.AddScoped<TokenStorage>();
    builder.Services.AddScoped<AuthService>();
    
    // Now register the handler that depends on TokenStorage and AuthService
    builder.Services.AddTransient<SecureHttpDelegateHandler>();
    
    // Register the named HttpClient that uses the handler
    builder.Services.AddHttpClient("XXXAPI", httpClient =>
    {
        httpClient.BaseAddress = new Uri("https://jsonplaceholder.typicode.com");
    })
    .AddHttpMessageHandler<SecureHttpDelegateHandler>();
    
    // Other app services
    builder.Services.AddScoped<AuthenticationStateProvider, AppAuthenticationStateProvider>();
    builder.Services.AddScoped<PostService>();
    
    // Factory-based client for injection
    builder.Services.AddScoped(sp =>
        sp.GetRequiredService<IHttpClientFactory>().CreateClient("XXXAPI"));
    
    builder.Services.AddAuthorizationCore();
    
    var app = builder.Build();
    
    await app.RunAsync();
    

    Notes:

    • AuthService must be registered (AddScoped<AuthService>() or similar) with the same or longer lifetime than the handler’s dependencies.
    • The handler’s constructor can then safely be:
        public SecureHttpDelegateHandler(TokenStorage tokenStorage, AuthService auth)
        {
            _tokenStorage = tokenStorage;
            _auth = auth;
        }
      
    • When using Blazor WebAssembly with JWTs, consider using AuthorizationMessageHandler and IAccessTokenProvider as described in the Blazor WebAssembly additional security scenarios. These are designed to attach tokens to outgoing requests and handle token acquisition/refresh.

    References:

    AI-generated content may be incorrect. Read our transparency notes for more information.

    Was this answer helpful?

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.