首页 > 解决方案 > CodeVerification Cookie 在 Edge 和 Chrome 中消失

问题描述

.NET 框架 4.6.1 网站使用 OIDC 身份验证 (Microsoft.Owin.Security.OpenIdConnect 4.1.0) 作为身份验证的一部分,我包括“code_challenge”。以下代码基于此示例

RedirectToIdentityProvider = n =>
                {
                    //ProcessCertificateValidation();
                    if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
                    {
                        if (AppSettingsKey.AuthCodeChallangeEnabled.Enabled)
                        {
                            // generate code verifier and code challenge
                            var codeVerifier = CryptoRandom.CreateUniqueId(32);

                            string codeChallenge;
                            using (var sha256 = SHA256.Create())
                            {
                                var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
                                codeChallenge = Base64UrlEncoder.Encode(challengeBytes);
                            }

                            // set code_challenge parameter on authorization request
                            n.ProtocolMessage.Parameters.Add("code_challenge", codeChallenge);
                            n.ProtocolMessage.Parameters.Add("code_challenge_method", "S256");

                            // remember code verifier in cookie (adapted from OWIN nonce cookie)
                            RememberCodeVerifier(n, codeVerifier);
                        }
                        if (AppSettingsKey.MultiFactorAuthEnabled.Enabled)
                            n.ProtocolMessage.AcrValues = authCfg.AcrValues ?? n.ProtocolMessage.AcrValues;
                    }
                    
                    logger.Debug("OIDC-Notification: RedirectToIdentityProvider Called");

                    //if signing out, add the id_token_hint
                    if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
                    {
                        logger.Debug("  RequestType=" + OpenIdConnectRequestType.Logout);
                        var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");

                        if (idTokenHint != null)
                        {
                            logger.Debug("  IdTokenHint got from n.OwinContext.Authentication.User");
                            n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
                        }
                        logger.Debug("  IdTokenHint=" + n?.ProtocolMessage?.IdTokenHint);
                        
                    }
                    return Task.CompletedTask;
                },

我确认发送了“codeVerifierCookie”。

AuthorizationCodeReceived = async n =>
                {
                    logger.Debug("OIDC-Notification: AuthorizationCodeReceived Called");
                    logger.Debug("  Code=" + n.Code);
                    logger.Debug("  AuthenticationType=" + n.Options.AuthenticationType);

                    if (authCfg.DiscoverEndpoints)
                    {
                        var disco = await n.Options.ConfigurationManager.GetConfigurationAsync(n.OwinContext.Request.CallCancelled);
                        authCfg.TokenEndpoint = disco.TokenEndpoint;
                        authCfg.UserinfoEndpoint = disco.UserInfoEndpoint;
                        authCfg.EndsessionEndpoint = disco.EndSessionEndpoint;
                        //authCfg.RevocationEndpoint = disco.RevocationEndpoint;
                        authCfg.WebKeySetEndpoint = disco.JwksUri;
                    }
                    if (AppSettingsKey.AuthCodeChallangeEnabled.Enabled) {
                        var codeVerifier = RetrieveCodeVerifier(n);
                        // attach code_verifier
                        n.TokenEndpointRequest.SetParameter("code_verifier", codeVerifier);
                    }
                    var requestMessage = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Post, authCfg.TokenEndpoint);
                    requestMessage.Content = new System.Net.Http.FormUrlEncodedContent(n.TokenEndpointRequest.Parameters);
                    var responseMessage = await n.Options.Backchannel.SendAsync(requestMessage);
                    responseMessage.EnsureSuccessStatusCode();
                    var responseContent = await responseMessage.Content.ReadAsStringAsync();
                    Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage message = new Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage(responseContent);

                    logger.Debug("  IdToken=" + message.IdToken);
                    logger.Debug("  AccessToken=" + message.AccessToken);

                    n.HandleCodeRedemption(message);

                },

问题是,当尝试检索“codeVerifierCookie”时,它不存在,当尝试登录 Edge 或 Chrome(在 Firefox 上)时。

以下是用于发送、检索和获取验证码的方法。CookieManager 配置为Microsoft.Owin.Host.SystemWeb.SystemWebChunkingCookieManager().

private void RememberCodeVerifier(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> n, string codeVerifier)
    {
        var properties = new AuthenticationProperties();
        properties.Dictionary.Add("cv", codeVerifier);
        n.Options.CookieManager.AppendResponseCookie(
            n.OwinContext,
            GetCodeVerifierKey(n.ProtocolMessage.State),
            Convert.ToBase64String(Encoding.UTF8.GetBytes(n.Options.StateDataFormat.Protect(properties))),
            new CookieOptions
            {
                SameSite = SameSiteMode.None,
                HttpOnly = true,
                Secure = n.Request.IsSecure,
                Expires = DateTime.UtcNow + n.Options.ProtocolValidator.NonceLifetime
            });
    }

    private string RetrieveCodeVerifier(AuthorizationCodeReceivedNotification n)
    {
        string key = GetCodeVerifierKey(n.ProtocolMessage.State);

        string codeVerifierCookie = n.Options.CookieManager.GetRequestCookie(n.OwinContext, key);
        if (codeVerifierCookie != null)
        {
            var cookieOptions = new CookieOptions
            {
                SameSite = SameSiteMode.None,
                HttpOnly = true,
                Secure = n.Request.IsSecure
            };

            n.Options.CookieManager.DeleteCookie(n.OwinContext, key, cookieOptions);
        }

        var cookieProperties = n.Options.StateDataFormat.Unprotect(Encoding.UTF8.GetString(Convert.FromBase64String(codeVerifierCookie)));
        cookieProperties.Dictionary.TryGetValue("cv", out var codeVerifier);

        return codeVerifier;
    }

    private string GetCodeVerifierKey(string state)
    {
        using (var hash = SHA256.Create())
        {
            return OpenIdConnectAuthenticationDefaults.CookiePrefix + "cv." + Convert.ToBase64String(hash.ComputeHash(Encoding.UTF8.GetBytes(state)));
        } 
    }

为什么我尝试从 Edge 或 Chrome 登录时缺少“codeVerifierCookie”?可能是一些默认设置还是我的设置缺少某些东西?为什么它可以在 Firefox 上运行?

感谢您阅读我的帖子,我欢迎有关此问题的所有意见。

标签: c#google-chromeowinmicrosoft-edgeopenid-connect

解决方案


你在测试你的应用程序时使用过 HTTPS 吗?断言的 CookieSameSite=None也必须标记为Secure. 我认为这个问题可能与同一站点 cookie 设置有关。

我找到了一个和你有同样问题的帖子,你可以参考一下。此外,还有一篇关于在 ASP.NET 中使用 SameSite cookie 的详细文章,您也可以查看它以获取更多信息。


推荐阅读