首页 > 解决方案 > signin-oidc 重定向时关联失败

问题描述

在 NGINX 负载均衡器后面的虚拟机上托管时,尝试对 .NET Core 3.1 Web 应用程序进行身份验证时遇到以下问题(本地它按预期工作,我目前在负载均衡器中只有一个虚拟机):

例外:关联失败。位置不明

异常:处理远程登录时遇到错误。Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler.HandleRequestAsync()

我做了很多研究并尝试了十几个修复程序,但似乎没有一个有效。以下是我的代码片段:

启动.Configure()

        // Initialize the ping options and get default values from the config
        var pingOptions = Configuration.GetSection("PingOAuthWebOptions").Get<PingOAuthWebOptions>();

        // Testing Stack OVerflow Example
        services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = "oidc";
        })
            .AddCookie(options =>
            {
                options.ExpireTimeSpan = TimeSpan.FromMinutes(10000);
                options.Cookie.Name = "PINGSESSION";
            })
            .AddOpenIdConnect("oidc", options =>
            {
                options.Authority = pingOptions.Authority;
                options.ClientId = pingOptions.ClientId;
                options.ClientSecret = pingOptions.ClientSecret;
                options.ResponseType = OpenIdConnectResponseType.Code;
                options.GetClaimsFromUserInfoEndpoint = true;
                options.Scope.Add("openid"); // Added this
                options.SaveTokens = true;
                options.Events = new OpenIdConnectEvents()
                {
                    OnRedirectToIdentityProvider = async ctx =>
                    {
                        // Change from "HTTP" to "HTTPS" if requested and necessary
                        if (pingOptions.ForceSecureRedirect && ctx.ProtocolMessage.RedirectUri.StartsWith("http:"))
                        {
                            ctx.ProtocolMessage.RedirectUri = ctx.ProtocolMessage.RedirectUri.Replace("http:", "https:");
                        }

                        await Task.FromResult(0);
                    },
                    OnUserInformationReceived = ctx =>
                    {
                        var accessToken = ctx.ProtocolMessage.AccessToken;
                        var jwt = new JwtSecurityTokenHandler().ReadJwtToken(accessToken);
                        var appIdentity = new ClaimsIdentity(jwt.Claims);

                        // Add app role
                        appIdentity.AddClaim(new Claim(ClaimTypes.Role, "Superuser"));

                        ctx.Principal.AddIdentity(appIdentity);
                        return Task.CompletedTask;
                    },
                };
            });

启动.ConfigureServices()

        if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }

            var forwardedHeaderOptions = new ForwardedHeadersOptions
            {
                ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
            };
            forwardedHeaderOptions.KnownNetworks.Clear();
            forwardedHeaderOptions.KnownProxies.Clear();
            app.UseForwardedHeaders(forwardedHeaderOptions);

            app.UseStaticFiles();
            app.UseRouting();
            app.UseAuthentication();
            app.UseHttpsRedirection();
            app.UseSession();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });

最后,我尝试过的一些事情:

services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
       ForwardedHeaders.XForwardedFor |
       ForwardedHeaders.XForwardedHost |
       ForwardedHeaders.XForwardedProto;
    options.ForwardLimit = 2;  //Limit number of proxy hops trusted
    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();
});

app.UseHttpsRedirection();

CookiePolicyOptions cookiePolicy = new CookiePolicyOptions() { Secure = CookieSecurePolicy.Always, };

redirectContext.ProtocolMessage.State = options.StateDataFormat.Protect(redirectContext.Properties);

app.Use((context, next) => { context.Request.Scheme = "https"; return next(); });

标签: c#oauthcorrelationasp.net-core-3.1

解决方案


更新:在使用 OP 进行一些测试后,似乎 auth cookie 因大小而被 NGINX 阻止。通过更改 NGINX 配置解决了问题: https ://unix.stackexchange.com/a/605614

proxy_buffers         8 16k;  # Buffer pool = 8 buffers of 16k
proxy_buffer_size     16k;    # 16k of buffers from pool used for headers

您能否检查一下您是否能够在开发人员中运行以下配置?在 NGINX 后面测试实例之前,请确保您已向 OpenId Connect 提供程序注册该 URL。

还要检查 X-Forwarded-For 和 X-Forwarded-Proto 是否已传递到您的应用程序。在服务器更改后,我遇到过几次问题。

我建议使用干净的应用程序对此进行测试,作为概念验证 (POC)。当 POC 在所有环境中启动并运行时,您可以将更改应用到现有代码库。

在 Startup.ConfigureServices

            var openIdConnectSettings = new OpenIdConnectSettings();
            Configuration.GetSection("OpenIdConnect").Bind(openIdConnectSettings);

            services.AddAuthentication(options =>
                {
                    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
                })
                .AddCookie()
                .AddOpenIdConnect(options =>
                {
                    options.Authority = openIdConnectSettings.Authority;
                    options.ClientId = openIdConnectSettings.ClientId;
                    options.ClientSecret = openIdConnectSettings.ClientSecret;
                    options.ResponseType = OpenIdConnectResponseType.Code;
                    options.GetClaimsFromUserInfoEndpoint = true;
                    options.Scope.Add("openid");
                    options.SaveTokens = true;
                });

在 Startup.Configure

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseCookiePolicy();
        }
        else
        {
            app.UseStatusCodePagesWithReExecute( ...

            // required in order to get https for OpenIdConnect
            // must come before app.UseAuthentication();
            var forwardedHeaderOptions = new ForwardedHeadersOptions
            {
                ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
            };
            forwardedHeaderOptions.KnownNetworks.Clear();
            forwardedHeaderOptions.KnownProxies.Clear();
            app.UseForwardedHeaders(forwardedHeaderOptions);
        }

        app.UseStaticFiles();
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();

        ....

配置类

    public class OpenIdConnectSettings
    {
        public string Authority { get; set; }
        public string ClientId { get; set; }
        public string ClientSecret { get; set; }
    }

推荐阅读