将自定义属性发送至 Amazon Cognito 并将其注入令牌中 - AWS 规范指引

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

将自定义属性发送至 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考。

先决条件和限制

先决条件

  • 活跃的 AWS 账户

  • 创建和管理 Amazon Cognito 用户池和函数的权限 AWS Lambda

  • AWS Command Line Interface (AWS CLI),已安装配置

  • 支持 Node.js 的集成式开发环境(IDE)

  • 已安装 NodeJS 版本 18 或更高版本

  • 已安装 npm 版本 8 或更高版本

  • TypeScript,已安装

限制

  • 此模式不适用于通过客户端凭证身份验证流程进行的应用程序集成。

  • 令牌生成前触发器只能添加或更改访问令牌和身份令牌的部分属性。有关更多信息,请参阅 Amazon Cognito 文档中的令牌生成前 Lambda 触发器

架构

目标架构

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

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

下图显示了如下工作流:

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

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

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

注意

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

自动化和扩展

您可以使用AWS CloudFormation、、HashiCorp Terraform 或任何支持的基础设施即代码 (IaC) 工具,自动配置 Amazon Cognito 用户池、 AWS Lambda 函数、数据库实例和其他资源。AWS Cloud Development Kit (AWS CDK)如需扩展部署规模,请使用持续集成和持续交付(CI/CD)管道,这有助于避免手动部署可能引发的错误。

工具

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 或浏览器的库或应用程序。

其他工具

  • Node.js 是一个事件驱动的 JavaScript 运行时环境,专为构建可扩展的网络应用程序而设计。

  • npm 是在 Node.js 环境中运行的软件注册表,用于共享或借用软件包以及管理私有软件包的部署。

最佳实践

建议您实施以下最佳实践:

  • 密钥和敏感数据 – 请勿在应用程序内部存储密钥或敏感数据。使用应用程序可以从中提取数据的外部系统,例如 AWS AppConfigAWS Secrets ManagerAWS Systems Manager Parameter Store

  • 标准化部署-使用 CI/CD 管道部署应用程序。您可以使用 AWS CodeBuildAWS CodePipeline 等服务。

  • 令牌到期 - 为访问令牌设置较短的到期日期。

  • 使用安全连接 - 客户端应用程序和后端之间的所有通信都应使用 SSL/TLS 进行加密。使用 AWS Certificate Manager (ACM) 生成和管理 SSL/TLS 证书,并使用 Amazon CloudFrontElastic 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 GuardDutyAWS Security Hub CSPM来检测潜在威胁。

  • 定期审查和更新安全策略 – 定期审查并更新您的安全策略和程序,确保其符合不断变化的安全要求和最佳实践。 AWS Config 用于跟踪和审核安全策略和程序的更改。

  • 自动注册 – 请勿启用 Amazon Cognito 用户池的自动注册功能。有关更多信息,请参阅使用 Amazon Cognito 用户池降低用户注册欺诈和短信激增的风险AWS (博客文章)。

如需了解其他最佳实践,请参阅 Amazon Cognito 文档中的 Amazon Cognito 用户池的安全最佳实践

操作说明

Task说明所需技能

创建用户池。

  1. 在 CLI 终端中,输入以下命令创建 Amazon Cognito 用户池:

    aws cognito-idp create-user-pool \ --pool-name <MyUserPool>
  2. 输入以下命令来创建客户端 ID 和密钥,以便与 SDK 一起使用 JavaScript:

    aws cognito-idp create-user-pool-client \ --user-pool-id <UserPoolID> \ --client-name <MyNewClient> \ --no-generate-secret \ --explicit-auth-flows "USER_PASSWORD_AUTH" "ADMIN_NO_SRP_AUTH"

有关如何在中设置用户池的更多信息和说明 AWS 管理控制台,请参阅用户池入门和向用户池添加更多功能和安全选项

提示

为了降低成本,请使用基础版计划或精简版计划来测试此模式。有关更多信息,请参阅 Amazon Cognito 定价

AWS 应用程序开发人员 DevOps

向用户池中添加用户。

输入以下命令,在 Amazon Cognito 用户池中创建一个用户:

aws cognito-idp sign-up \ --client-id <ClientID> \ --username <jane@example.com> \ --password <PASSWORD> \ --user-attributes Name="email",Value="<jane@example.com>" Name="name",Value="<Jane>"
AWS 应用程序开发人员 DevOps

