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

能否让用户选择通过邮箱或SMS接收AWS Cognito验证码?

实现AWS Cognito邮箱/SMS验证码二选一验证方案

前提准备

  • 确保Cognito用户池已开启邮箱和短信两种验证方式,且用户注册时已收集邮箱和手机号信息(或支持后续补充)
  • 配置Amplify Auth模块时,启用对应验证通道

核心实现步骤

1. 配置自定义认证流程(Lambda触发器)

Cognito默认按预设优先级发送验证码,要实现用户自主选择,需通过三个Lambda触发器干预流程:

  • Define Auth Challenge:判断认证阶段,首次请求时返回「选择验证通道」的挑战;用户选择通道后,触发「验证码验证」挑战;最后验证验证码是否正确,决定是否发放令牌
  • Create Auth Challenge:根据用户选择的通道(邮箱/SMS)生成验证码,调用Cognito API发送,并临时存储验证码用于后续校验
  • Verify Auth Challenge Response:比对用户输入的验证码与存储的验证码,返回验证结果

2. React前端交互实现

在登录/验证页面添加通道选择UI,配合Amplify Auth API完成流程:

  • 用户输入账号后调用Auth.signIn(username)触发初始认证
  • 收到Cognito返回的SELECT_CHALLENGE_CHANNEL挑战时,展示邮箱/SMS选择组件
  • 用户选择通道后,调用Auth.sendCustomChallengeAnswer(user, selectedChannel)传递选择给Lambda
  • 等待用户输入验证码后,再次调用Auth.sendCustomChallengeAnswer(user, verificationCode)完成验证

代码示例

React前端代码

import { Auth } from 'aws-amplify';

const handleSignIn = async (username) => {
  try {
    const user = await Auth.signIn(username);
    // 判断是否需要选择验证通道
    if (user.challengeName === 'SELECT_CHALLENGE_CHANNEL') {
      // 自定义方法:展示通道选择弹窗,获取用户选择
      const selectedChannel = await showChannelSelectModal();
      await Auth.sendCustomChallengeAnswer(user, selectedChannel);
      
      // 自定义方法:展示验证码输入框,获取用户输入
      const verificationCode = await showVerificationCodeInput();
      const result = await Auth.sendCustomChallengeAnswer(user, verificationCode);
      console.log('登录成功', result);
    }
  } catch (err) {
    console.error('认证失败', err);
  }
};

Define Auth Challenge Lambda触发器

exports.handler = async (event) => {
  const { request, response } = event;

  if (request.session.length === 0) {
    // 首次认证:要求用户选择验证通道
    response.challengeName = 'SELECT_CHALLENGE_CHANNEL';
    response.challengeParameters = { username: request.userName };
    response.failAuthentication = false;
    response.issueTokens = false;
  } else if (request.session.length === 1 && request.challengeAnswer) {
    // 用户已选通道:发起验证码验证挑战
    response.challengeName = 'VERIFY_CODE';
    response.challengeParameters = {
      selectedChannel: request.challengeAnswer,
      username: request.userName
    };
    response.failAuthentication = false;
    response.issueTokens = false;
  } else if (request.session.length === 2 && request.challengeAnswer) {
    // 验证验证码:需结合Create Auth Challenge中存储的验证码判断
    const isValid = verifyCode(request.userName, request.challengeAnswer);
    response.issueTokens = isValid;
    response.failAuthentication = !isValid;
  }

  return event;
};

Create Auth Challenge Lambda触发器

const AWS = require('aws-sdk');
const cognito = new AWS.CognitoIdentityServiceProvider();

exports.handler = async (event) => {
  const { request, response } = event;

  if (request.challengeName === 'VERIFY_CODE') {
    const { selectedChannel, username } = request.challengeParameters;
    const verificationCode = Math.floor(100000 + Math.random() * 900000).toString();
    
    // 自定义方法:将验证码存储到DynamoDB或用户临时属性
    await storeVerificationCode(username, verificationCode);

    // 根据选择的通道发送验证码
    if (selectedChannel === 'email') {
      await cognito.adminUpdateUserAttributes({
        UserPoolId: event.userPoolId,
        Username: username,
        UserAttributes: [{ Name: 'email_verified', Value: 'false' }]
      }).promise();
      await cognito.adminSendUserConfirmationCode({
        UserPoolId: event.userPoolId,
        Username: username,
        ClientId: event.callerContext.clientId,
        UsernameAttribute: 'email'
      }).promise();
    } else if (selectedChannel === 'sms') {
      await cognito.adminUpdateUserAttributes({
        UserPoolId: event.userPoolId,
        Username: username,
        UserAttributes: [{ Name: 'phone_number_verified', Value: 'false' }]
      }).promise();
      await cognito.adminSendUserConfirmationCode({
        UserPoolId: event.userPoolId,
        Username: username,
        ClientId: event.callerContext.clientId,
        UsernameAttribute: 'phone_number'
      }).promise();
    }

    response.privateChallengeParameters = { verificationCode };
    response.challengeMetadata = 'CODE_VERIFICATION_CHALLENGE';
  }

  return event;
};

注意事项

  • 确保Lambda触发器拥有调用Cognito API、访问验证码存储服务(如DynamoDB)的权限
  • 用户必须已在Cognito中留存邮箱和手机号,否则无法选择对应通道
  • 可在注册流程中强制用户填写双渠道信息,或提供信息补充入口

内容的提问来源于stack exchange,提问作者Paúl

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 14:36:11