我们有一个运行在Azure Kubernetes中的.NET 5 (Blazor Server)应用程序,它使用OpenID连接向第三方进行身份验证。这个应用是在Ingress后面运行的。入口使用https。这个应用程序只有http。在我们使用OIDC进行身份验证并被重定向回/signin-oidc之后,我们得到了一个无法解决的.NET错误。
warn: Microsoft.AspNetCore.Http.ResponseCookies[1]
The cookie '.AspNetCore.OpenIdConnect.Nonce.CfDJ8EYehvsxFBVNtGDsitGDhE8K9FHQZVQwqqr1YO-zVntEtRgpfb_0cHpxfZp77AdGnS35iGRKYV54DTgx2O6ZO_3gq98pbP_XcbHnJmBDtZg2g5hhPakTrRirxDb-Qab0diaLMFKdmDrNTqGkVmqiGWpQkSxcnmxzVGGE0Cg_l930hk6TYgU0qmkzSO9WS16UBOYiub32GF4I9_qPwIiYlCq5dMTtUJaMxGlo8AdAqknxTzYz4UsrrPBi_RiWUKaF6heQitbOD4V-auHmdXQm4LE' has set 'SameSite=None' and must also set 'Secure'.
warn: Microsoft.AspNetCore.Http.ResponseCookies[1]
The cookie '.AspNetCore.Correlation.MMrYZ2WKyYiV4hMC6bhQbGZozpubcF2tYsKq748YH44' has set 'SameSite=None' and must also set 'Secure'.
warn: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[15]
'.AspNetCore.Correlation.MMrYZ2WKyYiV4hMC6bhQbGZozpubcF2tYsKq748YH44' cookie not found.
fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[1]
An unhandled exception has occurred while executing the request.
System.Exception: An error was encountered while handling the remote login.
---> System.Exception: Correlation failed.
--- End of inner exception stack trace ---
at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.HandleRequestAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
public class Startup
{
private static readonly object refreshLock = new object();
private IConfiguration Configuration;
private readonly IWebHostEnvironment Env;
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
Console.WriteLine($"LogQAApp Version: {Assembly.GetExecutingAssembly().GetName().Version}");
// We apparently need to set a CultureInfo or some of the Razor pages dealing with DateTimes, like LogErrorCountByTime fails with JavaScript errors.
// I wanted to set it to CultureInvariant, but that wouldn't take. Didn't complain, but wouldn't actually set it.
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US");
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("en-US");
Configuration = configuration;
Env = env;
}
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); // Needed for 1252 code page encoding.
Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("");
services.AddSignalR(e =>
{
e.MaximumReceiveMessageSize = 102400000;
});
services.AddBlazoredSessionStorage();
services.AddCors();
services.AddSyncfusionBlazor();
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddHttpContextAccessor();
ServiceConfigurations.LoadFromConfiguration(Configuration);
#region Authentication
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(
options =>
{
options.Events = GetCookieAuthenticationEvents();
}
)
.AddOpenIdConnect("SlbOIDC", options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = Configuration["SlbOIDC:Authority"];
if (Env.IsDevelopment())
{
options.ClientId = Configuration["SlbOIDC:ClientID"];
options.ClientSecret = Configuration["SlbOIDC:ClientSecret"];
}
else
{
options.ClientId = Configuration.GetValue<string>("slbclientid");
options.ClientSecret = Configuration.GetValue<string>("slbclientsecret");
}
options.ResponseType = OpenIdConnectResponseType.Code;
options.UsePkce = true;
options.SaveTokens = true;
options.ClaimsIssuer = "SlbOIDC";
// Azure is communicating to us over http, but we need to tell SLB to respond back to us on https.
options.Events = new OpenIdConnectEvents()
{
OnRedirectToIdentityProvider = context =>
{
Console.WriteLine($"Before: {context.ProtocolMessage.RedirectUri}");
context.ProtocolMessage.RedirectUri = context.ProtocolMessage.RedirectUri.Replace("http://", "https://");
Console.WriteLine($"After: {context.ProtocolMessage.RedirectUri}");
return Task.FromResult(0);
}
};
});
services.AddSession(options =>
{
options.Cookie.SameSite = SameSiteMode.None;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.IsEssential = true;
});
#endregion
services.AddScoped<BrowserService>();
services.AddSingleton<ConcurrentSessionStatesSingleton>();
services.AddSingleton<URLConfiguration>();
services.AddScoped<CircuitHandler>((sp) => new CircuitHandlerScoped(sp.GetRequiredService<ConcurrentSessionStatesSingleton>(), sp.GetRequiredService<BrowserService>(), sp.GetRequiredService<IJSRuntime>()));
services.AddScoped<SessionServiceScoped>();
services.AddScoped<LogEditorScoped>();
services.AddSingleton<ModalService>();
services.AddFlexor();
services.AddScoped<ResizeListener>();
services.AddScoped<ApplicationLogSingleton>();
services.AddScoped<LogChartsSingleton>();
services.AddScoped<CurveNameClassificationSingleton>();
services.AddScoped<HubClientSingleton>();
services.AddScoped((sp) => new LogAquisitionScopedService(
sp.GetRequiredService<URLConfiguration>(),
sp.GetRequiredService<HubClientSingleton>(),
sp.GetRequiredService<ApplicationLogSingleton>(),
sp.GetRequiredService<IConfiguration>(),
sp.GetRequiredService<SessionServiceScoped>(),
sp.GetRequiredService<AuthenticationStateProvider>(),
sp.GetRequiredService<IHttpContextAccessor>(),
sp.GetRequiredService<IJSRuntime>()
)
);
services.AddScoped<UnitSingleton>();
services.AddServerSideBlazor().AddCircuitOptions(options => { options.DetailedErrors = true; });
services.AddScoped<TimeZoneService>();
services.AddHostedService<ExcelBackgroundService>();
services.AddHostedService<LogEditorBackgroundService>();
}
// 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("/Error");
//app.UseHsts();
}
//app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseCookiePolicy();
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
app.UseAuthentication();
if (!Env.IsDevelopment())
{
app.UseTrafficManager();
}
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
private CookieAuthenticationEvents GetCookieAuthenticationEvents()
{
return new CookieAuthenticationEvents()
{
OnValidatePrincipal = context =>
{
lock (refreshLock)
{
if (context.Properties.Items.ContainsKey(".Token.expires_at"))
{
DateTime expire = DateTime.Parse(context.Properties.Items[".Token.expires_at"]);
if (expire.AddMinutes(-20) < DateTime.Now)
{
try
{
CloudAuthentication cloudAuthentication = new CloudAuthentication();
TokenResponse tokenResponse = cloudAuthentication.GetRefreshToken(context.Properties.Items[".Token.refresh_token"]);
context.Properties.Items[".Token.access_token"] = tokenResponse.access_token;
context.Properties.Items[".Token.refresh_token"] = tokenResponse.refresh_token;
context.Properties.Items[".Token.expires_at"] = DateTime.Now.AddSeconds(tokenResponse.expires_in).ToString();
context.ShouldRenew = true;
}
catch (Exception ex)
{
context.RejectPrincipal();
}
}
}
return Task.FromResult(0);
}
}
};
}
}
发布于 2021-08-22 10:18:31
这是一个很好的问题-这里有几个有趣的点,我已经扩展了,因为它们与SameSite cookies相关。
反向代理设置
默认情况下,如果使用需要requires you to run on HTTPS连接的cookie,则为Microsoft堆栈SSL。但是,您通过Kubernetes入口提供SSL,Kubernetes入口是反向代理的一种形式。
Microsoft .Net Core Reverse Proxy Docs可以提供一种解决方案。该文档建议您可以通知运行时存在SSL上下文,即使您正在侦听HTTP:
app.Use((context, next) =>
{
context.Request.Scheme = "https";
return next();
});
如果微软不支持你的安装,我会很惊讶,因为它是一个相当主流的托管选择。如果这不起作用,那么您可以尝试:
更广泛的信息- API驱动的OAUTH
许多公司希望开发单页面应用程序,但使用基于网站的后端来管理OAuth安全性。将web内容服务与OAuth安全相结合增加了复杂性。人们通常不理解,如果以OAuth驱动的方式开发,API安全性会工作得更好。
以下资源显示了如何简化SPA代码,在此示例中,无论配置如何,API都将发出cookies。这将使它能够在集群内通过HTTP侦听(如果需要),但也可以发出安全cookies:
更广泛的信息: COOKIES
建议使用SameSite=strict
而不是SameSite=none
作为最安全的选项。然而,strict选项有时会出现可用性问题,这可能会导致cookies在重定向或从电子邮件链接导航后被丢弃。
这可能会导致公司将其网络安全降级到较不安全的SameSite选项。当使用API驱动的解决方案时,这些问题不会发生,然后您可以使用最强的SameSite=strict
选项。
https://stackoverflow.com/questions/68864160
复制相似问题