升级Microsoft.Owin.Security后OpenID Connect认证报IDX21323错误
问题背景
维护基于.NET Framework 4.6.1开发的应用时,将Microsoft.Owin.Security相关NuGet包从3.0.1版本升级至4.2.2版本后,认证功能出现异常:请求直接进入AuthenticationFailed回调分支,无法正常访问控制器方法,初期无明确排查方向。
原始Startup认证配置
public void Configuration(IAppBuilder app) { //User authentication setup app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType); app.UseCookieAuthentication(new CookieAuthenticationOptions()); app.UseOpenIdConnectAuthentication( new OpenIdConnectAuthenticationOptions { ClientId = clientId, Authority = $"{aadInstance}{tenant}", PostLogoutRedirectUri = postLogoutRedirectUri, TokenValidationParameters = new TokenValidationParameters { RoleClaimType = "roles", }, Notifications = new OpenIdConnectAuthenticationNotifications { AuthenticationFailed = context => { context.HandleResponse(); context.Response.Redirect("/Error"); return Task.FromResult(0); } } }); }
已定位到的异常信息
升级后捕获到明确异常:
IDX21323: RequireNonce is '[PII is hidden]'. OpenIdConnectProtocolValidationContext.Nonce was null, OpenIdConnectProtocol.ValidatedIdToken.Payload.Nonce was not null. The nonce cannot be validated. If you don't need to check the nonce, set OpenIdConnectProtocolValidator.RequireNonce to 'false'. Note if a 'nonce' is found it will be evaluated.
尝试社区流传的针对该错误的修复代码后,触发了无限循环问题,参考代码如下:
Notifications = new OpenIdConnectAuthenticationNotifications { AuthenticationFailed = context => { if (context.Exception.Message.Contains("IDX21323")) { context.HandleResponse(); context.OwinContext.Authentication.Challenge(); } return Task.FromResult(true); } }
根因与修复方案
这个异常的核心原因是:4.x版本的Microsoft.Owin.Security.OpenIdConnect中间件调整了nonce校验逻辑,nonce值默认存储在Cookie中,回调时如果读不到对应Cookie就会抛出该错误,网上直接触发Challenge的方案会导致「验证失败->发起登录挑战->回调时仍读不到nonce->再次验证失败」的死循环。
按以下优先级排查修复即可:
- 补全Cookie认证配置,不要使用空的
CookieAuthenticationOptions实例
3.x版本默认的Cookie配置可以适配大部分场景,4.2.2版本必须显式指定Cookie相关属性,否则会出现nonce Cookie写入失败、被浏览器拦截的问题:app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = CookieAuthenticationDefaults.AuthenticationType, CookieSameSite = SameSiteMode.Lax, CookieSecure = CookieSecureOption.SameAsRequest, CookieHttpOnly = true, SlidingExpiration = true }); - 校验IIS与web.config的Cookie规则是否冲突
如果站点部署在IIS上,检查web.config中system.web节点的Cookie配置,不要全局设置和Owin中间件冲突的SameSite、HttpOnly规则,否则托管管道会拦截Owin中间件写入的nonce Cookie。排查时可以在AuthenticationFailed回调中打印context.OwinContext.Request.Cookies集合,确认是否存在OpenIdConnect.nonce.前缀的Cookie,如果不存在即可确定是Cookie写入/读取被拦截。 - 修正AuthenticationFailed的处理逻辑,避免死循环
不要在捕获到IDX21323错误时直接调用Challenge(),先跳过本次异常,重定向到站点根路径让中间件重新判断认证状态即可:AuthenticationFailed = context => { if (context.Exception.Message.Contains("IDX21323")) { context.HandleResponse(); // 不要直接调Challenge,重定向到首页让中间件自动处理认证状态判断 context.Response.Redirect("/"); return Task.FromResult(0); } // 其他异常走原有错误页逻辑 context.HandleResponse(); context.Response.Redirect("/Error"); return Task.FromResult(0); } - 非必要不要关闭Nonce校验
OpenIdConnectProtocolValidator.RequireNonce = false属于下下策,关闭后会存在CSRF攻击风险,优先通过前面的Cookie配置解决问题。如果是内网测试环境临时验证,可以临时开启该配置确认问题范围,生产环境必须保持Nonce校验开启。
内容的提问来源于stack exchange,提问作者johnway2

