Asp.net web api 如何在.NETCore3.1WebAPI的Cognito标识池中用JWT令牌交换凭据

Asp.net web api 如何在.NETCore3.1WebAPI的Cognito标识池中用JWT令牌交换凭据,asp.net-web-api,jwt,amazon-cognito,openid-connect,asp.net-core-3.1,Asp.net Web Api,Jwt,Amazon Cognito,Openid Connect,Asp.net Core 3.1,概述:我正在尝试创建一个.Net Core 3.1 WebApi后端,该后端通过Amazon Cognito的身份验证。我想使用Cognito提供的亚马逊托管登录页面。我想利用Cognito标识池在用户登录后为他们提供临时范围的凭证。我不知道如何交换Cognoto令牌来创建调用AWS服务的凭据 技术概述 .NET核心3.1 WebApi 用于初始身份验证的Amazon Cognito用户池 用于为登录用户定义权限(角色)的Amazon标识池 使用AWS无服务器框架(基本上是CloudForma

概述:我正在尝试创建一个.Net Core 3.1 WebApi后端,该后端通过Amazon Cognito的身份验证。我想使用Cognito提供的亚马逊托管登录页面。我想利用Cognito标识池在用户登录后为他们提供临时范围的凭证。我不知道如何交换Cognoto令牌来创建调用AWS服务的凭据

技术概述

  • .NET核心3.1 WebApi
  • 用于初始身份验证的Amazon Cognito用户池
  • 用于为登录用户定义权限(角色)的Amazon标识池
  • 使用AWS无服务器框架(基本上是CloudFormation)通过API网关+Lambda在AWS上部署
目前正在进行以下两项工作:

  • 将[Authorize]属性添加到控制器端点,并在浏览器中访问URL。这会将我重新引导到Cognito托管的登录页面,并在成功登录后将我返回到控制器/端点,我获得授权
  • 创建一个单独的客户端应用程序并登录到AWS Cognito。当从客户端调用API时,在授权HTTP头中传递JWT令牌,授权成功并授予API访问权
在这两种情况下,都允许访问API,但是在WebApi中创建的AmazonServiceClient实例被授予与Lambda函数相关的权限(这是正确的行为)

问题 我需要创建AmazonServiceClient,其凭据与Cognito标识池定义的角色匹配。

为此,我需要将登录Cognoto用户池提供的令牌交换为标识池中的临时凭证

实际上,我在这个过程中找到的所有示例和文档都定义了如何使用API(而不是托管的web UI)手动登录到Cognito,然后使用API响应创建CognitoUser,然后使用该用户从标识池获取凭据

我能找到的最接近我需要的文档(尽管非常简短)来自AWS:

虽然该示例使用Facebook,但从概念上讲,任何提供商(Facebook、Google、Twitter、OpenId等)都应该使用相同的方法

我目前的尝试 我已经将CognitoAWSCredentials注册为作用域服务,因为它是特定于用户的,因此应该只在API请求会话存在时才存在

RegionEndpoint region = Configuration.GetAWSOptions().Region;
services.AddScoped(_ => new CognitoAWSCredentials(Settings.CognitoIdentityPoolId, region));
我已经创建了一个事件处理程序,它在触发OpenIdConnect事件“OnTokenValidated”时被触发。这发生在我登录到Cognito托管的web UI并被重定向回API之后

在此处理程序中,我可以调用:

CognitoAWSCredentials creds = services.BuildServiceProvider().GetRequiredService<CognitoAWSCredentials>();
creds.AddLogin( ... ??? ...);
相关数据结构 在可以调用AddLogin的事件处理程序中,我可以访问:
Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenValidatedContext
,其中特别包含:

  • Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage
    with:
    • 访问令牌
    • 身份证
    • 刷新令牌
  • System.IdentityModel.Tokens.Jwt.JwtSecurityToken
    带有:
我尝试使用
iss
值作为AddLogin中的providerName,以及
access\u令牌
id\u令牌
,但都不起作用


有人知道我需要使用什么来添加登录,以便Cognito基于Cognito用户池登录的JWT令牌为我创建身份池凭据吗?

除非我错过了,否则我没有看到说明这一点的文档,但是,即使各种数据结构上的所有Issuer字段都包含“https://”,在使用Issuer作为
AddLogin
调用上的
providerName
之前,您仍需要将其删除。啊

CognitoAWSCredentials creds = services.BuildServiceProvider().GetRequiredService<CognitoAWSCredentials>();
string shortIssuer = tokenValidatedContext.SecurityToken.Issuer;
if (shortIssuer.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase)) shortIssuer = shortIssuer.Substring("https://".Length);
if (shortIssuer.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase)) shortIssuer = shortIssuer.Substring("http://".Length);
creds.AddLogin(shortIssuer, tokenValidatedContext.TokenEndpointResponse.IdToken);

