自定义身份验证质询 Lambda 触发器
在为 Amazon Cognito 用户池构建身份验证流程时,您可能会发现需要在内置流程的基础上对身份验证模型进行扩展。自定义质询触发器的一个常见使用场景是在用户名、密码和多重身份验证(MFA)之外实施额外的安全检查。自定义质询是您可以使用 Lambda 支持的编程语言生成的任何问题和回答。例如,在允许用户进行身份验证之前,您可能希望要求用户先破解验证码或回答安全问题。另一个潜在的需求是与专门的身份验证因素或设备集成。或者,您可能已经开发了使用硬件安全密钥或生物识别设备对用户进行身份验证的软件。自定义质询的身份验证成功的定义是,您的 Lambda 函数接受为正确的答案:例如,固定字符串或来自外部 API 的满意响应。
您可以使用自定义质询开始身份验证并完全控制身份验证过程,也可以在应用程序收到自定义质询之前执行用户名和密码身份验证。
自定义身份验证质询 Lambda 触发器:
这三个 Lambda 函数链接在一起,呈现出一种完全由您控制且由您自己设计的身份验证机制。由于自定义身份验证需要在您的客户端和 Lambda 函数中使用应用程序逻辑,因此您无法在托管登录中处理自定义身份验证。此身份验证系统需要开发人员付出额外的努力。您的应用程序必须使用用户池 API 执行身份验证流程,并使用定制登录界面处理由此产生的质询,该界面可在自定义身份验证质询的中心呈现问题。
有关实施自定义身份验证的更多信息,请参阅自定义身份验证流程和质询。
API 操作 InitiateAuth 或 AdminInitiateAuth 与 RespondToAuthChallenge 或 AdminRespondToAuthChallenge 之间的身份验证。在此流程中,用户通过回答连续的质询进行身份验证,直到身份验证失败或用户获得令牌。质询回应可能是一个新的挑战。在这种情况下,您的应用程序会根据需要多次响应新的质询。当定义身份验证质询函数分析到目前为止的结果时,确定所有质询都已回答并返回 IssueTokens 时,身份验证就会成功。
自定义质询流程中的 SRP 身份验证
您可以让 Amazon Cognito 在发出自定义质询之前验证用户密码。当您在自定义质询流程中执行 SRP 身份验证时,请求频率限额身份验证类别中关联的任何 Lambda 触发器都将运行。过程概述如下:
-
您的应用程序使用
AuthParameters映射来调用InitiateAuth或AdminInitiateAuth,以此来启动登录。参数必须包括CHALLENGE_NAME: SRP_A,以及SRP_A和USERNAME的值。 -
Amazon Cognito 使用包含
challengeName: SRP_A和challengeResult: true的初始会话,调用您定义的身份验证质询 Lambda 触发器。 -
在收到这些输入后,您的 Lambda 函数发出
challengeName: PASSWORD_VERIFIER、issueTokens: false、failAuthentication: false响应。 -
如果密码验证成功,Amazon Cognito 会使用包含
challengeName: PASSWORD_VERIFIER和challengeResult: true的新会话再次调用您的 Lambda 函数。 -
为了启动您的自定义质询,Lambda 函数发出
challengeName: CUSTOM_CHALLENGE、issueTokens: false和failAuthentication: false响应。如果您不想启动包含密码验证的自定义身份验证流程,可以使用AuthParameters映射(包括CHALLENGE_NAME: CUSTOM_CHALLENGE)启动登录。 -
质询循环将一直重复到所有质询得到应答。
以下在使用 SRP 流进行自定义身份验证之前的起始 InitiateAuth 请求的示例。
{ "AuthFlow": "CUSTOM_AUTH", "ClientId": "1example23456789", "AuthParameters": { "CHALLENGE_NAME": "SRP_A", "USERNAME": "testuser", "SRP_A": "[SRP_A]", "SECRET_HASH": "[secret hash]" } }
在自定义身份验证 SRP 流程中重置密码
当用户处于 FORCE_CHANGE_PASSWORD 状态时,您的自定义身份验证流程必须集成密码更改步骤,同时保持身份验证质询的完整性。Amazon Cognito 会在 NEW_PASSWORD_REQUIRED 质询期间调用您的定义身份验证质询 Lambda 触发器。在这种情况下,使用自定义质询流程和 SRP 身份验证登录的用户如果处于密码重置状态,则可以设置新密码。
当用户处于 RESET_REQUIRED 或 FORCE_CHANGE_PASSWORD 状态时,他们必须使用 NEW_PASSWORD 来回应 NEW_PASSWORD_REQUIRED 质询。在使用 SRP 的自定义身份验证中,Amazon Cognito 会在用户完成 SRP PASSWORD_VERIFIER 质询后返回一个 NEW_PASSWORD_REQUIRED 质询。您的“定义身份验证质询”触发器会收到 session 数组中的两个质询结果,并可在用户成功更改密码后继续执行额外的自定义质询。
您的“定义身份验证质询”Lambda 触发器必须通过 SRP 身份验证、密码重置和随后的自定义质询来管理质询序列。该触发器会在 session 参数中收到一个已完成质询的数组,其中包括 PASSWORD_VERIFIER 和 NEW_PASSWORD_REQUIRED 的结果。如需了解实现示例,请参阅定义身份验证质询示例。
身份验证流程步骤
对于需要在自定义质询之前验证密码的用户,该过程遵循以下步骤:
-
您的应用程序使用
AuthParameters映射来调用InitiateAuth或AdminInitiateAuth,以此来启动登录。参数必须包括CHALLENGE_NAME: SRP_A,以及SRP_A和USERNAME的值。 -
Amazon Cognito 使用包含
challengeName: SRP_A和challengeResult: true的初始会话,调用您定义的身份验证质询 Lambda 触发器。 -
在收到这些输入后,您的 Lambda 函数发出
challengeName: PASSWORD_VERIFIER、issueTokens: false、failAuthentication: false响应。 -
如果密码验证成功,则会发生以下两种情况之一:
- 对于处于正常状态的用户:
-
Amazon Cognito 会使用包含
challengeName: PASSWORD_VERIFIER和challengeResult: true的新会话再次调用您的 Lambda 函数。为了启动您的自定义质询,Lambda 函数发出
challengeName: CUSTOM_CHALLENGE、issueTokens: false和failAuthentication: false响应。 - 对于处于
RESET_REQUIRED或FORCE_CHANGE_PASSWORD状态的用户: -
Amazon Cognito 会使用包含
challengeName: PASSWORD_VERIFIER和challengeResult: true的会话调用您的 Lambda 函数。您的 Lambda 函数应该使用
challengeName: NEW_PASSWORD_REQUIRED、issueTokens: false和failAuthentication: false作出响应。成功更改密码后,Amazon Cognito 会使用包含
PASSWORD_VERIFIER和NEW_PASSWORD_REQUIRED的会话调用您的 Lambda 函数。为了启动您的自定义质询,Lambda 函数发出
challengeName: CUSTOM_CHALLENGE、issueTokens: false和failAuthentication: false响应。
-
质询循环将一直重复到所有质询得到应答。
如果您不想启动包含密码验证的自定义身份验证流程,可以使用 AuthParameters 映射(包括 CHALLENGE_NAME: CUSTOM_CHALLENGE)启动登录。
会话管理
身份验证流程通过一系列会话 ID 和质询结果来维持会话的连续性。每个质询响应都会生成一个新的会话 ID,以防止会话重用错误,这对于多重身份验证流程尤其重要。
质询结果按时间顺序存储在您的 Lambda 触发器接收的会话数组中。对于处于 FORCE_CHANGE_PASSWORD 状态的用户,该会话数组包含:
session[0]- 最初的SRP_A质询session[1]-PASSWORD_VERIFIER的结果session[2]-NEW_PASSWORD_REQUIRED的结果后续要素 - 其他自定义质询的结果
身份验证流程示例
以下示例展示了一个完整的自定义身份验证流程,在该流程中,一个处于 FORCE_CHANGE_PASSWORD 状态的用户必须完成密码更改和自定义 CAPTCHA 质询。
-
InitiateAuth 请求
{ "AuthFlow": "CUSTOM_AUTH", "ClientId": "1example23456789", "AuthParameters": { "CHALLENGE_NAME": "SRP_A", "USERNAME": "testuser", "SRP_A": "[SRP_A]" } } -
InitiateAuth 响应
{ "ChallengeName": "PASSWORD_VERIFIER", "ChallengeParameters": { "USER_ID_FOR_SRP": "testuser" }, "Session": "[session_id_1]" } -
包含
PASSWORD_VERIFIER的 RespondToAuthChallenge 请求{ "ChallengeName": "PASSWORD_VERIFIER", "ClientId": "1example23456789", "ChallengeResponses": { "PASSWORD_CLAIM_SIGNATURE": "[claim_signature]", "PASSWORD_CLAIM_SECRET_BLOCK": "[secret_block]", "TIMESTAMP": "[timestamp]", "USERNAME": "testuser" }, "Session": "[session_id_1]" } -
包含
NEW_PASSWORD_REQUIRED质询的 RespondToAuthChallenge 响应{ "ChallengeName": "NEW_PASSWORD_REQUIRED", "ChallengeParameters": {}, "Session": "[session_id_2]" } -
包含
NEW_PASSWORD_REQUIRED的 RespondToAuthChallenge 请求{ "ChallengeName": "NEW_PASSWORD_REQUIRED", "ClientId": "1example23456789", "ChallengeResponses": { "NEW_PASSWORD": "[password]", "USERNAME": "testuser" }, "Session": "[session_id_2]" } -
包含 CAPTCHA 自定义质询的 RespondToAuthChallenge 响应
{ "ChallengeName": "CUSTOM_CHALLENGE", "ChallengeParameters": { "captchaUrl": "url/123.jpg" }, "Session": "[session_id_3]" } -
包含对 CAPTCHA 自定义质询的回答的 RespondToAuthChallenge 请求
{ "ChallengeName": "CUSTOM_CHALLENGE", "ClientId": "1example23456789", "ChallengeResponses": { "ANSWER": "123", "USERNAME": "testuser" }, "Session": "[session_id_3]" }
6. 最终成功响应
{ "AuthenticationResult": { "AccessToken": "eyJra456defEXAMPLE", "ExpiresIn": 3600, "IdToken": "eyJra789ghiEXAMPLE", "RefreshToken": "eyJjd123abcEXAMPLE", "TokenType": "Bearer" }, "ChallengeParameters": {} }