

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 将自定义属性发送至 Amazon Cognito 并将其注入令牌中
<a name="send-custom-attributes-cognito"></a>

*Carlos Alessandro Ribeiro 和 Mauricio Mendoza，Amazon Web Services*

## Summary
<a name="send-custom-attributes-cognito-summary"></a>

通过向 Amazon Cognito 身份验证流程发送自定义属性，可为应用程序提供额外上下文信息，实现更精细的访问控制，并更轻松地管理用户配置文件和身份验证要求。这些功能适用于各类应用程序及场景，有助于提升应用程序的整体安全性与功能性。

此模式显示当应用程序需要为[访问令牌](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-access-token.html)或[身份（ID）令牌](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-id-token.html)提供额外上下文时，如何向 Amazon Cognito 身份验证流程发送自定义属性。您使用 Node.js 作为后端应用程序。该应用程序会对 Amazon Cognito [用户池](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools.html)中的用户进行身份验证，并传递生成令牌所需的自定义属性。您可以使用 Amazon Cognito 的 [AWS Lambda 触发器](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-working-with-lambda-triggers.html)来自定义身份验证流程，而无需进行大量的代码自定义或完成大量工作。

**重要**  
此模式中的代码和示例仅用于演示，不建议用于生产工作负载。对于生产工作负载，需要在客户端上进行额外配置。使用此模式仅供飞行员参考，或仅供参 proof-of-concept考。

## 先决条件和限制
<a name="send-custom-attributes-cognito-prereqs"></a>

