You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何允许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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 21:13:13