【问题标题】:OpenIdConnectAuthentication cookie is never issued从未发出 OpenIdConnect 身份验证 cookie
【发布时间】:2020-07-06 12:06:18
【问题描述】:

我们有一个使用 ASP.NET Membership 进行用户身份验证的 .net MVC Web 应用程序。我们还为我们的移动应用程序发行不记名令牌。我正在尝试以 okta 为例来添加对 SSO 的支持。我在 Okta 中创建了一个应用程序,并正在尝试登录我们的应用程序。我目前的流程如下:

  1. 打开登录页面
  2. 点击“使用 okta 登录”
  3. 我们的服务器收到请求。用户未通过身份验证,因此发出了质询
  4. 用户被重定向到 Okta 的登录页面
  5. 成功登录后,会向登录重定向 url 发出请求(我想它是由 OpenIdConnect 中间件捕获的?)
  6. AuthorizationCodeReceived 事件被触发。
  7. 重定向完成,但未发出 cookie
  8. 用户被重定向到登录页面,但由于他们没有经过身份验证,它会重定向到 Okta 进行登录。循环回到第 4 步并重复。

似乎在登录重定向并且我设置了正确的声明之后,结果应该是应该发出一个 cookie 并在响应中返回以表示用户已登录。我不确定我在做什么在这里失踪,但如果有人能指出我正确的方向,或解释正在发生的事情,我将非常感激!

下面是我们 Startup.cs 中的一个 sn-p:

public void ConfigureAuth(IAppBuilder app, bool allowInsecureHttp = false)
{
    // Configure the db context, user manager and signin manager to use a single instance per request
    app.CreatePerOwinContext(OwinIdentityDbContext.Create);
    app.CreatePerOwinContext<IdentityUserManager>(IdentityUserManager.Create);
    app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

    app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
    {
        ClientId = GlobalConstants.Settings.OktaClientId,
        ClientSecret = GlobalConstants.Settings.OktaClientSecret,
        Authority = GlobalConstants.Settings.OktaDomain,
        AuthenticationType = "okta", // This is basically a "name" for this open id configuration
        RedirectUri = GlobalConstants.Settings.OktaRedirectUri,
        ResponseType = OpenIdConnectResponseType.CodeIdToken,
        Scope = $"{OpenIdConnectScope.OpenIdProfile} {OpenIdConnectScope.Email}", // Get the profile and email
        PostLogoutRedirectUri = GlobalConstants.Settings.OktaPostLogoutRedirectUri,
        TokenValidationParameters = new TokenValidationParameters
        {
            NameClaimType = "name"
        },

        Notifications = new OpenIdConnectAuthenticationNotifications
        {
            AuthorizationCodeReceived = async n =>
            {
                // Exchange code for access and ID tokens
                var tokenClient = new TokenClient(GlobalConstants.Settings.OktaDomain + "/v1/token", GlobalConstants.Settings.OktaClientId, GlobalConstants.Settings.OktaClientSecret);
                var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(n.Code, GlobalConstants.Settings.OktaRedirectUri);

                if (tokenResponse.IsError)
                {
                    throw new Exception(tokenResponse.Error);
                }

                var userInfoClient = new UserInfoClient(GlobalConstants.Settings.OktaDomain + "/v1/userinfo");
                var userInfoResponse = await userInfoClient.GetAsync(tokenResponse.AccessToken);
                var claims = new List<Claim>();
                claims.AddRange(userInfoResponse.Claims);
                claims.Add(new Claim("id_token", tokenResponse.IdentityToken));
                claims.Add(new Claim("access_token", tokenResponse.AccessToken));

                if (!string.IsNullOrEmpty(tokenResponse.RefreshToken))
                {
                    claims.Add(new Claim("refresh_token", tokenResponse.RefreshToken));
                }

                // Add custom user claims here
                #region Add custom claims

                var email = claims.Find(_ => _.Type == "email")?.Value;

                claims.Add(new Claim(ClaimTypes.Name, email));
                claims.Add(new Claim(ClaimTypes.NameIdentifier, email));

                // Hardcoded for now. Discover these for real later
                claims.Add(new Claim(CustomClaimTypes.OurUserId, "{userId}"));
                claims.Add(new Claim(CustomClaimTypes.OurOrganizationId, "{orgId}"));

                #endregion

                // https://stackoverflow.com/questions/55797063/asp-net-owin-openid-connect-not-creating-user-authentication
                var identity = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie, ClaimTypes.Name, ClaimTypes.Role);
                n.AuthenticationTicket = new AuthenticationTicket(identity, n.AuthenticationTicket.Properties);
                n.AuthenticationTicket.Identity.AddClaims(claims);

                return;
            },

            RedirectToIdentityProvider = n =>
            {
                // If signing out, add the id_token_hint
                if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
                {
                    var idTokenClaim = n.OwinContext.Authentication.User.FindFirst("id_token");

                    if (idTokenClaim != null)
                    {
                        n.ProtocolMessage.IdTokenHint = idTokenClaim.Value;
                    }
                }

                return Task.CompletedTask;
            }
        },
    });

    app.UseCookieAuthentication(
        new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            CookieManager = new SystemWebCookieManager(),
            LoginPath = new PathString("/"),
            SlidingExpiration = true,
            CookieSecure = GlobalConstants.Settings.IsEnvironmentDev ? CookieSecureOption.SameAsRequest : CookieSecureOption.Always,
            Provider = new CookieAuthenticationProvider
            {
                OnApplyRedirect = context =>
                {
                    if (IsClientPortalRequest(context.Request))
                    {
                        context.Response.Redirect("/public/ClientPortalLogin");
                    }
                    else if (!IsAjaxRequest(context.Request) && !IsPublicApiRequest(context.Request) && !IsWopiRequest(context.Request))
                    {
                        context.Response.Redirect(context.RedirectUri);
                    }
                }
            }
        });

    // For Two Factor Log In
    app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(10));

    OAuthOptions = new OAuthAuthorizationServerOptions
    {
        AllowInsecureHttp = allowInsecureHttp, // NOTE: Only for unit tests
        TokenEndpointPath = new PathString("/token"),
        AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
        Provider = new CustomBearerTokenProvider()
    };

    // Configure Bearer Token Generation
    app.UseOAuthAuthorizationServer(OAuthOptions);

    // Needed to Authenticate Bearer Tokens
    app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

    // Set Cookies are the default type
    app.SetDefaultSignInAsAuthenticationType(DefaultAuthenticationTypes.ApplicationCookie);
}

