本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
将自定义属性发送至 Amazon Cognito 并将其注入令牌中
Carlos Alessandro Ribeiro 和 Mauricio Mendoza,Amazon Web Services
Summary
通过向 Amazon Cognito 身份验证流程发送自定义属性,可为应用程序提供额外上下文信息,实现更精细的访问控制,并更轻松地管理用户配置文件和身份验证要求。这些功能适用于各类应用程序及场景,有助于提升应用程序的整体安全性与功能性。
此模式显示当应用程序需要为访问令牌或身份(ID)令牌提供额外上下文时,如何向 Amazon Cognito 身份验证流程发送自定义属性。您使用 Node.js 作为后端应用程序。该应用程序会对 Amazon Cognito 用户池中的用户进行身份验证,并传递生成令牌所需的自定义属性。您可以使用 Amazon Cognito 的 AWS Lambda 触发器来自定义身份验证流程,而无需进行大量的代码自定义或完成大量工作。
重要
此模式中的代码和示例仅用于演示,不建议用于生产工作负载。对于生产工作负载,需要在客户端上进行额外配置。使用此模式仅供飞行员参考,或仅供参 proof-of-concept考。
先决条件和限制
先决条件
限制
此模式不适用于通过客户端凭证身份验证流程进行的应用程序集成。
令牌生成前触发器只能添加或更改访问令牌和身份令牌的部分属性。有关更多信息,请参阅 Amazon Cognito 文档中的令牌生成前 Lambda 触发器。
架构
目标架构
下图显示了此模式的目标架构。它还显示了 Node.js 应用程序如何与后端协同实现数据库更新。但是,后端数据库更新不在此模式的范围之内。

