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

Time:09-02

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.

CodePudding user response:

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.

CodePudding user response:

You can do that in various ways... To my mind using a middleware is the preferred way. You may also do that in the _Host file with similar code.

Here's a small code sample upon which you can improve your code( .Net 5.0)

Configure method

app.Use(async (context, next) =>
 {
     await next.Invoke();
     if (context.User.Identity.IsAuthenticated)
     {
        var userName = context.User.Identity.Name;
        using (var dbContext = 
        context.RequestServices.GetRequiredService<ApplicationDbContext>())
        {
          var user = dbContext.Users.Where(u => u.UserName == 
                 userName).FirstOrDefault();
           if (user != null && user.Email == "")
           {
               user.PhoneNumber = "1234567";
               dbContext.Update(user);
               dbContext.SaveChanges();

            }
          }
      }
});

The code is very simple... It checks if the user is authenticated, and if it does, it creates an instance of the DBContext service, access the Users table, retrive a given user, apply some changes, and save.

  • Related