**先决条件**
+ 活跃的 AWS 账户
+ 创建和管理 Amazon Cognito 用户池和函数的权限 AWS Lambda 
+ AWS Command Line Interface (AWS CLI)，[已安装](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)并[配置](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html)
+ 支持 Node.js 的集成式开发环境（IDE）
+ [已安装](https://nodejs.org/en/download/) NodeJS 版本 18 或更高版本
+ [已安装](https://docs.npmjs.com/getting-started) npm 版本 8 或更高版本
+ TypeScript，[已安装](https://www.typescriptlang.org/download/)

**限制**
+ 此模式不适用于通过客户端凭证身份验证流程进行的应用程序集成。
+ 令牌生成前触发器只能添加或更改访问令牌和身份令牌的部分属性。有关更多信息，请参阅 Amazon Cognito 文档中的[令牌生成前 Lambda 触发器](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-token-generation.html)。

## 架构
<a name="send-custom-attributes-cognito-architecture"></a>

**目标架构**

下图显示了此模式的目标架构。它还显示了 Node.js 应用程序如何与后端协同实现数据库更新。但是，后端数据库更新不在此模式的范围之内。

![\[向 Amazon Cognito 用户池颁发带有自定义属性的访问令牌的 Node.js 应用程序。\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/9f0855e6-77f9-48c2-846e-f9c317127e1f/images/8c52c88b-8954-4b4c-aed3-fd8c22f84c1d.png)


下图显示了如下工作流：

1. Node.js 应用程序向 Amazon Cognito 用户池颁发带有自定义属性的访问令牌。

1. Amazon Cognito 用户池启动令牌生成前 Lambda 函数，该函数可自定义访问令牌和 ID 令牌。

1. Node.js 应用程序通过 Amazon API Gateway 进行 API 调用。

**注意**  
此架构中显示的其他架构组件仅供参考，不在此模式的范围之内。

**自动化和扩展**

您可以使用[AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html)、、[HashiCorp Terraform](https://www.terraform.io/docs) 或任何支持的基础设施即代码 (IaC) 工具，自动配置 Amazon Cognito 用户池、 AWS Lambda 函数、数据库实例和其他资源。[AWS Cloud Development Kit (AWS CDK)](https://docs.aws.amazon.com/cdk/v2/guide/home.html)如需扩展部署规模，请使用持续集成和持续交付（CI/CD）管道，这有助于避免手动部署可能引发的错误。

## 工具
<a name="send-custom-attributes-cognito-tools"></a>

**AWS 服务**
+ [Amazon API Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/welcome.html) 可帮助您创建、发布、维护、监控和保护任何规模的 RES WebSocket APIs T、HTTP。
+ [Amazon Cognito](https://docs.aws.amazon.com/cognito/latest/developerguide/what-is-amazon-cognito.html) 为您的 Web 和移动应用程序提供身份验证、授权和用户管理。
+ [Amazon Elastic Container Service（Amazon ECS）](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html)是一项快速、可扩展的容器管理服务，可帮助您运行、停止和管理集群上的容器。
+ [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) 是一项计算服务，可帮助您运行代码，无需预调配或管理服务器。它只在需要时运行您的代码，并自动进行扩展，因此您只需为使用的计算时间付费。
+ [适用于 JavaScript 的 AWS SDK](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/welcome.html)为提供了 JavaScript API AWS 服务。您可以使用其构建适用于 Node.js 或浏览器的库或应用程序。

**其他工具**
+ [Node.js](https://nodejs.org/en/docs/) 是一个事件驱动的 JavaScript 运行时环境，专为构建可扩展的网络应用程序而设计。
+ [npm](https://docs.npmjs.com/about-npm) 是在 Node.js 环境中运行的软件注册表，用于共享或借用软件包以及管理私有软件包的部署。

## 最佳实践
<a name="send-custom-attributes-cognito-best-practices"></a>

建议您实施以下最佳实践：
+ **密钥和敏感数据** – 请勿在应用程序内部存储密钥或敏感数据。使用应用程序可以从中提取数据的外部系统，例如 [AWS AppConfig](https://docs.aws.amazon.com/appconfig/latest/userguide/what-is-appconfig.html)、[AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html) 或 [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html)。
+ **标准化部署**-使用 CI/CD 管道部署应用程序。您可以使用 [AWS CodeBuild](https://docs.aws.amazon.com/codebuild/latest/userguide/welcome.html) 和 [AWS CodePipeline](https://docs.aws.amazon.com/codepipeline/latest/userguide/welcome.html) 等服务。
+ **令牌到期** - 为访问令牌设置较短的到期日期。
+ **使用安全连接** - 客户端应用程序和后端之间的所有通信都应使用 SSL/TLS 进行加密。使用 [AWS Certificate Manager (ACM)](https://docs.aws.amazon.com/acm/latest/userguide/acm-overview.html) 生成和管理 SSL/TLS 证书，并使用 [Amazon CloudFront](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Introduction.html) 或 [Elastic Load Balancing](https://docs.aws.amazon.com/elasticloadbalancing/latest/userguide/what-is-load-balancing.html) 来处理 SSL/TLS 终止问题。
+ **验证用户输入** - 确保对所有用户输入进行验证，以防止注入攻击和其他安全漏洞。使用输入验证库以及 Amazon API Gateway 和 [AWS WAF](https://docs.aws.amazon.com/waf/latest/developerguide/what-is-aws-waf.html#waf-intro) 等服务，防范常见的攻击向量。
+ **使用 IAM 角色**-使用 [AWS Identity and Access Management (IAM)](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html) 角色控制对 AWS 资源的访问权限，并确保只有经过授权的用户才能访问资源。遵循最低权限原则，确保每个用户仅拥有执行其角色所需的权限。
+ **使用密码策略** - 配置符合您的安全要求的密码策略，例如最小长度、复杂性和有效期。使用 Secrets Manag AWS Systems Manager er 或 Parameter Store 来安全地存储和管理密码。
+ **启用多重身份验证（MFA）**- 为所有用户启用 MFA，以提供额外的安全层并降低未经授权访问的风险。使用 [AWS IAM Identity Center](https://docs.aws.amazon.com/singlesignon/latest/userguide/what-is.html) 或 Amazon Cognito 启用 MFA 及其他身份验证方法。
+ **安全地存储敏感信息** - 使用（[AWS Key Management ServiceAWS KMS）](https://docs.aws.amazon.com/kms/latest/developerguide/overview.html)或其他加密服务，安全地存储密码、访问令牌等敏感信息。
+ **使用强身份验证方法** - 为了提高身份验证流程的安全性，请使用生物识别身份验证或多重身份验证等强身份验证方法。
+ **监控可疑活动** - 使用 [AWS CloudTrail](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-user-guide.html) 及其他监控工具来监控可疑活动和潜在的安全威胁。为异常活动设置自动警报，并使用 [Amazon GuardDuty](https://docs.aws.amazon.com/guardduty/latest/ug/what-is-guardduty.html) 或[AWS Security Hub CSPM](https://docs.aws.amazon.com/securityhub/latest/userguide/what-is-securityhub.html)来检测潜在威胁。
+ **定期审查和更新安全策略** – 定期审查并更新您的安全策略和程序，确保其符合不断变化的安全要求和最佳实践。 AWS Config 用于跟踪和审核安全策略和程序的更改。
+ **自动注册** – 请勿启用 Amazon Cognito 用户池的自动注册功能。有关更多信息，请参阅使用 [Amazon Cognito 用户池降低用户注册欺诈和短信激增的风险](https://aws.amazon.com/blogs/security/reduce-risks-of-user-sign-up-fraud-and-sms-pumping-with-amazon-cognito-user-pools/)AWS （博客文章）。

如需了解其他最佳实践，请参阅 Amazon Cognito 文档中的 [Amazon Cognito 用户池的安全最佳实践](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-security-best-practices.html)。

## 操作说明
<a name="send-custom-attributes-cognito-epics"></a>

### 设置 AWS 资源
<a name="set-up-the-aws-resources"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建用户池。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/send-custom-attributes-cognito.html)有关如何在中设置用户池的更多信息和说明 AWS 管理控制台，请参阅用户池[入门和向用户池](https://docs.aws.amazon.com/cognito/latest/developerguide/getting-started-user-pools.html)[添加更多功能和安全选项](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-next-steps.html)。为了降低成本，请使用基础版计划或精简版计划来测试此模式。有关更多信息，请参阅 [Amazon Cognito 定价](https://aws.amazon.com/cognito/pricing/)。 | AWS 应用程序开发人员 DevOps | 
| 向用户池中添加用户。 | 输入以下命令，在 Amazon Cognito 用户池中创建一个用户：<pre>aws cognito-idp sign-up \<br />   --client-id <ClientID> \<br />   --username <jane@example.com> \<br />   --password <PASSWORD> \<br />   --user-attributes Name="email",Value="<jane@example.com>" Name="name",Value="<Jane>"</pre> | AWS 应用程序开发人员 DevOps | 
| 向用户池中添加应用程序客户端。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/send-custom-attributes-cognito.html) | AWS 系统管理员、AWS 管理员、AWS DevOps、应用程序开发者 | 
| 创建令牌生成前 Lambda 触发器。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/send-custom-attributes-cognito.html) | AWS DevOps，应用程序开发者 | 
| 自定义用户池工作流。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/send-custom-attributes-cognito.html)有关更多信息，请参阅 Amazon Cognito 文档中的[使用 Lambda 触发器自定义用户池工作流](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-working-with-lambda-triggers.html)。 | AWS DevOps，应用程序开发者 | 

### 创建 Node.js 应用程序
<a name="create-the-node-js-application"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建 Node.js 应用程序。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/send-custom-attributes-cognito.html) | 应用程序开发人员 | 
| 实施身份验证逻辑。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/send-custom-attributes-cognito.html)您可以根据自己的用例创建自己的 TypeScript 文件或修改所提供的示例。 | 应用程序开发人员 | 
| 配置环境变量和配置文件。 | 在终端中，输入以下命令创建环境变量：<pre>export USERNAME="<COGNITO_USER_NAME>"<br />export PASSWORD="<COGNITO_USER_PASSWORD>"<br />export USER_POOL_ID="<COGNITO_USER_ID>"<br />export CLIENT_ID="<COGNITO_CLIENT_ID>"</pre>请勿对密钥进行硬编码或泄露您的凭证。 | 应用程序开发人员 | 
| 运行 应用程序。 | 输入以下命令运行应用程序，并确认其正常工作：<pre>npm run build<br />npm start</pre> | 应用程序开发人员 | 
| 确认自定义属性已注入到令牌中。 | 使用 IDE 的调试功能查看访问令牌和 ID 令牌。确认自定义属性已添加。有关示例令牌，请参阅此模式的[其他信息](#send-custom-attributes-cognito-additional)部分。 | 应用程序开发人员 | 

## 问题排查
<a name="send-custom-attributes-cognito-troubleshooting"></a>


| 问题 | 解决方案 | 
| --- | --- | 
| 尝试对用户进行身份验证时发生客户端 ID 无效错误 | 当您使用的客户端 ID 已生成客户端密钥时，通常会发生此错误。您必须创建一个不附加密钥的客户端 ID。有关更多信息，请参阅[使用应用程序客户端进行应用程序特定设置](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-client-apps.html)。 | 

## 相关资源
<a name="send-custom-attributes-cognito-resources"></a>
+ [使用 Lambda 触发器自定义用户池工作流](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html)（Amazon Cognito 文档）
+ [令牌生成前 Lambda 触发器](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-token-generation.html)（Amazon Cognito 文档）
+ [CognitoIdentityProviderClient](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/cognito-identity-provider/)（适用于 JavaScript 的 AWS SDK 文档）
+ [cognito-idp（文档）](https://awscli.amazonaws.com/v2/documentation/api/2.0.34/reference/cognito-idp/index.html#cli-aws-cognito-idp)AWS CLI 

## 附加信息
<a name="send-custom-attributes-cognito-additional"></a>

**示例 TypeScript 文件**

以下代码示例是一个通过使用 AWS 软件开发工具包向 Amazon Cognito 发送自定义属性来调用身份验证过程的 TypeScript 文件：

```
import * as AmazonCognitoIdentity from "amazon-cognito-identity-js";

const userPoolId: string = process.env.USER_POOL_ID ?? '';
const clientId: string = process.env.CLIENT_ID ?? '';

const poolData = {
  UserPoolId: userPoolId,
  ClientId: clientId
};
const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);

export const loginWithCognitoSDK = function (userName: string, password: string) {
  const authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails({
    Username: userName,
    Password: password,
    ClientMetadata: {
        customGroup: "MyCustomGroup",
        customApplicationData: "Custom data from a custom application"
    }
  });
  const userData = {
    Username: userName,
    Pool: userPool
  };

  const cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);

  // Authenticate the user using the authenticationDetails object
  cognitoUser.authenticateUser(authenticationDetails, {
    onSuccess: function (result: any) {},
    onFailure: function (err: any) {},
  });
}
loginWithCognitoSDK(process.env.USERNAME ?? '', process.env.PASSWORD ?? '');
```

该示例使用的 SDK 中的`AuthenticationDetails`模型 JavaScript 来提供用户名、密码和`ClientMetadada`。在 Amazon Cognito 中进行身份验证后，可以从访问令牌和 ID 令牌中检索客户端元数据。

**示例 Lambda 函数**

以下代码示例是一个 Lambda 函数，该已与 Amazon Cognito 的[令牌生成前触发器](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html)关联。它可以帮助您自定义 Amazon Cognito 使用的访问令牌和 ID 令牌。这些令牌会通过您架构之间的集成传递。此示例包含名为 `customApplicationData` 的自定义声明属性以及名为 `MyCustomGroup` 的自定义组名称：

```
export const handler = async(event, context, callback) => {
    event.response = {
        claimsOverrideDetails: {
            claimsToAddOrOverride: { customApplicationData: event.request.clientMetadata.customApplicationData },
            groupOverrideDetails: { groupsToOverride: [event.request.clientMetadata.customGroup] }
        }
    };
    callback(null, event);
};
```

**示例访问令牌**

您可以对访问令牌进行解码，以查看已添加的自定义属性。以下是示例访问令牌：

```
{
  "sub": "6daf331f-4451-48b4-abde-774579299204",
  "cognito:groups": [
    "MyCustomGroup"
  ],
  "iss": "https://cognito-idp.<REGION>.amazonaws.com/<USERPOOL_ID>",
  "client_id": "<YOUR_CLIENT_ID>",
  "origin_jti": "acff7e91-09f9-4fde-8eec-38b0f8c47cdc",
  "event_id": "c5113a9c-1f01-435b-9b73-a5cd3e88514e",
  "token_use": "access",
  "scope": "aws.cognito.signin.user.admin",
  "auth_time": 1677979246,
  "exp": 1677982846,
  "iat": 1677979246,
  "jti": "5c9c2708-a871-4428-bd9b-18ad261bea90",
  "username": "<USER_NAME>"
}
```

**示例 ID 令牌**

您可以对访问令牌进行解码，以查看已添加的自定义属性。以下是示例访问令牌：

```
{
  "sub": "6daf331f-4451-48b4-abde-774579299204",
  "cognito:groups": [
    "MyCustomGroup"
  ],
  "iss": "https://cognito-idp.<REGION>.amazonaws.com/<USERPOOL_ID>",
  "cognito:username": "<USER_NAME>",
  "origin_jti": "acff7e91-09f9-4fde-8eec-38b0f8c47cdc",
  "customApplicationData": "Custom data from a custom application",
  "aud": "<YOUR_CLIENT_ID>",
  "event_id": "c5113a9c-1f01-435b-9b73-a5cd3e88514e",
  "token_use": "id",
  "auth_time": 1677979246,
  "exp": 1677982846,
  "iat": 1677979246,
  "jti": "f7ca006b-f25b-44d2-a7a4-6e6423f4201f",
  "email": "<USER_EMAIL>"
}
```