如何构建身份和访问管理(IAM)服务 part3
导语:在本文中,我们探讨了开发身份和访问管理服务的几种方法之间的差异。
3. 使用 AWS Cognito 构建基于云的 IAM 服务
AWS Cognito是基于云的身份和访问管理服务的另一个示例,可帮助企业避免从头开始创建自托管 IAM 解决方案。AWS Cognito 提供简单、安全的用户注册、访问控制和用户身份管理。
该服务的用户规模高达 4000 万,并支持通过 Facebook 和 Google 等社交帐户进行身份验证。在 AWS Cognito 中,身份通过两种类型的池进行管理:
用户池是指逻辑企业/部门等用户目录。
身份池。如果您需要调整RBAC功能,则创建身份池至关重要。这些池为登录用户提供对其他 AWS 资源的访问。
用户池提供以下功能:
登记
用户组
登录/注销
找回密码
艺术硕士
要做的第一件事是设置 AWS Cognito 进行集成:
1. 创建用户池。我们不会详细介绍如何创建池,因为该过程非常直观。这有点像参加一个测验,要求您根据需要选择不同的池选项。
因此,我们只概述最重要的设置:
登录需要什么数据
令牌中应包含哪些用户属性
注册密码规则
是否需要MFA
是否需要验证用户的手机号码或邮箱
假设我们创建了一个名为 Developers 的池,其中:
用户可以使用电子邮件地址登录
应验证电子邮件地址
用户的个人资料将包含用户的名字和姓氏、电子邮件和电话号码
密码策略要求至少 8 个字符
通过短信启用 MFA
2. 创建作为身份验证过程一部分的应用程序客户端。在创建过程中,我们会被要求自定义令牌的生存时间 (TTL)。为此,请转到常规设置->应用程序客户端->添加应用程序客户端。
之后,我们将看到应用程序集成选项。这是我们接下来需要做的:
转到应用程序集成-> 应用程序客户端设置并设置回调 URL(这是我们的 API 服务器,它将在身份验证过程中接收授权代码)并启用授权代码授予选项。在允许的 OAuth 范围部分中,选择email和openid。
转到应用程序集成->域名并提供我们的域名。该名称将成为身份验证链接的基本 URL 的一部分。该名称在 AWS Cognito 空间中应该是唯一的。
经过这些步骤后,我们最终得到以下结果:
应用程序中用于身份验证的基本 URL,例如 :
客户端ID/客户端秘密
用户池标识符
重定向 URL,例如用于接收授权码
3.1. 验证
在这里,我们有两个选项来管理身份验证:
使用自托管 UI 表单并通过amazon-cognito-identity-js或aws-amplify手动管理所有 API 调用。这可以完全无服务器地完成,但我们不会在这里展示如何做到这一点,因为这是一个复杂的主题,需要单独的文章。
使用 Cognito 托管的 UI 表单并通过纯 HTTP 调用 Cognito
AWS Cognito 支持各种类型的身份验证流程,例如具有不同授权类型的普通OAuth和OIDC(例如 Google)。
让我们看一下使用授权代码授予类型通过 OAuth 进行身份验证。这种情况下的典型登录工作流程看起来与 Auth0 部分中描述的相同,只是登录表单是从 AWS Cognito 加载的。
让我们仔细看看如何使用 Cognito 托管的 UI 和纯 HTTP:
1. Web 服务器向 AWS Cognito 请求登录页面:
该登录页面包含以下内容:
BASEURL=iamtest.auth.us-east-1.amazoncognito.com,其中包括 Cognito 托管的页面。可以在 UI 定制部分对页面进行定制。我们将使用默认页面。
/login登录端点
client_id=7ou2226v1vvlp5obm34k0omt39,这 是我们的公共应用程序 ID
response_type=code,这 是将发送到 API 服务器以换取令牌的 OAuth 授予类型授权代码
scope=email+openid,这是一个仅限于此范围的访问令牌
redirect_uri=https://example.com,这是一个自托管资源服务器,将接收授权码
2. 用户向网站提供凭据,单击登录,然后提供 OTP 代码(如果启用了 MFA)。redirect_uri然后,AWS Cognito使用授权代码将用户重定向到:
https://example.com/?code=13fc34f7-5334-46e6-a77a-75fb52ab37b3
3. API 服务器如何用代码交换令牌:
const res = await fetch(`https://iamtest.auth.us-east-1.amazoncognito.com/oauth2/token`, { method: "POST", headers: new Headers({"content-type": "application/x-www-form-urlencoded"}), body: Object.entries({ "grant_type": "authorization_code", "client_id": clientId, "code": auth_code, "redirect_uri": redirect_uri, }).map(([k, v]) => `${k}=${v}`).join("&"), }); if (!res.ok) { console.log(await res.json()); } const tokens = await res.json();
结果响应中返回的 JSON 具有以下键:
id_token – 有效的用户池 ID 令牌。仅当请求 openID 范围时才提供 ID 令牌。
access_token – 有效的用户池访问令牌。
Refresh_token – 用于获取额外访问令牌的特殊令牌。
4. API服务器重定向到注销页面:
https://iamtest.auth.us-east-1.amazoncognito.com/logout?response_type=code&client_id=7ou2226v1vvlp5obm34k0omt39&redirect_uri=https://example.com
现在,我们来看看如何使用该 SDK :
import { Auth } from 'aws-amplify'; /*1. Login initiated*/ async function initSignIn() { try { const user = await Auth.signIn(username, password); // todo: cache user if ( // since we enabled SMS only user.challengeName === 'SMS_MFA' ) { throw new Error MFARequired(user.challengeName); } else { // The user signs in without challenge console.log(user); } } catch (err) { if (err.code === 'UserNotConfirmedException') { // User is not confirmed } else if (err.code === 'NotAuthorizedException') { // incorrect password } else if (err.code === 'UserNotFoundException') { // User does not exist in Cognito pool } else { console.log(err); } } /*2. User provides code through UI*/ /*Login confirmed*/ async function confirmSignIn(code) { try { // get user from cache const loggedUser = await Auth.confirmSignIn( user, // object from Auth.signIn() code, // Confirmation code 'SMS_MFA' ); } catch (err) { console.log(err); }
3.2. 授权
有很多有用的库可以通过 AWS Cognito 管理授权。要在我们自己的资源服务器上授权用户,我们可以使用 aws-jwt-verify 库。我们需要提供应用程序和用户池标识符来验证访问令牌。
此外,我们可以完全依赖 aws-jwt-verify 库,因为它将代表我们执行以下检查:
自动从https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json 获取 .well-known/jwks.json并使用与“匹配”的密钥验证签名访问令牌标头中的“kid”部分
检查令牌是否过期
将有效负载中的aud声明与绑定到用户池的应用程序 ID进行比较
以下是使用 AWS Cognito 启用授权机制的方法:
3.3. RBAC
AWS Cognito 中基于角色的访问控制功能通过 IAM 角色/策略发挥作用。您可以使用 RBAC 仅控制对根账户下配置的 AWS 资源(例如 S3 存储桶和 DynamoDB)的访问。允许或拒绝其他 AWS 用户使用由配置基于 Cognito 的 IAM 解决方案的同一账户配置的 AWS 服务的场景不属于本文的讨论范围。
但是,如果我们需要定义一组逻辑权限,则只能部分通过 Cognito 组来实现。我们可以在池设置中创建组并将用户分配到每个组。为此,我们需要在 AWS 控制台中选择我们的池,然后转到“用户和组” → “创建组” → “添加用户”。
然后,我们的资源服务器将从名为cognito:groups的访问令牌声明接收用户组,并将每个组映射到某些权限集。不幸的是,无法通过 Cognito 定义自定义权限。资源服务器必须自行定义它们并将它们映射到组。
3.4. 暴力破解和机器人保护
AWS Cognito 提供的安全措施包括失败的登录尝试和需要授权计数的其他操作。一旦达到此类计数的限制,安全机制将阻止某些操作一段时间。这称为锁定,随着登录尝试失败次数的增加,锁定时间从 1 秒呈指数增长到 15 分钟。
不幸的是,AWS Cognito 提供的这些本机检查无法自定义。但您可以通过 AWS Lambda 使用预身份验证触发器添加自己的策略。
3.5. 审核日志
AWS Cognito 中的日志记录和监控可以通过将 AWS CloudTrail 与 AWS CloudWatch 相结合来实现。
AWS CloudTrail 允许您通过捕获 API 调用并将其报告为事件来跟踪用户、角色和 AWS 服务所执行的操作。然后,您可以在 CloudWatch 中激活特定 CloudTrail 事件的警报。
3.6. 备份可能性
通过 Cognito 用户配置文件导出功能可以部分备份用户池。此功能使用 AWS Step Function 和 DynamoDB 定期将用户和组从用户池导出到应在与用户池相同的区域中启用的 DynamoDB 表。
此类解决方案不会导出敏感数据,例如密码、启用了 MFA 的用户池以及附加到 Cognito 组的 IAM 角色。
选择哪种开发方案:结果比较
现在我们已经探索了构建 IAM 服务的所有三种方法,让我们总结一下 IAM 服务比较的结果。
对于相对简单且廉价的身份和访问管理解决方案以及需要控制系统工作方式各个方面的用户来说,基于 OIH 的自定义本地 IAM 解决方案是一个不错的选择。
但在开发这样的系统时,您必须关注源代码支持以及管理后端基础设施。此类解决方案的所有方面都应由实施和支持该解决方案的工程师控制。
在云提供商上构建 IAM 解决方案可以帮助您避免物理维护成本,同时确保分布式和冗余系统的正常运行时间。
AWS Cognito 和 Auth0 都提供了更广泛的可能性来满足多种需求,提供以最小的努力进行扩展的能力,并允许您专注于您需要什么而不是如何做。
此外,它们还提供精美的 UI 来方便地设置 IAM 解决方案的许多方面,并提供注册和登录表单等现成的用户界面,从而帮助您加速开发。
让我们将身份和访问管理服务开发方法的比较结果放在表格中,以查看最重要标准之间的差异:
注意:在 AWS 管理面板中创建 AWS Cognito 用户池时设置选项后,这些选项将被禁用并且无法轻松更改。如果需要进行更改,您应该删除整个实例并创建一个新实例,这可能会耗费资源和时间。
结论
选择构建自定义还是基于云的 IAM 解决方案可能具有挑战性。为了提供高效、安全的身份和访问管理解决方案,您需要清楚地了解将使用该解决方案的企业的目标和细微差别。了解现有的开发方法、它们提供的机会及其优缺点也很重要。
发表评论