如何在 .NET Core 3.1 WebApi 中将 JWT 令牌交换为 Cognito 身份池中的凭据
How to exchange JWT token for Credentials in Cognito Identity Pool in .NET Core 3.1 WebApi
概览:我正在尝试创建一个针对 Amazon Cognito 进行身份验证的 .Net Core 3.1 WebApi 后端。我想使用 Cognito 提供的 Amazon-hosted sign-in 页面。我想在用户登录后利用 Cognito Identity Pool 为用户提供临时范围内的凭据。我不知道如何交换 Cognito 令牌来创建凭据来调用 AWS 服务。
技术概览
- .NET 核心 3.1 WebApi
- 用于初始身份验证的 Amazon Cognito 用户池
- 用于为登录用户定义权限(角色)的 Amazon Identity Pool
- 使用 AWS 无服务器框架(基本上是 CloudFormation)通过 API 网关 + Lambda 在 AWS 上部署
目前有以下两项工作:
- 将 [Authorize] 属性添加到控制器端点并在浏览器中访问 URL。这 re-directs 我到 Cognito-hosted 登录页面,成功登录后, returns 我回到 controller/endpoint 我被授权。
- 创建一个单独的客户端应用程序并登录到 AWS Cognito。从客户端调用 APIs 时在授权 HTTP header 中传递 JWT 令牌,授权成功并授予 API 访问权限。
在这两种情况下,都允许访问 API,但是在 WebApi 中创建的 AmazonServiceClient 实例被授予与 Lambda 函数关联的权限(这是正确的行为)。
问题
我需要创建 AmazonServiceClients,其凭据与 Cognito 身份池定义的角色相匹配。
为此,我需要交换通过登录 Cognito 用户池提供的令牌以获取身份池中的临时凭据。
我在这个过程中可以找到的几乎所有示例和文档都定义了如何使用 API(不是托管网站 UI)手动登录 Cognito,然后使用 API 响应以创建 CognitoUser,然后使用该用户从身份池获取凭据。
我能找到的最接近(虽然 超级 简短)的文档来自 AWS:https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/cognito-creds-provider.html
// Authenticate user through Facebook
string facebookToken = GetFacebookAuthToken();
// Add Facebook login to credentials. This clears the current AWS credentials
// and retrieves new AWS credentials using the authenticated role.
credentials.AddLogin("graph.facebook.com", facebookAccessToken);
虽然该示例使用 Facebook,但从概念上讲,它对于任何提供商(Facebook、Google、Twitter、OpenId 等)都应该是相同的。
我目前的尝试
我已将 CognitoAWSCredentials 注册为范围服务,因为它是 user-specific,因此只要 API 请求 session 存在,它就应该存在。
RegionEndpoint region = Configuration.GetAWSOptions().Region;
services.AddScoped(_ => new CognitoAWSCredentials(Settings.CognitoIdentityPoolId, region));
我已经创建了一个事件处理程序,它在 OpenIdConnect 事件 'OnTokenValidated' 被触发时被触发。这发生在我登录到 Cognito 托管网站 UI 并被重定向回我的 API.
在这个处理程序中我可以调用:
CognitoAWSCredentials creds = services.BuildServiceProvider().GetRequiredService<CognitoAWSCredentials>();
creds.AddLogin( ... ??? ...);
(注意:因为我在 Startup.ConfigureServices(IServiceCollection services)
方法中设置了所有这些,所以每次身份验证成功时我都会构建一个 IServiceProvider
实例......这可能是效率低下,但我还没有想出另一种方法来访问 ConfigureServices 方法中的范围服务)
所有这些序言都说我找不到允许此测试调用成功的 AddLogin
调用的一组值:
ImmutableCredentials immCreds = creds.GetCredentials();
相关数据结构
在我可以调用 AddLogin 的事件处理程序中,我可以访问:Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenValidatedContext
,其中特别包含:
Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage
与:
- access_token
- id_token
- refresh_token
System.IdentityModel.Tokens.Jwt.JwtSecurityToken
与:
{
{
"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": "**************************"
}
}
我尝试使用 iss
值作为 AddLogin 中的 providerName,并且 access_token
或 id_token
都不起作用。
有谁知道我需要为 AddLogin 使用什么,以便 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);
现在,上面的代码有一个问题,因为 services.BuildServiceProvider().
部分意味着我修改的凭据对象不是全局的(我认为只是我在这里构建的服务提供商的本地对象),但这是一个不同的问题- 只是注意以防万一有人复制此代码。
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;
}
};
})
(删除了一些代码以专门关注 OpenId Connect 事件和 CognitoAWSCredentials init)
概览:我正在尝试创建一个针对 Amazon Cognito 进行身份验证的 .Net Core 3.1 WebApi 后端。我想使用 Cognito 提供的 Amazon-hosted sign-in 页面。我想在用户登录后利用 Cognito Identity Pool 为用户提供临时范围内的凭据。我不知道如何交换 Cognito 令牌来创建凭据来调用 AWS 服务。
技术概览
- .NET 核心 3.1 WebApi
- 用于初始身份验证的 Amazon Cognito 用户池
- 用于为登录用户定义权限(角色)的 Amazon Identity Pool
- 使用 AWS 无服务器框架(基本上是 CloudFormation)通过 API 网关 + Lambda 在 AWS 上部署
目前有以下两项工作:
- 将 [Authorize] 属性添加到控制器端点并在浏览器中访问 URL。这 re-directs 我到 Cognito-hosted 登录页面,成功登录后, returns 我回到 controller/endpoint 我被授权。
- 创建一个单独的客户端应用程序并登录到 AWS Cognito。从客户端调用 APIs 时在授权 HTTP header 中传递 JWT 令牌,授权成功并授予 API 访问权限。
在这两种情况下,都允许访问 API,但是在 WebApi 中创建的 AmazonServiceClient 实例被授予与 Lambda 函数关联的权限(这是正确的行为)。
问题
我需要创建 AmazonServiceClients,其凭据与 Cognito 身份池定义的角色相匹配。
为此,我需要交换通过登录 Cognito 用户池提供的令牌以获取身份池中的临时凭据。
我在这个过程中可以找到的几乎所有示例和文档都定义了如何使用 API(不是托管网站 UI)手动登录 Cognito,然后使用 API 响应以创建 CognitoUser,然后使用该用户从身份池获取凭据。
我能找到的最接近(虽然 超级 简短)的文档来自 AWS:https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/cognito-creds-provider.html
// Authenticate user through Facebook
string facebookToken = GetFacebookAuthToken();
// Add Facebook login to credentials. This clears the current AWS credentials
// and retrieves new AWS credentials using the authenticated role.
credentials.AddLogin("graph.facebook.com", facebookAccessToken);
虽然该示例使用 Facebook,但从概念上讲,它对于任何提供商(Facebook、Google、Twitter、OpenId 等)都应该是相同的。
我目前的尝试
我已将 CognitoAWSCredentials 注册为范围服务,因为它是 user-specific,因此只要 API 请求 session 存在,它就应该存在。
RegionEndpoint region = Configuration.GetAWSOptions().Region;
services.AddScoped(_ => new CognitoAWSCredentials(Settings.CognitoIdentityPoolId, region));
我已经创建了一个事件处理程序,它在 OpenIdConnect 事件 'OnTokenValidated' 被触发时被触发。这发生在我登录到 Cognito 托管网站 UI 并被重定向回我的 API.
在这个处理程序中我可以调用:
CognitoAWSCredentials creds = services.BuildServiceProvider().GetRequiredService<CognitoAWSCredentials>();
creds.AddLogin( ... ??? ...);
(注意:因为我在 Startup.ConfigureServices(IServiceCollection services)
方法中设置了所有这些,所以每次身份验证成功时我都会构建一个 IServiceProvider
实例......这可能是效率低下,但我还没有想出另一种方法来访问 ConfigureServices 方法中的范围服务)
所有这些序言都说我找不到允许此测试调用成功的 AddLogin
调用的一组值:
ImmutableCredentials immCreds = creds.GetCredentials();
相关数据结构
在我可以调用 AddLogin 的事件处理程序中,我可以访问:Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenValidatedContext
,其中特别包含:
Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage
与:- access_token
- id_token
- refresh_token
System.IdentityModel.Tokens.Jwt.JwtSecurityToken
与:
{
{
"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": "**************************"
}
}
我尝试使用 iss
值作为 AddLogin 中的 providerName,并且 access_token
或 id_token
都不起作用。
有谁知道我需要为 AddLogin 使用什么,以便 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);
现在,上面的代码有一个问题,因为 services.BuildServiceProvider().
部分意味着我修改的凭据对象不是全局的(我认为只是我在这里构建的服务提供商的本地对象),但这是一个不同的问题- 只是注意以防万一有人复制此代码。
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;
}
};
})
(删除了一些代码以专门关注 OpenId Connect 事件和 CognitoAWSCredentials init)