下图显示了如下工作流:
Node.js 应用程序向 Amazon Cognito 用户池颁发带有自定义属性的访问令牌。
Amazon Cognito 用户池启动令牌生成前 Lambda 函数,该函数可自定义访问令牌和 ID 令牌。
Node.js 应用程序通过 Amazon API Gateway 进行 API 调用。
注意
此架构中显示的其他架构组件仅供参考,不在此模式的范围之内。
自动化和扩展
您可以使用AWS CloudFormation、、HashiCorp Terraform
工具
AWS 服务
Amazon API Gateway 可帮助您创建、发布、维护、监控和保护任何规模的 RES WebSocket APIs T、HTTP。
Amazon Cognito 为您的 Web 和移动应用程序提供身份验证、授权和用户管理。
Amazon Elastic Container Service(Amazon ECS)是一项快速、可扩展的容器管理服务,可帮助您运行、停止和管理集群上的容器。
AWS Lambda 是一项计算服务,可帮助您运行代码,无需预调配或管理服务器。它只在需要时运行您的代码,并自动进行扩展,因此您只需为使用的计算时间付费。
适用于 JavaScript 的 AWS SDK为提供了 JavaScript API AWS 服务。您可以使用其构建适用于 Node.js 或浏览器的库或应用程序。
其他工具
最佳实践
建议您实施以下最佳实践:
密钥和敏感数据 – 请勿在应用程序内部存储密钥或敏感数据。使用应用程序可以从中提取数据的外部系统,例如 AWS AppConfig、AWS Secrets Manager 或 AWS Systems Manager Parameter Store。
标准化部署-使用 CI/CD 管道部署应用程序。您可以使用 AWS CodeBuild 和 AWS CodePipeline 等服务。
令牌到期 - 为访问令牌设置较短的到期日期。
使用安全连接 - 客户端应用程序和后端之间的所有通信都应使用 SSL/TLS 进行加密。使用 AWS Certificate Manager (ACM) 生成和管理 SSL/TLS 证书,并使用 Amazon CloudFront 或 Elastic Load Balancing 来处理 SSL/TLS 终止问题。
验证用户输入 - 确保对所有用户输入进行验证,以防止注入攻击和其他安全漏洞。使用输入验证库以及 Amazon API Gateway 和 AWS WAF 等服务,防范常见的攻击向量。
使用 IAM 角色-使用 AWS Identity and Access Management (IAM) 角色控制对 AWS 资源的访问权限,并确保只有经过授权的用户才能访问资源。遵循最低权限原则,确保每个用户仅拥有执行其角色所需的权限。
使用密码策略 - 配置符合您的安全要求的密码策略,例如最小长度、复杂性和有效期。使用 Secrets Manag AWS Systems Manager er 或 Parameter Store 来安全地存储和管理密码。
启用多重身份验证(MFA)- 为所有用户启用 MFA,以提供额外的安全层并降低未经授权访问的风险。使用 AWS IAM Identity Center 或 Amazon Cognito 启用 MFA 及其他身份验证方法。
安全地存储敏感信息 - 使用(AWS Key Management ServiceAWS KMS)或其他加密服务,安全地存储密码、访问令牌等敏感信息。
使用强身份验证方法 - 为了提高身份验证流程的安全性,请使用生物识别身份验证或多重身份验证等强身份验证方法。
监控可疑活动 - 使用 AWS CloudTrail 及其他监控工具来监控可疑活动和潜在的安全威胁。为异常活动设置自动警报,并使用 Amazon GuardDuty 或AWS Security Hub CSPM来检测潜在威胁。
定期审查和更新安全策略 – 定期审查并更新您的安全策略和程序,确保其符合不断变化的安全要求和最佳实践。 AWS Config 用于跟踪和审核安全策略和程序的更改。
自动注册 – 请勿启用 Amazon Cognito 用户池的自动注册功能。有关更多信息,请参阅使用 Amazon Cognito 用户池降低用户注册欺诈和短信激增的风险
AWS (博客文章)。
如需了解其他最佳实践,请参阅 Amazon Cognito 文档中的 Amazon Cognito 用户池的安全最佳实践。
操作说明
| Task | 说明 | 所需技能 |
|---|---|---|
创建用户池。 |
有关如何在中设置用户池的更多信息和说明 AWS 管理控制台,请参阅用户池入门和向用户池添加更多功能和安全选项。 提示为了降低成本,请使用基础版计划或精简版计划来测试此模式。有关更多信息,请参阅 Amazon Cognito 定价 | AWS 应用程序开发人员 DevOps |
向用户池中添加用户。 | 输入以下命令,在 Amazon Cognito 用户池中创建一个用户:
| AWS 应用程序开发人员 DevOps |
向用户池中添加应用程序客户端。 |
| AWS 系统管理员、AWS 管理员、AWS DevOps、应用程序开发者 |
创建令牌生成前 Lambda 触发器。 |
| AWS DevOps,应用程序开发者 |
自定义用户池工作流。 |
有关更多信息,请参阅 Amazon Cognito 文档中的使用 Lambda 触发器自定义用户池工作流。 | AWS DevOps,应用程序开发者 |
| Task | 说明 | 所需技能 |
|---|---|---|
创建 Node.js 应用程序。 |
| 应用程序开发人员 |
实施身份验证逻辑。 |
注意您可以根据自己的用例创建自己的 TypeScript 文件或修改所提供的示例。 | 应用程序开发人员 |
配置环境变量和配置文件。 | 在终端中,输入以下命令创建环境变量:
重要请勿对密钥进行硬编码或泄露您的凭证。 | 应用程序开发人员 |
运行 应用程序。 | 输入以下命令运行应用程序,并确认其正常工作:
| 应用程序开发人员 |
确认自定义属性已注入到令牌中。 | 使用 IDE 的调试功能查看访问令牌和 ID 令牌。确认自定义属性已添加。有关示例令牌,请参阅此模式的其他信息部分。 | 应用程序开发人员 |
问题排查
| 问题 | 解决方案 |
|---|---|
尝试对用户进行身份验证时发生客户端 ID 无效错误 | 当您使用的客户端 ID 已生成客户端密钥时,通常会发生此错误。您必须创建一个不附加密钥的客户端 ID。有关更多信息,请参阅使用应用程序客户端进行应用程序特定设置。 |
相关资源
使用 Lambda 触发器自定义用户池工作流(Amazon Cognito 文档)
令牌生成前 Lambda 触发器(Amazon Cognito 文档)
CognitoIdentityProviderClient(适用于 JavaScript 的 AWS SDK 文档)
cognito-idp(文档)
AWS CLI
附加信息
示例 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 的令牌生成前触发器关联。它可以帮助您自定义 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>" }