Asp.net Core微软OIDC库OnAuthorizationCodeReceived事件触发两次问题咨询
问题根因
经过排查,OnAuthorizationCodeReceived 事件触发两次的问题和当前的中间件顺序、异步代码写法、OIDC事件逻辑错误三个问题直接相关,均和反向代理环境特性有关:
- 中间件顺序错误:反向代理透传的
X-Forwarded-*头需要提前解析才能让应用拿到正确的公网域名、协议信息,当前配置把UseForwardedHeaders放在了认证、会话中间件之后,导致回调校验时地址不匹配,第一次处理失败后框架自动触发重试,就会二次调用事件。 - 异步代码阻塞:
OnAuthorizationCodeReceived中混用await和.Result导致同步阻塞,反向代理超时后会自动重试请求,触发第二次事件。 - OIDC跳转逻辑错误:在
OnRedirectToIdentityProvider中清空了所有协议参数,导致内置的state、nonce、correlation校验参数丢失,第一次回调校验失败后框架自动发起二次认证,触发二次回调。
修复方案
1. 调整中间件顺序
将 UseForwardedHeaders 放在所有中间件最前面,同时调整会话、认证的顺序符合ASP.NET Core规范:
// 必须放在最前面,优先解析反向代理透传的地址、协议头 app.UseForwardedHeaders(); app.UseHttpsRedirection(); app.UseStaticFiles(); //app.UseAntiXssMiddleware(); app.UseCookiePolicy(); app.UseRouting(); // 会话放在认证之前 app.UseSession(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); });
2. 修正OnAuthorizationCodeReceived异步写法
移除阻塞调用 .Result,改用纯异步写法,异常时主动终止后续默认处理逻辑:
OnAuthorizationCodeReceived = async authorizationCtx => { try { BellLogger.WriteLog("OnAuthorizationCodeReceived start:" + authorizationCtx.TokenEndpointRequest.Code, Framework.Common.LogType.Info); HttpClient httpClient = new HttpClient(); TokenClientOptions tokenClientOptions = new TokenClientOptions() { ClientId = clientId, ClientSecret = clientSecret, Address = string.Format("{0}/v1/token", _authorityAPI) }; var tokenClient = new TokenClient(httpClient, tokenClientOptions); // 移除.Result,用异步等待避免阻塞 var tokenResponse = await tokenClient.RequestAuthorizationCodeTokenAsync(authorizationCtx.TokenEndpointRequest.Code, authorizationCtx.TokenEndpointRequest.RedirectUri); authorizationCtx.HandleCodeRedemption(tokenResponse.AccessToken, tokenResponse.IdentityToken); } catch (Exception ex) { Logger.WriteLog(ex, Framework.Common.LogType.Error); // 异常时终止后续默认处理,避免框架重试 authorizationCtx.HandleResponse(); authorizationCtx.Response.StatusCode = 500; } }
3. 修复OIDC跳转逻辑
不要清空所有协议参数,避免内置校验参数丢失:
OnRedirectToIdentityProvider = (context) => { if (context.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication) { // 注释清空参数的逻辑,保留state、nonce等内置校验参数 // context.ProtocolMessage.Parameters.Clear(); context.ProtocolMessage.IssuerAddress = centralLogin + "?RedirectURL=https://" + GetRequestHostName(context, Configuration); return Task.CompletedTask; } return Task.CompletedTask; }
4. 补全CookiePolicy的SameSite处理逻辑
已定义的CheckSameSite方法没有注册到CookiePolicy中,补充注册避免部分浏览器Cookie拦截导致的重试:
services.Configure<CookiePolicyOptions>(options => { options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; options.Secure = CookieSecurePolicy.Always; options.HandleSameSiteCookieCompatibility(); // 注册自定义SameSite处理逻辑 options.OnAppendCookie = cookieContext => CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); options.OnDeleteCookie = cookieContext => CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); });
内容的提问来源于stack exchange,提问作者Jith123
相关产品推荐
相关产品推荐

