IdentityServer4中Backchannel Logout返回400 BadRequest的正确实现
OpenID Backchannel Logout:正确返回400 BadRequest的实现方法
场景与问题
拥有基于OpenID的自建服务器及多客户端,使用Backchannel Logout实现全局登出。根据OpenID规范,收到logout_token后需验证有效性,验证失败时需返回400 BadRequest。
现有客户端Startup.cs中的相关实现代码如下:
public void ConfigureServices(IServiceCollection services) { //... 其他配置 services.AddAuthentication() .AddCookie(/* 配置选项 */) .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options => { //... 其他配置 options.Events.OnRemoteSignOut = context => { var parameters = context.ProtocolMessage.Parameters; if (parameters.TryGetValue("logout_token", out string token)) { if(...) // 按规范验证令牌有效性 { // 执行登出操作 } else return Task.FromResult(new BadRequestResult()); // <-- 此处为问题核心 } return Task.CompletedTask; } } }
当前问题:执行到标记行的BadRequestResult代码时,Postman返回的是404而非预期的400。OnRemoteSignOut的返回类型为Func<RemoteSignOutContext, Task>,不清楚如何正确返回符合规范的400错误。
规范要求(翻译自OpenID登出令牌验证官方规范)
当验证
logout_token失败时,服务器必须返回HTTP 400(Bad Request)状态码。
解决方案
不能直接返回BadRequestResult,因为OnRemoteSignOut事件的上下文RemoteSignOutContext提供了Response对象,需要直接操作响应来设置状态码,并标记事件已处理,避免后续流程触发404。修改后的代码如下:
options.Events.OnRemoteSignOut = async context => { var parameters = context.ProtocolMessage.Parameters; if (parameters.TryGetValue("logout_token", out string token)) { if(...) // 按规范验证令牌有效性 { // 执行登出操作 await context.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); context.HandleResponse(); // 标记事件已处理,阻止后续默认流程 } else { // 设置400状态码 context.Response.StatusCode = StatusCodes.Status400BadRequest; // 可选:返回错误描述 await context.Response.WriteAsync("Invalid logout token"); context.HandleResponse(); // 关键:阻止后续流程,避免返回404 } } else { // 若无logout_token,按规范返回400 context.Response.StatusCode = StatusCodes.Status400BadRequest; await context.Response.WriteAsync("Missing logout token"); context.HandleResponse(); } };
关键要点
- 使用
context.Response.StatusCode直接设置HTTP状态码为400 - 调用
context.HandleResponse()标记事件已处理,防止ASP.NET Core执行后续默认逻辑(这是之前返回404的核心原因) - 可选:通过
context.Response.WriteAsync()返回错误描述,提升响应可读性 - 异步操作需标记方法为
async并使用await,无需返回Task.FromResult类型结果
内容的提问来源于stack exchange,提问作者MilConDoin
相关产品推荐
相关产品推荐

