You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET MVC5集成AWS Cognito动态配置后出现Invalid_Client错误

AWS Cognito集成.NET MVC5时动态分配ClientId/Authority触发invalid_client错误

我正在把AWS Cognito用户认证集成到.NET MVC5前端应用里。应用能正常重定向到Cognito域名完成登录,也能进入AuthorizationCodeReceived环节,但执行完该环节的Task.CompletedTask后,出现invalid_client错误,error_description为null。

只有在RedirectToIdentityProvider中动态分配ClientId和Authority时才会出问题,用默认ClientId和Authority时完全正常。

相关代码如下:

var userpoolId = "default-userpoolId";
var authority = $"https://cognito-idp.eu-west-1.amazonaws.com/{userpoolId}";
var authMetadata = $"{authority}/.well-known/openid-configuration";
var cognitoAuthority = "Cognito-Client-Domain";

app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
    // 信任的认证源
    Authority = authority,
    ClientId = clientId,
    MetadataAddress = authMetadata,

    // 暂时可能不需要
    RedirectUri = "example.com",
    PostLogoutRedirectUri = "example.com",

    // PKCE流程,获取用户信息和引用令牌
    ResponseType = OpenIdConnectResponseType.Code,
    ResponseMode = "query",
    // 所需权限:登录、用户角色、API访问
    Scope = "openid profile email",// aws.cognito.signin.user.admin",

    // 将加密信息存储在登录Cookie中
    SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
    // 禁用令牌生命周期自动处理,手动管理过期
    UseTokenLifetime = false,
    SaveTokens = true,
    RedeemCode = true,
    ProtocolValidator = new OpenIdConnectProtocolValidator() { RequireNonce = false, RequireStateValidation = false, RequireState = false },

    AuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType,
    AuthenticationMode = AuthenticationMode.Active,
    TokenValidationParameters = new TokenValidationParameters
    {
        // 不使用默认的单一发行者验证,注入多租户验证逻辑
        ValidateIssuer = false,
    },

    Notifications = new OpenIdConnectAuthenticationNotifications
    {
        RedirectToIdentityProvider = async n =>
        {
            var serverPathBase = $"https://{n.Request.Host}{GetPathBase(n.Request)}";
            var instanceConnection = await InstanceCache.MapUrlToInstance(n.Request.Uri);
            cognitoAuthority = instanceConnection.CognitoAppClientDomain;

            authority = $"https://cognito-idp.eu-west-1.amazonaws.com/{instanceConnection.UserpoolId}";
            authMetadata = $"{authority}/.well-known/openid-configuration";

            Logging.Logger.Info($"Instance Connections are:{instanceConnection.UserpoolId} , {instanceConnection.CognitoAppClientDomain}, {instanceConnection.CognitoClientId}");
            n.Options.Authority = authority;
            n.Options.ClientId = instanceConnection.CognitoClientId;
            n.ProtocolMessage.ClientId = instanceConnection.CognitoClientId;
            n.Options.MetadataAddress = authMetadata;

            if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
            {
                var postLogoutUri = $"{serverPathBase}";
                n.ProtocolMessage.IssuerAddress = $"{cognitoAuthority}/logout?logout_uri={postLogoutUri}&client_id={clientId}";

                n.OwinContext.Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType, OpenIdConnectAuthenticationDefaults.AuthenticationType);

                await Task.FromResult(0);
            }

            if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
            {
                n.ProtocolMessage.IssuerAddress = $"{cognitoAuthority}/login";
                n.Options.MetadataAddress = $"{authority}/.well-known/openid-configuration";

                (var startupId, var startupCookie) = LookForStarterInformation(n.Request);
                Logging.Logger.Info($"Mapped {n.Request.Uri} to {instanceConnection.InstanceName} for login");
                n.ProtocolMessage.AcrValues = $"tenant:{instanceConnection.InstanceName}";

                if (startupId.HasValue && !String.IsNullOrWhiteSpace(startupCookie))
                {
                    n.ProtocolMessage.AcrValues += $" startupId:{startupId} startupCookie:{startupCookie}";
                }

                var redirectUri = $"{serverPathBase}/signin-oidc";
                n.ProtocolMessage.RedirectUri = redirectUri;
                n.ProtocolMessage.PostLogoutRedirectUri = $"{serverPathBase}";

                n.ProtocolMessage.ResponseMode = "query";

                // PKCE code challenge生成
                var codeVerifier = CryptoRandom.CreateUniqueId(32);
                string codeChallenge;
                using (var sha256 = SHA256.Create())
                {
                    var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
                    codeChallenge = Base64Url.Encode(challengeBytes);
                }

                n.ProtocolMessage.SetParameter("code_challenge", codeChallenge);
                n.ProtocolMessage.SetParameter("code_challenge_method", "S256");

                Logging.Logger.Info("Code verifier is: " + codeVerifier);

                RememberCodeVerifier(n, codeVerifier);

                await Task.CompletedTask;
            }
        },
        AuthorizationCodeReceived = async n =>
        {
            Logging.Logger.Info("Authorization Code Received");

            var instanceConnection = await InstanceCache.MapUrlToInstance(n.Request.Uri);

            var codeVerifier = RetrieveCodeVerifier(n);

            Logging.Logger.Info("Code verifier is: " + codeVerifier);

            var serverPathBase = $"https://{n.Request.Host}{GetPathBase(n.Request)}";

            n.Options.ClientId = instanceConnection.CognitoClientId;
            n.ProtocolMessage.ClientId = instanceConnection.CognitoClientId;
            n.TokenEndpointRequest.RedirectUri = $"{serverPathBase}/signin-oidc";
            n.ProtocolMessage.RedirectUri = $"{serverPathBase}/signin-oidc";

            n.TokenEndpointRequest.SetParameter("code_verifier", codeVerifier);
            n.TokenEndpointRequest.SetParameter("client_id", instanceConnection.CognitoClientId);

            await Task.CompletedTask;
        }
    }
});

