Blazor WebAssembly使用OIDC时,静默认证的AuthenticationStateChanged事件不触发
AuthenticationStateChanged事件不触发 我正在迁移一个此前使用自定义AuthenticationStateProvider的客户端Blazor WebAssembly应用,将其更新为通过Microsoft现成的RemoteAuthenticationService和AddOidcAuthentication()方法对接OIDC身份提供商服务器。
在旧实现中,无论是用户通过登录UI完成登录,还是用户启动应用时已处于登录状态、认证仅需静默验证,AuthenticationStateChanged事件都会触发。这并非我的代码实现的,而是由Microsoft认证框架处理的。
切换至Microsoft的OIDC支持后,AuthenticationStateChanged事件仅在用户通过UI登录时触发,而在静默验证认证(令牌仍有效,用户启动应用)时不会触发。
我希望实现该事件像之前一样触发:每次应用启动,在认证完成后触发事件——无论是通过登录UI路由完成认证,还是因令牌仍有效而通过OIDC服务器静默解析认证等场景。
我的OIDC认证子系统配置如下:
builder.Services.AddOidcAuthentication(options => { // Configure our OIDC Identity Provider options.ProviderOptions.Authority = "{OIDC-PROVIDER-AUTHORITY-URL}"; options.ProviderOptions.ClientId = "{OIDC-CLIENT-ID}"; options.ProviderOptions.ResponseMode = "query"; options.ProviderOptions.DefaultScopes.Add("email"); // openid and profile are already there by default }); builder.Services.AddAuthorizationCore(options => { options.AddPolicy("IsCustomUser", policy => policy.RequireClaim("custom_user", "true")); options.AddPolicy("IsCustomAdmin", policy => policy.RequireClaim("custom_admin", "true")); });
请问有什么方法能让AuthenticationStateChanged事件在静默认证验证时也触发?
解决方案
方法1:自定义RemoteAuthenticationProvider主动触发状态变更
创建自定义OIDC认证提供者,继承RemoteAuthenticationProvider<RemoteAuthenticationState>,在静默认证和常规登录完成后手动调用NotifyAuthenticationStateChanged触发事件。
public class CustomOidcAuthenticationProvider : RemoteAuthenticationProvider<RemoteAuthenticationState> { private readonly IUserAuthenticationStateProvider _authStateProvider; public CustomOidcAuthenticationProvider(IUserAuthenticationStateProvider authStateProvider) { _authStateProvider = authStateProvider; } public override async Task<RemoteAuthenticationResult<RemoteAuthenticationState>> ProcessSignInAsync(RemoteAuthenticationContext<RemoteAuthenticationState> context) { var result = await base.ProcessSignInAsync(context); if (result.Status == RemoteAuthenticationStatus.Success) { await TriggerAuthStateChange(); } return result; } public override async Task<RemoteAuthenticationResult<RemoteAuthenticationState>> ProcessSilentSignInAsync(RemoteAuthenticationContext<RemoteAuthenticationState> context) { var result = await base.ProcessSilentSignInAsync(context); if (result.Status == RemoteAuthenticationStatus.Success) { await TriggerAuthStateChange(); } return result; } private async Task TriggerAuthStateChange() { await _authStateProvider.NotifyAuthenticationStateChanged( _authStateProvider.GetAuthenticationStateAsync()); } }
在服务配置中替换默认提供者:
builder.Services.AddOidcAuthentication(options => { options.ProviderOptions.Authority = "{OIDC-PROVIDER-AUTHORITY-URL}"; options.ProviderOptions.ClientId = "{OIDC-CLIENT-ID}"; options.ProviderOptions.ResponseMode = "query"; options.ProviderOptions.DefaultScopes.Add("email"); }) .AddRemoteAuthenticationProvider<CustomOidcAuthenticationProvider>();
方法2:利用OIDC事件回调触发状态变更
通过配置OIDC的事件回调,在静默认证成功后直接触发事件,无需自定义整个提供者:
builder.Services.AddOidcAuthentication(options => { options.ProviderOptions.Authority = "{OIDC-PROVIDER-AUTHORITY-URL}"; options.ProviderOptions.ClientId = "{OIDC-CLIENT-ID}"; options.ProviderOptions.ResponseMode = "query"; options.ProviderOptions.DefaultScopes.Add("email"); }); builder.Services.PostConfigure<RemoteAuthenticationOptions<OidcProviderOptions>>(options => { options.Events.OnSilentSignInSucceeded = async context => { var authStateProvider = context.HttpContext.RequestServices.GetRequiredService<IUserAuthenticationStateProvider>(); await authStateProvider.NotifyAuthenticationStateChanged( authStateProvider.GetAuthenticationStateAsync()); }; });
方法3:在App组件中主动触发状态检查
在应用根组件App.razor初始化时,主动获取认证状态,若用户已通过静默认证则手动触发事件:
@inject IUserAuthenticationStateProvider AuthStateProvider @implements IAsyncDisposable @code { private IDisposable? _authSubscription; protected override async Task OnInitializedAsync() { var authState = await AuthStateProvider.GetAuthenticationStateAsync(); if (authState.User.Identity?.IsAuthenticated == true) { await AuthStateProvider.NotifyAuthenticationStateChanged( AuthStateProvider.GetAuthenticationStateAsync()); } _authSubscription = AuthStateProvider.AuthenticationStateChanged += HandleAuthStateChanged; await base.OnInitializedAsync(); } private void HandleAuthStateChanged(Task<AuthenticationState> authStateTask) { // 你的状态变更处理逻辑 } public async ValueTask DisposeAsync() { _authSubscription?.Dispose(); await Task.CompletedTask; } }
内容的提问来源于stack exchange,提问作者Todd

