简体   繁体   中英

Call API from AccountClaimsPrincipalFactory in Blazor WASM

I am using Blazor WASM with AzureB2C to call an API hosted in Azure Functions. I would like to call my API on a successful login to add/update user info into a database. I have been following this guide . When trying to inject my typed httpclient into the AccountClaimsPrincipalFactory I am met with a runtime error:

Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] Unhandled exception rendering component: ValueFactory attempted to access the Value property of this instance. System.InvalidOperationException: ValueFactory attempted to access the Value property of this instance.

This shows in the browser, but the app compiles and runs just fine. The codes works great if I don't inject my PlatformServiceClient , but I need to make the API call to record the user. The following files are involved. I adjusted some things to simplify. This seems like the appropriate approach, but I have not seen examples where an api call was made in the claims factory.

CustomAccountFactory.cs

 public class CustomAccountFactory: AccountClaimsPrincipalFactory<CustomUserAccount>
    {
        public IPlatformServiceClient client { get; set; }
        public CustomAccountFactory(NavigationManager navigationManager,
            IPlatformServiceClient platformServiceClient,
            IAccessTokenProviderAccessor accessor) : base(accessor)
        {
            client = platformServiceClient;
        }

        public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
            CustomUserAccount account, RemoteAuthenticationUserOptions options)
        {
            var initialUser = await base.CreateUserAsync(account, options);

            if (initialUser.Identity.IsAuthenticated)
            {
                //call the API here
                await client.RegisterUserAsync();
                
            }

            return initialUser;
        }
    }

Program.cs excerpt

builder.Services.AddScoped<CustomAuthorizationMessageHandler>();

builder.Services.AddHttpClient<IPlatformServiceClient, PlatformServiceClient>(
    client => client.BaseAddress = new Uri(builder.Configuration["PlatformServiceUrl"]))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();

builder.Services.AddMsalAuthentication<RemoteAuthenticationState, CustomUserAccount>(options =>
{
    builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
    options.ProviderOptions.DefaultAccessTokenScopes.Add("openid");
    options.ProviderOptions.DefaultAccessTokenScopes.Add("offline_access");
    options.ProviderOptions.DefaultAccessTokenScopes.Add("access_as_user");
    options.ProviderOptions.LoginMode = "redirect";
    options.UserOptions.RoleClaim = "roles";
}).AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, CustomUserAccount, CustomAccountFactory>();

CustomAuthorizationMessageHandler.cs

 public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
    {
        public CustomAuthorizationMessageHandler(IAccessTokenProvider provider,
            NavigationManager navigationManager)
            : base(provider, navigationManager)
        {
            ConfigureHandler(
                authorizedUrls: new[] { "http://localhost:7071" },
                scopes: new[] { "access_as_user" });
        }
    }

I solved this by creating a named instance of the client and passing an IHttpClientFactory into the CustomAccountFactory.

builder.Services.AddHttpClient<PlatformServiceClient>("PlatformServiceClient",
    client => client.BaseAddress = new Uri(builder.Configuration["PlatformServiceUrl"]))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();

There I can create a client, but I have to setup my urls manually vs using the typed client where I have this work already done.

var client = factory.CreateClient("PlatformServiceClient");
var response = await client.GetAsync("/user/me");

I also registered the new client prior to calling AddMsalAuthenication

builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("PlatformServiceClient"));

I did all of this following the code found here by Coding Flamingo . It is all working as expected.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM