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

如何配置Amazon Cognito用户池支持邮箱/手机号作为用户名登录

解决Cognito用户池允许邮箱+姓名或手机号+姓名注册登录的问题

问题描述

当前SAM模板配置下,用户注册时被要求同时提供邮箱和手机号,但实际需求是允许用户仅使用邮箱+姓名或仅使用手机号+姓名完成注册与登录流程。

问题根源

虽然UserPool的Schema中phone_number和email的Required已设为false,但UsernameAttributes同时包含了这两个字段。Cognito默认不会自动校验"二选一"的逻辑,导致注册时可能出现需要同时提供两者的情况,或者用户仅提供一个时无法通过校验。

解决方案

需要通过自定义PreSignUp Lambda触发器实现"邮箱/手机号二选一"的校验逻辑,同时确保用户池配置适配这种场景。

1. 修改SAM模板配置

原有UserPool的Schema和UsernameAttributes配置无需修改,但需要确保PreSignUp Lambda触发器正确关联,并补充Lambda资源定义。以下是完整修改后的SAM模板:

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
  Passwordless SMS/Email authentication backend using Amazon Cognito User Pools CUSTOM AUTH challenge flow w/ AWS Lambda triggers

Metadata:
  AWS::ServerlessRepo::Application:
    Name: passwordless-sms-email-auth 
    Description: >
      Passwordless SMS/Email authentication backend using Amazon Cognito User Pools CUSTOM AUTH challenge flow w/ AWS Lambda triggers
    SpdxLicenseId: MIT
    LicenseUrl: LICENSE
    Labels: ['passwordless', 'authentication', 'cognito', 'auth', 'sms', 'email', 'iOS', 'mobile', 'serverless', 'amplify']
    SemanticVersion: 1.14.20

Globals:
  Function:
    Timeout: 3
    Runtime: nodejs18.x

Parameters:
  UserPoolName:
    Type: String
    Description: The name you want the User Pool to be created with
    Default: rafaelTest

Resources:
  # 自定义PreSignUp Lambda触发器
  PreSignUp:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      CodeUri: ./pre-sign-up/ # 指向存放Lambda代码的目录
      Policies:
        - AWSLambdaBasicExecutionRole

  CreateAuthChallenge:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      CodeUri: ./create-auth-challenge/
      Policies:
        - AWSLambdaBasicExecutionRole
        - AmazonSNSFullAccess # 若使用SNS发送短信验证码

  DefineAuthChallenge:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      CodeUri: ./define-auth-challenge/
      Policies:
        - AWSLambdaBasicExecutionRole

  VerifyAuthChallengeResponse:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      CodeUri: ./verify-auth-challenge-response/
      Policies:
        - AWSLambdaBasicExecutionRole

  UserPool:
    Type: "AWS::Cognito::UserPool"
    Properties:
      UserPoolName: !Ref UserPoolName
      Schema:
        - Name: name
          AttributeDataType: String
          Mutable: true
          Required: true
        - Name: phone_number
          AttributeDataType: String
          Mutable: true
          Required: false
        - Name: email
          AttributeDataType: String
          Mutable: true
          Required: false
      Policies:
        PasswordPolicy:
          MinimumLength: 6
          RequireLowercase: false
          RequireNumbers: false
          RequireSymbols: false
          RequireUppercase: false
      UsernameAttributes:
        - phone_number
        - email
      MfaConfiguration: "OFF"
      LambdaConfig:
        CreateAuthChallenge: !GetAtt CreateAuthChallenge.Arn
        DefineAuthChallenge: !GetAtt DefineAuthChallenge.Arn
        PreSignUp: !GetAtt PreSignUp.Arn
        VerifyAuthChallengeResponse: !GetAtt VerifyAuthChallengeResponse.Arn

  UserPoolClient:
    Type: "AWS::Cognito::UserPoolClient"
    Properties:
      ClientName: sms-email-auth-client
      GenerateSecret: false
      UserPoolId: !Ref UserPool
      ExplicitAuthFlows:
        - CUSTOM_AUTH_FLOW_ONLY

Outputs:
  UserPoolId:
    Description: ID of the User Pool
    Value: !Ref UserPool
  UserPoolClientId:
    Description: ID of the User Pool Client
    Value: !Ref UserPoolClient

2. 实现PreSignUp Lambda校验逻辑

在./pre-sign-up/index.js中编写以下代码,确保用户至少提供邮箱或手机号中的一个,并自动确认用户:

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

  // 强制校验:必须提供邮箱或手机号其中一个
  if (!userAttributes.email && !userAttributes.phone_number) {
    throw new Error("请提供邮箱或手机号");
  }

  // 自动确认用户(若需验证邮箱/手机号可关闭此选项)
  event.response.autoConfirmUser = true;
  
  // 显式设置用户名(可选,Cognito会自动处理,显式设置更清晰)
  if (userAttributes.email) {
    event.request.userAttributes.username = userAttributes.email;
  } else if (userAttributes.phone_number) {
    event.request.userAttributes.username = userAttributes.phone_number;
  }

  return event;
};

3. 适配CreateAuthChallenge触发器逻辑

确保CreateAuthChallenge能根据用户提供的字段发送对应渠道的验证码,核心逻辑示例:

exports.handler = async (event) => {
  const { userAttributes } = event.request;
  const challengeCode = Math.floor(100000 + Math.random() * 900000).toString();

  // 根据用户属性选择验证码发送渠道
  if (userAttributes.email) {
    // 调用SES发送邮件验证码的逻辑
    console.log(`发送验证码${challengeCode}到邮箱${userAttributes.email}`);
  } else if (userAttributes.phone_number) {
    // 调用SNS发送短信验证码的逻辑
    console.log(`发送验证码${challengeCode}到手机号${userAttributes.phone_number}`);
  }

  // 存储验证码供后续校验使用
  event.response.privateChallengeParameters = {
    challengeCode: challengeCode
  };
  event.response.challengeMetadata = `CODE_${userAttributes.email || userAttributes.phone_number}`;

  return event;
};

验证

部署修改后的模板后,测试注册流程:

  • 仅填写姓名+邮箱:成功注册并收到邮箱验证码
  • 仅填写姓名+手机号:成功注册并收到短信验证码
  • 同时填写姓名+邮箱+手机号:正常注册(若需禁止此场景,可在PreSignUp中补充逻辑)

内容的提问来源于stack exchange,提问作者Rafael de Carvalho

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 01:40:22