I've implemented a user authorization in Blazor server using both local DB and Microsoft Azure.
- When a user authorizes using Microsoft Azure the authorization is persistent when changing Blazor pages.
- When a user authorizes using local DB the authorization is reset (GetAuthorizationState returns null). And my question is why?
I've a custom CustomAuthenticationStateProvider:
public class CustomAuthenticationStateProvider : AuthenticationStateProvider
{
private readonly IHttpContextAccessor _httpContextAccessor;
public CustomAuthenticationStateProvider(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
var principal = _httpContextAccessor.HttpContext?.User;
if (principal?.Identity?.IsAuthenticated != true)
{
principal = new ClaimsPrincipal(new ClaimsIdentity());
}
return Task.FromResult(new AuthenticationState(principal));
}
public void MarkUserAsAuthenticated(ClaimsPrincipal user)
{
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user)));
}
public void MarkUserAsLoggedOut()
{
var principal = _httpContextAccessor.HttpContext?.User;
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(principal)));
}
}
In my Program.cs i have the azure authentication setup as follow:
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(
options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.AccessDeniedPath = "/accessdenied";
//builder.Configuration.GetSection("AzureAd")
builder.Configuration.Bind("AzureAD", options);
options.Events ??= new OpenIdConnectEvents();
options.Events.OnTokenValidated = async context =>
{
var identity = (ClaimsIdentity)context.Principal.Identity;
var email = identity.Name;
var repository = new UsersRepository(new SqlConnectionFactory(builder.Configuration.GetConnectionString("SuperSecretConnectionString")));
var roles = await repository.GetUserRolesAsync(email);
foreach (var role in roles)
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
}
};
}
);
And here is my code that authenticates user when he is logging in using the local DB (email and password)
public async Task SignInUserAsync(object user)
{
List<string> roles = await _usersRepository.GetUserRolesAsync(user.Email);
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.Name), // Use the actual name from the user model
new Claim(ClaimTypes.Email, user.Email), // Use the actual email from the user model
};
foreach (var role in roles)
{
var roleClaim = new Claim(ClaimTypes.Role, role);
claims.Add(roleClaim); // Add each role claim
}
var identity = new ClaimsIdentity(claims, /*Explicit*/CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);
((CustomAuthenticationStateProvider)AuthenticationStateProvider).MarkUserAsAuthenticated(principal);
}
Thank you for any input!
I've implemented a user authorization in Blazor server using both local DB and Microsoft Azure.
- When a user authorizes using Microsoft Azure the authorization is persistent when changing Blazor pages.
- When a user authorizes using local DB the authorization is reset (GetAuthorizationState returns null). And my question is why?
I've a custom CustomAuthenticationStateProvider:
public class CustomAuthenticationStateProvider : AuthenticationStateProvider
{
private readonly IHttpContextAccessor _httpContextAccessor;
public CustomAuthenticationStateProvider(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
var principal = _httpContextAccessor.HttpContext?.User;
if (principal?.Identity?.IsAuthenticated != true)
{
principal = new ClaimsPrincipal(new ClaimsIdentity());
}
return Task.FromResult(new AuthenticationState(principal));
}
public void MarkUserAsAuthenticated(ClaimsPrincipal user)
{
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user)));
}
public void MarkUserAsLoggedOut()
{
var principal = _httpContextAccessor.HttpContext?.User;
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(principal)));
}
}
In my Program.cs i have the azure authentication setup as follow:
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(
options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.AccessDeniedPath = "/accessdenied";
//builder.Configuration.GetSection("AzureAd")
builder.Configuration.Bind("AzureAD", options);
options.Events ??= new OpenIdConnectEvents();
options.Events.OnTokenValidated = async context =>
{
var identity = (ClaimsIdentity)context.Principal.Identity;
var email = identity.Name;
var repository = new UsersRepository(new SqlConnectionFactory(builder.Configuration.GetConnectionString("SuperSecretConnectionString")));
var roles = await repository.GetUserRolesAsync(email);
foreach (var role in roles)
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
}
};
}
);
And here is my code that authenticates user when he is logging in using the local DB (email and password)
public async Task SignInUserAsync(object user)
{
List<string> roles = await _usersRepository.GetUserRolesAsync(user.Email);
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.Name), // Use the actual name from the user model
new Claim(ClaimTypes.Email, user.Email), // Use the actual email from the user model
};
foreach (var role in roles)
{
var roleClaim = new Claim(ClaimTypes.Role, role);
claims.Add(roleClaim); // Add each role claim
}
var identity = new ClaimsIdentity(claims, /*Explicit*/CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);
((CustomAuthenticationStateProvider)AuthenticationStateProvider).MarkUserAsAuthenticated(principal);
}
Thank you for any input!
Share Improve this question asked Mar 10 at 12:00 baxiusbaxius 3710 bronze badges 5- GPT provides this answer: ''Blazor Server is stateful, but HTTP is stateless. When a user logs in using Azure AD, the authentication token is stored in a cookie, which is sent with every request. When a user logs in using local DB, their authentication is only stored in memory, and since Blazor Server apps can lose state (e.g., after a disconnection or reload), it resets. '' – baxius Commented Mar 10 at 20:04
- I had a test in my side and I can reproduce your issue. In my side I followed this document to use custom auth state for my blazor server app(.NET 7). Sign in could work but refreshing the website would clear the auth state. But if I sign in with Azure AD, the auth state could persist. – Tiny Wang Commented Mar 11 at 10:24
- I'm afraid this is due to local sign in doesn't handle the auth state persistence task well as blazor uses Singalr connections so that each refresh will create a new Circuit which is recognized as a new connection. We need to find a built-in/custom approach to manage the auth state. Maybe Cookie/local storage/server side mechanism, I haven't done. – Tiny Wang Commented Mar 11 at 10:26
- @TinyWang i've tried to implement JWT authentication. Now the local-side authentication is working but Azure AD fails. This custom (see my next reply) GetAuthenticationStateAsync returns false when using Azure AD. – baxius Commented Mar 13 at 13:51
- I think what we need to try is about cookie. In my test, if I only works on AAD auth, refresh the page won't cause auth state clear, if I only works on .Net core Default Identity auth, refresh the page won't clear auth state too. I found this is controlled by cookie stored in browser. So that if we follow the custom auth state document and added cookie in the sign in method, we can resolve the issue I think. But I haven't worked it out. – Tiny Wang Commented Mar 14 at 9:17
1 Answer
Reset to default 0
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
var token = await _localStorage.GetItemAsync<string>(TokenKey);
if (string.IsNullOrWhiteSpace(token))
{
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
}
if (TokenExpired(token))
{
await _localStorage.RemoveItemAsync(TokenKey);
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
}
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var identity = new ClaimsIdentity(ParseClaims(token), "jwt");
var user = new ClaimsPrincipal(identity);
return new AuthenticationState(user);
}
Fixed the server Log-in. But now the Azure AD is failing to authenticate against JWT authentication
both authentications are configured like this:
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
};
})
.AddMicrosoftIdentityWebApp(options =>
{
builder.Configuration.Bind("AzureAD", options);
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.AccessDeniedPath = "/accessdenied";
options.Events ??= new OpenIdConnectEvents();
});
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744848118a4596966.html
评论列表(0条)