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.