(一些代码被删除,专门关注OpenId Connect事件和CognitoAWSCredentials init)

除非我错过了它,否则我还没有看到说明这一点的文档,但即使各种数据结构上的所有Issuer字段都包括“https://”,在使用发卡机构作为
AddLogin
调用上的
providerName
之前,需要将其剥离。啊

CognitoAWSCredentials creds = services.BuildServiceProvider().GetRequiredService<CognitoAWSCredentials>();
string shortIssuer = tokenValidatedContext.SecurityToken.Issuer;
if (shortIssuer.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase)) shortIssuer = shortIssuer.Substring("https://".Length);
if (shortIssuer.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase)) shortIssuer = shortIssuer.Substring("http://".Length);
creds.AddLogin(shortIssuer, tokenValidatedContext.TokenEndpointResponse.IdToken);
(删除了一些代码,专门关注OpenId Connect事件和CognitoAWSCredentials init)

{
    {
        "alg": "RS256",
        "kid": "**************************"
    }. {
        "at_hash": "**************************",
        "sub": "**************************",
        "email_verified": true,
        "iss": "https://cognito-idp.ca-central-1.amazonaws.com/**************************",
        "cognito:username": "**************************",
        "nonce": "**************************",
        "aud": "**************************",
        "event_id": "**************************",
        "token_use": "id",
        "auth_time": 1595260191,
        "exp": 1595263791,
        "iat": 1595260191,
        "email": "**************************"
    }
}
CognitoAWSCredentials creds = services.BuildServiceProvider().GetRequiredService<CognitoAWSCredentials>();
string shortIssuer = tokenValidatedContext.SecurityToken.Issuer;
if (shortIssuer.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase)) shortIssuer = shortIssuer.Substring("https://".Length);
if (shortIssuer.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase)) shortIssuer = shortIssuer.Substring("http://".Length);
creds.AddLogin(shortIssuer, tokenValidatedContext.TokenEndpointResponse.IdToken);
            services...<other authentication setup>...
                    .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
                        {
                            options.ClientId        = Settings.CognitoClientId;
                            options.MetadataAddress = CognitoMetadataAddress;
                            options.ResponseType    = OpenIdConnectResponseType.Code;
                            options.SaveTokens      = true;
                            options.UsePkce         = true;
                            options.TokenValidationParameters = new TokenValidationParameters()
                            {
                                ValidateIssuer = true,
                                ValidIssuers = new string[] { Settings.CognitoAuthority },
                                ValidateAudience = true,
                                ValidAudiences = new string[] { Settings.CognitoClientId }
                            };
                            options.Events = new OpenIdConnectEvents() {
                                OnTokenValidated = tokenValidatedContext => {
                                    CognitoAWSCredentials creds = services.BuildServiceProvider().GetRequiredService<CognitoAWSCredentials>();
                                    string shortIssuer = tokenValidatedContext.SecurityToken.Issuer;
                                    if (shortIssuer.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase)) shortIssuer = shortIssuer.Substring("https://".Length);
                                    if (shortIssuer.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase)) shortIssuer = shortIssuer.Substring("http://".Length);
                                    creds.AddLogin(shortIssuer, tokenValidatedContext.TokenEndpointResponse.IdToken);
                                    return Task.CompletedTask;
                                }
                            };
                        })