ASP.Net Core 3.1部署PCF后SSO认证报错:oauth state缺失或无效
在ASP.Net Core 3.1中实现SSO认证并部署到Pivot Cloud Foundry(PCF)后,出现未处理异常,本地运行正常。异常信息如下:
处理请求时发生未处理异常。
Exception: The oauth state was missing or invalid.
Unknown location
Exception: 处理远程登录时遇到错误。
Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler.HandleRequestAsync()
相关代码如下:
HomeController.cs
#if !LOCALTEST [Authorize] #endif public IActionResult Index() { string user = ""; if (User.Identity.IsAuthenticated) { user = User.Identity.Name; } else { // WindowsIdentity.GetCurrent user = WindowsIdentity.GetCurrent().Name.Substring(WindowsIdentity.GetCurrent().Name.LastIndexOf(@"\") + 1); } TempData["user"] = user; return View(); }
Manifest.yml
--- applications: - name: ApplicationName memory: 1G stack: cflinuxfs3 buildpacks: - dicf_dotnet_core_buildpack_2339_cflinuxfs3 instances: 2 disk_quota: 1G env: ASPNETCORE_ENVIRONMENT: Development GRANT_TYPE: authorization_code SSO_IDENTITY_PROVIDERS : XXX-sso SSO_SCOPES : openid,roles,user_attributes SSO_AUTO_APPROVED_SCOPES : openid,roles,user_attributes SSO_USERINFO_URL : https://appsso.login.sr3.pcf.xxx.com/userinfo services : - serviceName
Startup.cs
public void ConfigureServices(IServiceCollection services) { services.AddSession(); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); services.AddMvc(); services.Configure<MyOptions>(myOptions => { myOptions.ConnString = Configuration.GetConnectionString("DefaultConnection"); }); services .AddMvc() .AddJsonOptions(options => options.JsonSerializerOptions.PropertyNamingPolicy = null); services.AddScoped<IRepository, RepositoryConcrete>(); services.AddControllersWithViews(); services.AddRazorPages(); services.AddCloudFoundryContainerIdentity(Configuration); services.AddAuthentication((options) => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CloudFoundryDefaults.AuthenticationScheme; }) .AddCookie((options) => { options.AccessDeniedPath = new PathString("/Home/AccessDenied"); }) .AddCloudFoundryOAuth(Configuration); services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddControllersWithViews(); var serviceInfos = CloudFoundryServiceInfoCreator.Instance(Configuration); var ssoInfo = serviceInfos.GetServiceInfos<SsoServiceInfo>().FirstOrDefault() ?? throw new NullReferenceException("Service info for an SSO Provider was not found!"); userInfoEndPoint = ssoInfo.AuthDomain + "/userinfo"; } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedProto }); app.UseSession(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); }
1. 解决多实例Session不一致问题
部署了2个实例,默认内存型Session会导致跨实例请求时丢失OAuth State值(State存储在Session中):
- 改用PCF提供的Redis服务作为分布式Session存储:
// 在ConfigureServices中添加 services.AddStackExchangeRedisCache(options => { options.Configuration = Configuration.GetConnectionString("RedisConnection"); }); services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(30); options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; }); - 确保已将Redis服务绑定到应用,连接字符串正确注入配置。
2. 调整Cookie策略配置
当前CheckConsentNeeded = true会阻止非必要Cookie,而OAuth认证依赖的Cookie属于必要范畴:
- 修改CookiePolicyOptions:
services.Configure<CookiePolicyOptions>(options => { options.CheckConsentNeeded = context => false; options.MinimumSameSitePolicy = SameSiteMode.None; options.Secure = CookieSecurePolicy.Always; // PCF强制HTTPS环境下启用 }); - 同步调整Cookie认证选项,确保多实例环境下Cookie可共享:
.AddCookie((options) => { options.AccessDeniedPath = new PathString("/Home/AccessDenied"); options.Cookie.Name = ".AspNetCore.Cookies"; options.Cookie.SecurePolicy = CookieSecurePolicy.Always; options.Cookie.SameSite = SameSiteMode.None; options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; })
3. 修正ForwardedHeaders中间件顺序
当前UseForwardedHeaders在UseRouting之后,会导致认证中间件无法正确获取真实请求协议,影响Cookie的Secure属性:
- 调整中间件顺序至
UseRouting之前:app.UseHttpsRedirection(); app.UseStaticFiles(); // 移至UseRouting之前 app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost }); app.UseRouting(); app.UseSession(); app.UseAuthentication(); app.UseAuthorization();
4. 修正环境配置
Manifest.yml中ASPNETCORE_ENVIRONMENT设为Development,生产环境需改为Production以启用安全配置:
env: ASPNETCORE_ENVIRONMENT: Production
5. 验证SSO服务绑定
确认serviceName对应的SSO服务已正确绑定,且服务实例的回调URL配置为应用的PCF域名(如https://applicationname.sr3.pcf.xxx.com/signin-cloudfoundry),回调URL不匹配会直接导致State验证失败。
6. 清理冗余代码
- Startup.cs中重复调用了
services.AddControllersWithViews(),保留一次即可; - PCF容器环境下不会执行
WindowsIdentity.GetCurrent()逻辑,可保留但无需额外修改。
内容的提问来源于stack exchange,提问作者Amrita Verma

