首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Asp.net Blazor服务器应用无法在具有OIDC的kubernetes中重定向

Asp.net Blazor服务器应用无法在具有OIDC的kubernetes中重定向
EN

Stack Overflow用户
提问于 2021-08-20 14:52:53
回答 1查看 284关注 0票数 0

我们有一个运行在Azure Kubernetes中的.NET 5 (Blazor Server)应用程序,它使用OpenID连接向第三方进行身份验证。这个应用是在Ingress后面运行的。入口使用https。这个应用程序只有http。在我们使用OIDC进行身份验证并被重定向回/signin-oidc之后,我们得到了一个无法解决的.NET错误。

代码语言:javascript
运行
复制
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)

代码语言:javascript
运行
复制
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);
                }
            }
        };
    }
}
EN

回答 1

Stack Overflow用户

发布于 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:

代码语言:javascript
运行
复制
app.Use((context, next) =>
{
    context.Request.Scheme = "https";
    return next();
});

如果微软不支持你的安装,我会很惊讶,因为它是一个相当主流的托管选择。如果这不起作用,那么您可以尝试:

  • 进一步搜索Blazor和‘反向代理托管’
  • 最坏的情况下,您可能不得不在集群内使用SSL来处理此特定组件,正如Johan指出的

更广泛的信息- API驱动的OAUTH

许多公司希望开发单页面应用程序,但使用基于网站的后端来管理OAuth安全性。将web内容服务与OAuth安全相结合增加了复杂性。人们通常不理解,如果以OAuth驱动的方式开发,API安全性会工作得更好。

以下资源显示了如何简化SPA代码,在此示例中,无论配置如何,API都将发出cookies。这将使它能够在集群内通过HTTP侦听(如果需要),但也可以发出安全cookies:

更广泛的信息: COOKIES

建议使用SameSite=strict而不是SameSite=none作为最安全的选项。然而,strict选项有时会出现可用性问题,这可能会导致cookies在重定向或从电子邮件链接导航后被丢弃。

这可能会导致公司将其网络安全降级到较不安全的SameSite选项。当使用API驱动的解决方案时,这些问题不会发生,然后您可以使用最强的SameSite=strict选项。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68864160

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档