Search code examples
asp.net-coreblazormiddleware

Blazor - B2C authentication - What is the proper way to persist user data on login?


I'm building a Blazor app to see how I can persist user data after a B2C AD login.

I want to persist claim data to sql database (ef 6 core) when the user logs in to the app.

I'm trying to capture a Tenant for the user for use in filtering on the app.

I is custom middleware a good way to go with this?

This is a Blazor Server Side app

I have something like this for testing.

public class PersistUserChangesMiddleware
    {
        private readonly RequestDelegate _next;

        public PersistUserChangesMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        [Authorize]
        public Task Invoke(HttpContext httpContext, MyContext context)
        {

            try
            {
                var user = httpContext.User;
                var claims = user.Claims;
                var tenant = claims?.FirstOrDefault(c => c.Type.Equals("extension_CompanyId", StringComparison.OrdinalIgnoreCase));

                if(tenant != null)
                {
                    context.Tenants.Add(new Models.Tenant()
                    {
                        TenantName = tenant.Value
                    });
                    context.SaveChanges();
                }

            }
            catch (Exception)
            {

                throw;
            }

            return _next(httpContext);
        }
    }

}

I'm not getting the user back from this call in the middleware. Do I need to do it a different way for Blazor? I set [Authorize] but still no user.


Solution

  • I can't see which AuthenticationStateProvider is configured, but it's likely to be ServerAuthenticationStateProvider.

    Create a custom AuthenticationStateProvider which is essentially a pass through provider that just grabs the ClaimsPrincipal user and does whatever you want with it. (Let me know if you're using a different provider).

    public class MyAuthenticationStateProvider : ServerAuthenticationStateProvider
    {
        public override async Task<AuthenticationState> GetAuthenticationStateAsync()
        {
            var authstate = await base.GetAuthenticationStateAsync();
    
            if (authstate.User is not null)
            {
                ClaimsPrincipal user = authstate.User;
                // do stuff with the ClaimsPrincipal
            }
    
            return authstate;
        }
    }
    

    And then register it in Program:

    builder.Services.AddRazorPages();
    builder.Services.AddServerSideBlazor();
    // sequence is crucial - Must be after AddServerSideBlazor
    builder.Services.AddScoped<AuthenticationStateProvider, MyAuthenticationStateProvider>();
    builder.Services.AddSingleton<WeatherForecastService>();
    

    Test it with a break point on the first line of GetAuthenticationStateAsync.