如何允许Cognito用户池用户选择手机号或邮箱重置密码?
这个问题问得好!咱们先理清Cognito原生支持的能力,再说说实现用户自主选择OTP渠道的可行方案:
Cognito原生服务的限制
首先明确:原生Cognito并不支持让用户自主选择OTP接收渠道。当你调用前端SDK的cognitoUser.forgotPassword()时,Cognito会严格遵循用户池的配置逻辑:
- 如果用户池设置为“仅邮箱”或“仅手机号”接收验证码,Cognito只会往对应的已验证渠道发OTP;
- 如果用户池设置为“邮箱或手机号”,Cognito会自动选择用户已验证的其中一个渠道(通常是用户池配置的优先顺序,比如先手机号后邮箱),但不会给用户提供选择入口。
自定义解决方案(推荐实现)
要让用户在双渠道都验证的情况下自主选择OTP接收方式,需要结合前端交互、后端API调用和Cognito的Lambda触发器来实现,具体步骤如下:
1. 前端交互逻辑
首先要确认用户是否同时验证了邮箱和手机号,再展示渠道选择界面:
// 获取当前用户的属性,判断是否双渠道已验证 cognitoUser.getUserAttributes((err, attributes) => { if (err) { console.error('获取用户属性失败:', err); return; } const isEmailVerified = attributes.find(attr => attr.Name === 'email_verified')?.Value === 'true'; const isPhoneVerified = attributes.find(attr => attr.Name === 'phone_number_verified')?.Value === 'true'; if (isEmailVerified && isPhoneVerified) { // 展示渠道选择弹窗/组件,让用户选邮箱或手机号 showChannelSelectionUI(); } else { // 单渠道已验证,直接调用原生重置密码方法 cognitoUser.forgotPassword({ onSuccess: (result) => console.log('OTP发送成功:', result), onFailure: (err) => console.error('重置密码失败:', err) }); } }); // 用户选择渠道后,调用后端自定义接口 function handleChannelSelect(selectedChannel) { fetch('/api/initiate-reset-password', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: cognitoUser.username, preferredChannel: selectedChannel // 可选值: 'email' 或 'phone' }) }) .then(res => res.json()) .then(data => console.log('OTP已发送至选择的渠道')) .catch(err => console.error('请求失败:', err)); }
2. 后端API调用Cognito Admin接口
不能直接用前端的forgotPassword(),而是需要后端调用Cognito的AdminResetUserPassword API,并通过ClientMetadata传递用户选择的渠道偏好:
// Node.js后端示例(使用AWS SDK v3) const { CognitoIdentityServiceClient, AdminResetUserPasswordCommand } = require("@aws-sdk/client-cognito-identity-service-provider"); const client = new CognitoIdentityServiceClient({ region: '你的区域' }); exports.initiateResetPassword = async (req, res) => { const { username, preferredChannel } = req.body; const command = new AdminResetUserPasswordCommand({ UserPoolId: process.env.COGNITO_USER_POOL_ID, Username: username, ClientMetadata: { preferred_channel: preferredChannel } }); try { await client.send(command); res.status(200).json({ message: 'OTP已发送至你选择的渠道' }); } catch (error) { res.status(500).json({ error: error.message }); } };
3. 配置Cognito自定义消息Lambda触发器
通过Custom Message触发器,在Cognito发送OTP前拦截请求,根据ClientMetadata中的渠道偏好,只往用户选择的渠道发送验证码:
// Custom Message Lambda函数示例 exports.handler = async (event) => { // 仅处理重置密码的消息触发 if (event.triggerSource !== 'CustomMessage_ForgotPassword') { return event; } const { clientMetadata, userAttributes, request } = event; const preferredChannel = clientMetadata?.preferred_channel; // 验证渠道有效性(确保用户确实验证了该渠道) if (preferredChannel === 'email' && userAttributes.email_verified === 'true') { // 只发送到邮箱,清空手机号相关配置 event.response.emailMessage = `你的重置密码验证码是:${request.codeParameter}`; event.response.emailSubject = '重置你的账户密码'; delete event.response.smsMessage; } else if (preferredChannel === 'phone' && userAttributes.phone_number_verified === 'true') { // 只发送到手机号,清空邮箱相关配置 event.response.smsMessage = `你的重置密码验证码是:${request.codeParameter}`; delete event.response.emailMessage; delete event.response.emailSubject; } return event; };
注意事项
- 确保用户池配置允许邮箱和手机号作为登录/验证渠道,且用户的
email_verified和phone_number_verified属性均为true; - 后端API需要拥有
cognito-idp:AdminResetUserPassword的IAM权限; - Lambda触发器需要配置正确的触发源(Custom Message),并拥有访问Cognito用户属性的权限。
内容的提问来源于stack exchange,提问作者Rohit Gupta
相关产品推荐
相关产品推荐

