Blazor应用对接外部OIDC授权中心登出功能失效求助
问题背景
我搭建了一个对接外部oidc授权中心的Blazor应用,登录功能正常,但登出功能失效。期望执行登出时跳转至授权中心完成登出,之后返回/signout-callback-oidc,但实际无任何反应。
认证配置代码
builder.Services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultAuthenticateScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie(options => { options.Cookie.SameSite = SameSiteMode.Lax; options.Cookie.IsEssential = true; options.AccessDeniedPath = "/AccessDenied"; }) .AddOpenIdConnect("oidc", options => { options.Authority = jwtBearerOptions.Authority; options.ClientId = jwtBearerOptions.ClientId; options.ClientSecret = jwtBearerOptions.ClientSecret; options.ResponseType = "code"; options.GetClaimsFromUserInfoEndpoint = false; options.Scope.Add("openid"); options.Scope.Add("profile"); options.Scope.Add("offline_access"); options.RequireHttpsMetadata = false; options.CallbackPath = "/signin-oidc"; options.SignedOutCallbackPath = "/signout-callback-oidc"; options.SignInScheme = "Cookies"; })
当前登出代码
await HttpContext.SignOutAsync("Cookies"); await HttpContext.SignOutAsync("oidc", new AuthenticationProperties { RedirectUri = "/" }); NavigationManager.NavigateTo("/");
更新测试情况
使用Visual Studio 2022创建.NET 8的Blazor Web App(Server渲染模式,无默认认证),添加Microsoft.AspNetCore.Authentication.OpenIdConnect包,配置builder.Services.AddAuthentication、app.UseAuthentication和app.UseAuthorization(配置代码同上):
- 为
Counter.razor页面添加@attribute [Authorize] - 修改
Routes.razor为:
@using Microsoft.AspNetCore.Components.Authorization <CascadingAuthenticationState> <Router AppAssembly="typeof(Program).Assembly"> <Found Context="routeData"> <AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" /> <FocusOnNavigate RouteData="routeData" Selector="h1" /> </Found> </Router> </CascadingAuthenticationState>
此时Counter页面显示“未授权”但未触发登录,移除App.razor中<Routes>的@rendermode="InteractiveServer"后恢复正常。
添加登出逻辑代码:
@code { [CascadingParameter] private HttpContext HttpContext { get; set; } = default!; private int currentCount = 0; private ClaimsPrincipal User { get; set; } protected async override Task OnInitializedAsync() { AuthenticationState authstate = await _AuthenticationStateProvider.GetAuthenticationStateAsync(); User = authstate.User; if (HttpContext.User.Identity?.IsAuthenticated ?? false) { if (HttpContext.Request.Query.ContainsKey("Logout")) { await HttpContext.SignOutAsync("Cookies"); await HttpContext.SignOutAsync("oidc", new AuthenticationProperties { RedirectUri = "/" }); NavigationManager.NavigateTo("/"); } } } }
访问/counter?Logout仅完成本地登出,未跳转至IdentityServer,但再次访问/counter时会触发授权中心登录。
解决方案
核心问题原因
Blazor Server组件运行在SignalR连接上下文内,直接调用HttpContext.SignOutAsync("oidc")生成的跳转响应无法通过SignalR通道传递给客户端,浏览器收不到跳转指令,因此只会清除本地Cookie,不会触发授权中心的登出流程。
修复步骤
- 用控制器处理登出请求
创建专门的AccountController,通过标准HTTP请求处理登出:
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Mvc; public class AccountController : Controller { [HttpGet("logout")] public async Task<IActionResult> Logout() { await HttpContext.SignOutAsync("Cookies"); return SignOut(new AuthenticationProperties { RedirectUri = "/" }, "oidc"); } }
- 调整组件内的登出触发逻辑
在Blazor组件中,通过强制加载的方式导航到控制器的登出接口:
@inject NavigationManager NavigationManager <button @onclick="TriggerLogout">登出</button> @code { private void TriggerLogout() { NavigationManager.NavigateTo("/logout", forceLoad: true); } }
forceLoad: true会强制浏览器发起全新HTTP请求,而非Blazor客户端路由,确保控制器的跳转逻辑能被浏览器执行。
- 验证授权中心配置
- 确认授权中心的
{Authority}/.well-known/openid-configuration端点包含end_session_endpoint - 在授权中心的客户端配置中,将
/signout-callback-oidc或/添加到PostLogoutRedirectUris允许列表
内容的提问来源于stack exchange,提问作者Richard Barraclough