问题排查与修复建议

  1. 统一ClientId传递
    在AuthorizationCodeReceived中,确保n.Options.ClientId、n.ProtocolMessage.ClientId、n.TokenEndpointRequest中的client_id参数完全一致,且与Cognito用户池的应用客户端ID匹配。

  2. 同步更新令牌请求的Authority配置
    在AuthorizationCodeReceived中补充更新Authority和Metadata地址,避免令牌请求使用旧的配置:

    // 在AuthorizationCodeReceived方法内添加
    var userPoolAuthority = $"https://cognito-idp.eu-west-1.amazonaws.com/{instanceConnection.UserpoolId}";
    n.Options.Authority = userPoolAuthority;
    n.Options.MetadataAddress = $"{userPoolAuthority}/.well-known/openid-configuration";
    
  3. 验证Cognito应用客户端配置

    • 确认动态分配的ClientId对应的应用客户端已启用授权码流(Authorization Code Grant)。
    • 检查该应用客户端的回调URL列表是否包含动态生成的https://{你的域名}/signin-oidc,Cognito会拒绝不在列表内的回调请求。
    • 如果应用客户端设置了客户端密钥,必须在令牌请求中添加client_secret参数:
      n.TokenEndpointRequest.SetParameter("client_secret", instanceConnection.CognitoClientSecret);
      
  4. 避免全局变量并发覆盖
    代码中使用的全局authority、cognitoAuthority变量在多请求并发时可能被覆盖,建议改用请求上下文存储这些值,或直接在通知方法内使用局部变量。

  5. 日志排查参数正确性
    在AuthorizationCodeReceived中添加日志,输出n.TokenEndpointRequest的所有参数,确认client_id、redirect_uri、code_verifier等参数是否正确传递给Cognito令牌端点。

内容的提问来源于stack exchange,提问作者Rutuja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 23:50:55