这是我们的 okta 应用程序配置的屏幕截图:

【问题讨论】:

  • 服务器是否正在创建身份验证cookie并保存在浏览器中?可以在浏览器中使用开发者工具查看吗?
  • 当我检查响应的 cookie 时,有一个名为 "OpenIdConnect.nonce.lmY6ycbG52f%2BQeGNuvsb32vwVZnKZSPVof0xJ7VE%2FgY%3D" 的厨师,这是请求的 cookie 的名称,值为空白
  • @chronolinq 你解决过这个问题吗?我正在更新旧版 ASPNET MVC 5 应用程序以使用 OpenIdConnect 并具有完全相同的症状 - 身份验证有效,但它重定向到没有设置 ApplicationCookie 的 Home 控制器,因此重定向回 Idp 登录页面,该页面立即进行身份验证,重定向回 Home等等等等 - 我不知道为什么没有设置 ApplicationCookie,OpenIdConnect.nonce 和 OpenIdConnect.nv cookie 在身份验证过程中设置并在来自 /signin-oidc 的 Home 重定向中清除(OpenIdConnect 中间件必须处理) - 热衷于收到你的来信:)

标签: .net asp.net-mvc owin openid-connect okta


【解决方案1】:

我遇到了同样的问题,这就是我发现的。问题不在我的接线代码中,它实际上在我的控制器类中。我的控制器操作上有一个属性,要求用户担任某些角色。这最终导致了我的重定向循环。

如果你有这种情况,你需要摆脱它并找到一种不同的方式来处理角色。

这显示了有问题的代码: Circled code is what I removed.

这里是这篇文章的链接,让我有我的“啊哈”时刻:

https://www.blinkingcaret.com/2016/01/20/authorization-redirect-loops-asp-net-mvc/

希望这对某人有所帮助,我花了很长时间才弄清楚这一点。

【讨论】:

    猜你喜欢
    • 2017-10-22
    • 1970-01-01
    • 2020-07-22
    • 1970-01-01
    • 2016-09-20
    • 2015-04-30
    • 2019-01-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多