Reputation: 103
I have a hosted Blazor WebAssembly app. The app uses the .net 7 individual accounts auth. In the client I am trying to inherit from AuthenticationStateProvider (so that I can access some of its protected functions and implement some of my own auth pages in Blazor instead or the default .cshtml ones), but I am running in to an issue whereby as soon as I do this I get a crash when creating a httpClient instance and I have no idea why its happening or how to fix it.
Here is my basic AuthenticationStateProvider derived class:
using Microsoft.AspNetCore.Components.Authorization;
using System.Net.WebSockets;
using System.Security.Claims;
namespace SiteBuddy.Client.Services
{
public class CustomStateProvider : AuthenticationStateProvider
{
public async override Task<AuthenticationState> GetAuthenticationStateAsync()
{
var anonymous = new ClaimsIdentity();
return await Task.FromResult(new AuthenticationState(new ClaimsPrincipal(anonymous)));
}
}
}
Its as minimal as I could make it and it does nothing except implement the basic required GetAuthenticationStateAsync function.
In my program.cs file I add the following in order to make sure the new class gets used:
builder.Services.AddScoped<CustomStateProvider>();
builder.Services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<CustomStateProvider>());
At the beginning of my program.cs file I have these two lines:
builder.Services.AddHttpClient("private", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
// Add this to allow us to make anonymous calls that dont require being authenticated
builder.Services.AddHttpClient("public", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
I create 2 HttpClients; the private one is used for almost all my back end calls where authentication is used, the private one is just used for a couple of other functions where I need to allow anonymous calls.
Further down my program.cs I have this line:
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("private"));
As soon as I registered the CustomStateProvider class, my app crashes as soon as I try to create an instance of the 'private' HttpClient. Note that the public one is working just fine, its just the private one that causes a crash. I cant create it using the IHttpClientFactory and any time a class is created where the client is injected it crashes with the exact same error. This is the error:
Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] Unhandled exception rendering component: Specified cast is not valid. System.InvalidCastException: Specified cast is not valid. at Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.<>c__0
3[[Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationState, Microsoft.AspNetCore.Components.WebAssembly.Authentication, Version=7.0.5.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteUserAccount, Microsoft.AspNetCore.Components.WebAssembly.Authentication, Version=7.0.5.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[Microsoft.AspNetCore.Components.WebAssembly.Authentication.ApiAuthorizationProviderOptions, Microsoft.AspNetCore.Components.WebAssembly.Authentication, Version=7.0.5.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].<AddRemoteAuthentication>b__0_0(IServiceProvider sp) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor
2[[Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeResolverContext, Microsoft.Extensions.DependencyInjection, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[System.Object, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].VisitCallSiteMain(ServiceCallSite callSite, RuntimeResolverContext argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2[[Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeResolverContext, Microsoft.Extensions.DependencyInjection, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[System.Object, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].VisitCallSite(ServiceCallSite callSite, RuntimeResolverContext argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor
2[[Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeResolverContext, Microsoft.Extensions.DependencyInjection, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[System.Object, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].VisitCallSiteMain(ServiceCallSite callSite, RuntimeResolverContext argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2[[Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeResolverContext, Microsoft.Extensions.DependencyInjection, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[System.Object, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].VisitCallSite(ServiceCallSite callSite, RuntimeResolverContext argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeServiceProviderEngine.<>c__DisplayClass4_0.<RealizeService>b__0(ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[BaseAddressAuthorizationMessageHandler](IServiceProvider provider) at Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.<>c__4
1[[Microsoft.AspNetCore.Components.WebAssembly.Authentication.BaseAddressAuthorizationMessageHandler, Microsoft.AspNetCore.Components.WebAssembly.Authentication, Version=7.0.5.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].b__4_1(HttpMessageHandlerBuilder b)
It appears to be some kind of circular dependency issue, but I dont understand how that can be happening when I have added practically no code at all to CustomStateProvider.
Can anyone cast any light on what the problem is?
Upvotes: 0
Views: 193
Reputation: 30167
You have problems with both inheritance and registration.
Here's an example custom provider. Note adding your extra stuff to the provided ClaimsPrincipal
.
public class CustomAuthenticationStateProvider : RemoteAuthenticationStateProvider
{
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
var authState = await base.GetAuthenticationStateAsync();
var user = authState.User;
// Get your custom data - in this case some roles
// add some new identities to the Claims Principal
user.AddIdentity(new ClaimsIdentity(new List<Claim>() { new Claim(ClaimTypes.Role, "Admin") }));
user.AddIdentity(new ClaimsIdentity(new List<Claim>() { new Claim(ClaimTypes.Role, "User") }));
// return the modified principal
return await Task.FromResult(new AuthenticationState(user));
}
}
Which you register like this:
//other registrations
//Last one so it overrides the default loaded provider
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();
Upvotes: 0