.NET 5 Blazor 服务器 OKTA 身份验证显示 HTTP 错误 400

Posted

技术标签:

【中文标题】.NET 5 Blazor 服务器 OKTA 身份验证显示 HTTP 错误 400【英文标题】:.NET 5 Blazor Server OKTA Authentication showing HTTP Error 400 【发布时间】:2022-01-21 10:36:25 【问题描述】:

将 ASP.NET Core (.NET 5) Blazor 服务器与 OKTA 结合使用。 OKTA 日志页面已提示。我在提交 OKTA uid/pwd 时收到错误消息

HTTP Error 400. The size of the request headers is too long.

我的中间件如下所示,使用 OpenId Connect。

services.AddAuthentication(options =>
                           
                                  options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                                  options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
                                  options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                           )
                           .AddOpenIdConnect(options =>
                           
                                  options.RemoteAuthenticationTimeout = TimeSpan.FromMinutes(30);
                                  options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;
                                  options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                                  options.Authority = configuration["Okta:Domain"] + "/oauth2/default";
                                  options.RequireHttpsMetadata = true;
                                  options.ClientId = configuration["Okta:ClientId"];
                                  options.ClientSecret = configuration["Okta:ClientSecret"];
                                  options.ResponseMode = OpenIdConnectResponseMode.FormPost;
                                  options.ResponseType = OpenIdConnectResponseType.Code;
                                  options.Scope.Add("offline_access");
                                  options.UseTokenLifetime = true;
                                  options.GetClaimsFromUserInfoEndpoint = true;
                                  options.AccessDeniedPath = "/Public/AccessDenied";
                                  options.Scope.Add("openid");
                                  options.Scope.Add("profile");
                                  options.Scope.Add("email");

                                  // Describe how to map the user info we receive to user claims
                                  options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub", "string");
                                  options.ClaimActions.MapJsonKey(ClaimTypes.GivenName, "given_name", "string");
                                  options.ClaimActions.MapJsonKey(ClaimTypes.Name, "given_name", "string");
                                  options.ClaimActions.MapJsonKey("LastName", "lastname", "string");
                                  options.ClaimActions.MapJsonKey("FirstName", "firstname", "string");
                                  options.ClaimActions.MapJsonKey(ClaimTypes.Email, "email", "string");
                                  options.ClaimActions.MapJsonKey("Groups", "Groups", "string");
                                  options.ClaimActions.MapJsonKey("membership_roles", "membership_roles", "string");

                                  options.SaveTokens = true;
                                  options.NonceCookie.SameSite = SameSiteMode.Unspecified;
                                  options.CorrelationCookie.SameSite = SameSiteMode.Unspecified;

                                  options.TokenValidationParameters = new TokenValidationParameters
                                  
                                         NameClaimType = "name",
                                         RoleClaimType = "groups",
                                         RequireSignedTokens = true,
                                         ValidateIssuer = false
                                  ;
                           )
                           .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, displayName: $"EPDOne_GlobalVariables.LocalEnv.EnvironmentName",
                        options =>
                        

                              options.Cookie.Name = $"EPDOne_ GlobalVariables.LocalEnv.EnvironmentName";
                              options.Cookie.HttpOnly = false;
                               options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
                              options.Cookie.IsEssential = true;


                              options.Events = new CookieAuthenticationEvents
                              
                                     // this event is fired everytime the cookie has been validated by the cookie middleware,
                                     // so basically during every authenticated request
                                     // the decryption of the cookie has already happened so we have access to the user claims
                                     // and cookie properties - expiration, etc..
                                     OnValidatePrincipal = context =>
                                     
                                            // since our cookie lifetime is based on the access token one,
                                            // check if we're more than halfway of the cookie lifetime

                                            var now = DateTimeOffset.UtcNow;
                                            TimeSpan timeElapsed = now.Subtract(DateTime.Now.AddDays(1));
                                            TimeSpan timeRemaining = now.Subtract(DateTime.Now.AddDays(2));
                                            if (context is not null)
                                            
                                                   if (context.Properties is not null && context.Properties.IssuedUtc is not null)
                                                   
                                                          timeElapsed = now.Subtract(context.Properties.IssuedUtc.Value);
                                                   
                                                   else
                                                   
                                                          context.ShouldRenew = true;
                                                   
                                                   if (context.Properties is not null && context.Properties.ExpiresUtc is not null)
                                                   
                                                          timeRemaining = context.Properties.ExpiresUtc.Value.Subtract(now);
                                                   
                                                   else
                                                   
                                                          context.ShouldRenew = true;
                                                   
                                            


                                            if (timeElapsed > timeRemaining || context?.ShouldRenew == true)
                                            
                                                   context.ShouldRenew = true;

                                                   var identity = (ClaimsIdentity)context?.Principal?.Identity;
                                                   if (identity is not null && identity.IsAuthenticated)
                                                   
                                                          string CurrentBaseAddress = CurrentURL(context.HttpContext);
                                                          string returnUrl = "";
                                                          if (string.IsNullOrEmpty(CurrentBaseAddress) == false)
                                                          
                                                                 returnUrl = "?returnUrl=" + CurrentBaseAddress;
                                                          
                                                          context.Response.Redirect(GlobalVariables.OKTACallBackURI + $"/refreshreturnUrl");
                                                   
                                            

                                            return Task.CompletedTask;
                                     
                              ;
                        );


                           services.AddRazorPages(options =>
                           
                                  options.Conventions.AuthorizeFolder("/");
                                  //options.Conventions.AllowAnonymousToFolder("/Public");
                           
                           );

