Reputation: 245
I am using blazor server.
I have a structure where Each User can be part of Multiple organizations and they have a separate roles/claims set based on the organization. When they first login, the loading of roles based on the default organization works fine.
I want the roles to get updated if the user selects a different organization from a dropdown after they are logged in.
How do I update the AuthenticationState so that all the views/UI change based on the new updated roles claim?
I am injecting a custom AuthenticationStateProvider that inherits from RevalidatingServerAuthenticationStateProvider class. (Let's call it RevalidateServerAuthenticationState)
I have a public method in this RevalidateServerAuthenticationState class that gets the current authentication state, updates the roles and then calls SetAuthenticationState.
public class RevalidateServerAuthenticationState<TUser>
: RevalidatingServerAuthenticationStateProvider where TUser : class
{
private readonly IServiceScopeFactory _scopeFactory;
private readonly IdentityOptions _options;
public RevalidateServerAuthenticationState(
ILoggerFactory loggerFactory,
IServiceScopeFactory scopeFactory,
IOptions<IdentityOptions> optionsAccessor)
: base(loggerFactory)
{
_scopeFactory = scopeFactory;
_options = optionsAccessor.Value;
}
protected override TimeSpan RevalidationInterval => TimeSpan.FromSeconds(300);
protected override async Task<bool> ValidateAuthenticationStateAsync(
AuthenticationState authenticationState, CancellationToken cancellationToken)
{
}
public async Task UpdateCurrentOrganization(string currentOrganizationId)
{
var authState = await this.GetAuthenticationStateAsync();
var scope = _scopeFactory.CreateScope();
try
{
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
if (authState.User?.Identity.IsAuthenticated == true)
{
var identity = authState.User.Identities.First();
var user = await userManager.GetUserAsync(authState.User);
if (userManager.SupportsUserRole)
{
var userOrgRoles = await user.GetUserRoles(user.DefaultOrgId, dbContext);
var claimsToRemove = identity.Claims.Where(x => x.Type == ClaimTypes.Role
&& !userOrgRoles.Contains(x.Value))
.ToArray();
foreach (var claim in claimsToRemove)
{
identity.TryRemoveClaim(claim);
}
}
var currentOrgIdClaim = identity.Claims.FirstOrDefault(x => x.Type == "CurrentOrgId");
if (currentOrgIdClaim != default)
{
identity.TryRemoveClaim(currentOrgIdClaim);
}
var defaultOrgIdClaim = identity.Claims.FirstOrDefault(x => x.Type == "DefaultOrgId");
if (defaultOrgIdClaim != default)
{
identity.TryRemoveClaim(defaultOrgIdClaim);
}
identity.AddClaim(new Claim("CurrentOrgId", currentOrganizationId ?? string.Empty));
identity.AddClaim(new Claim("DefaultOrgId", user.DefaultOrgId ?? string.Empty));
var userOrgSettings = await dbContext.UserOrgSettings
.FirstOrDefaultAsync(x => x.UserId == user.Id
&& x.OrgId == currentOrganizationId);
identity.AddClaim(new Claim("AdminAccess", userOrgSettings != null && userOrgSettings.AdminAccess ? "true" : "false"));
identity.AddClaim(new Claim("FinancialAccess", userOrgSettings != null && userOrgSettings.FinancialAccess ? "true" : "false"));
var newAuthState = Task.FromResult(authState);
SetAuthenticationState(newAuthState);
}
}
finally
{
if (scope is IAsyncDisposable asyncDisposable)
{
await asyncDisposable.DisposeAsync();
}
else
{
scope.Dispose();
}
}
}
}
When the user changes their organization in a dropdown, this method is called and then the page is force reloaded. Upon reloading the page, when the AuthenticationState is read to get the user's roles, it still only shows the roles and claims that were loaded when the user first logged in.
How do I dynamically change the AuthenticationState to update the current list of roles and claims?
Upvotes: 1
Views: 3207
Reputation: 149
literally change SetAuthenticationState(newAuthState);
to NotifyAuthenticationStateChanged(newAuthState);
See this similar issue I had.
Upvotes: 0
Reputation: 11826
A custom AuthenticationStateProvider can invoke NotifyAuthenticationStateChanged on the AuthenticationStateProvider base class to notify consumers of the authentication state change to rerender.
You could check this document related and follow the sample codes in the document
If you still have question related ,please show the UI codes that could reproduce the issue and I could found a solution for you
Upvotes: 0