向用户池中添加应用程序客户端。

  1. 导航到 Amazon Cognito 控制台

  2. 选择用户池

  3. 选择您之前创建的用户池。

  4. 定义您的应用程序下,选择最适合您要为其创建身份验证和授权服务的应用场景的应用程序类型

  5. 命名您的应用程序中,输入描述性名称或继续使用默认名称。

  6. 添加返回 URL 下,输入用户完成身份验证后的应用程序重定向路径。

  7. 选择创建应用程序。创建应用程序客户端后,您可以设置高级选项。

AWS 系统管理员、AWS 管理员、AWS DevOps、应用程序开发者

创建令牌生成前 Lambda 触发器。

  1. 打开 Lamba 控制台的 Functions page(函数页面)。

  2. 选择 Create function(创建函数)。

  3. 选择从头开始编写

  4. 基本信息窗格中,为函数名称输入 myPreTokenGenerationLambdaFunction

  5. 运行时中,选择 Node.js 22

  6. 架构设置为 x86_64

  7. 选择创建函数

  8. 代码选项卡上的代码源下,输入此模式的其他资源部分中的示例 Lambda 函数。此 Lambda 函数会将自定义属性注入到 ID 令牌和访问令牌中。

AWS DevOps,应用程序开发者

自定义用户池工作流。

  1. 导航到 Amazon Cognito 控制台

  2. 选择用户池

  3. 选择您之前创建的用户池。

  4. 在左侧导航窗格的身份验证下,选择扩展

  5. 选择添加 Lambda 触发器

  6. 添加或编辑令牌生成前触发器

  7. 对于触发器类型,选择身份验证

  8. 身份验证下,选择令牌生成前触发器

  9. 对于分配 Lambda 函数,选择 myPreTokenGenerationLambdaFunction

  10. 选择添加 Lambda 触发器

  11. 基本功能 + 面向用户身份的访问令牌自定义设置基本功能 + 面向用户和机器身份的访问令牌自定义设置中,选择触发事件版本。此设置会更新 Amazon Cognito 发送给您的函数的请求参数,使该函数包含用于自定义访问令牌的字段。

有关更多信息,请参阅 Amazon Cognito 文档中的使用 Lambda 触发器自定义用户池工作流

AWS DevOps,应用程序开发者
Task说明所需技能

创建 Node.js 应用程序。

  1. 在终端中,输入以下命令:

    mkdir my-app cd my-app npm init -y npm install --save-dev typescript npm i --save-dev @types/node npm i amazon-cognito-identity-js npx tsc --init mkdir src touch src/index.ts
  2. 修改package.json文件以包含编 TypeScript 译脚本:

    "scripts": { "start": "node src/index.js", "build": "tsc" },
应用程序开发人员

实施身份验证逻辑。

  1. 打开 index.ts 文件。

  2. 粘贴此模式的其他资源部分中提供的示例 Typescript。

  3. 保存并关闭 index.ts 文件。

注意

您可以根据自己的用例创建自己的 TypeScript 文件或修改所提供的示例。

应用程序开发人员

配置环境变量和配置文件。

在终端中,输入以下命令创建环境变量:

export USERNAME="<COGNITO_USER_NAME>" export PASSWORD="<COGNITO_USER_PASSWORD>" export USER_POOL_ID="<COGNITO_USER_ID>" export CLIENT_ID="<COGNITO_CLIENT_ID>"
重要

请勿对密钥进行硬编码或泄露您的凭证。

应用程序开发人员

运行 应用程序。

输入以下命令运行应用程序,并确认其正常工作:

npm run build npm start
应用程序开发人员

确认自定义属性已注入到令牌中。

使用 IDE 的调试功能查看访问令牌和 ID 令牌。确认自定义属性已添加。有关示例令牌,请参阅此模式的其他信息部分。

应用程序开发人员

问题排查

问题解决方案

尝试对用户进行身份验证时发生客户端 ID 无效错误

当您使用的客户端 ID 已生成客户端密钥时,通常会发生此错误。您必须创建一个不附加密钥的客户端 ID。有关更多信息,请参阅使用应用程序客户端进行应用程序特定设置

相关资源

附加信息

示例 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>" }