正如您在上面看到的,我在 Startup.cs 中使用了 OpenId,应用程序通过 OKTA 凭据对话框进行提示,在提交 uid/pwd 后,页面的行为就像在循环中,然后显示 HTTP 错误 400 消息。这里有什么线索吗?

【问题讨论】:

您是否有从浏览器收集的网络流量(HAR 文件),以查看导致 400 的原因或发生循环的位置? 是的。我在这里应付了1drv.ms/u/s!ArnWsPocPHeKhZgv_lNQbeSTpJfviA?e=OAWU50。我在另一个简单的应用程序中本地使用了相同的本地主机配置,它可以工作。 从我在 HAR 中看到的情况来看,当调用 /authorize 时,要求重定向为 /signin-oidc。因此,当您使用代码重定向到那里时,/signin-oidc 不会正确处理它(似乎)并将您发送回/authorize。在这一点上,我可能没用了,因为必须有人可以根据您收集的信息为您建议 .net 应用程序配置更改 【参考方案1】:

Philipp Grigoryev - 感谢您的宝贵时间。我后来在我的 .NET Core Startup.cs 文件中发现了下面的代码。

app.UseAuthorization();

app.UseAuthorization();

正确的行应该是

app.UseAuthentication();
app.UseAuthorization();

所以实际上身份验证中间件本身没有启用,我匆忙使用了两行启用授权本身。在提到的更正之后,它可以工作。对不起任何花时间在这上面的人。我正在关闭此查询。

【讨论】:

以上是关于.NET 5 Blazor 服务器 OKTA 身份验证显示 HTTP 错误 400的主要内容,如果未能解决你的问题,请参考以下文章

Microsoft Graph API - Blazor Server App .NET 5.0 使用 Azure 身份验证,MsalUiRequiredException 因用户质询而引发

从 ASP.NET/Blazor 服务器中的当前经过身份验证的用户获取数据

在 Blazor 中同时使用 ASP.Net Core Identity 和 Azure 身份验证

使用 OKTA 的 WSO2 最终用户身份验证

如何使用我的数据库创建身份验证服务器 OKTA?

在 JAVA 中通过身份验证后如何获取 okta 用户详细信息/当前会话