如何在AWS Cognito无密码流程中实现注册登录合一并确认后签发访问令牌
实现AWS Cognito注册/登录合一的无密码认证流程
核心思路
利用Cognito的自定义认证流(Custom Auth Flow),结合三个Lambda触发器,将用户注册、验证、登录合并为单一步骤:用户仅输入邮箱,系统自动判断是否为新用户,仅发送一次验证码,验证通过后直接返回访问令牌。
步骤1:配置Cognito用户池
- 进入用户池控制台,在「认证流程」中启用自定义认证
- 确保用户池已配置邮箱作为登录标识(可设置为用户名或别名),并开启无密码登录模式
- 关闭用户池的「自动验证」和「确认后强制登录」等默认流程,避免重复发码
步骤2:创建三个关键Lambda触发器
需要为用户池按顺序配置以下三个Lambda触发器:
1. Define Auth Challenge(定义认证挑战)
该函数决定当前认证阶段的操作,核心逻辑是判断用户是否存在,新用户自动完成注册:
exports.handler = async (event) => { const { request, response } = event; // 检查用户是否存在 const userExists = await checkUserExists(event.userPoolId, request.userAttributes.email); if (!userExists) { // 新用户:自动创建并启用用户 await createAndEnableUser(event.userPoolId, request.userAttributes.email); response.challengeName = 'CUSTOM_CHALLENGE'; response.challengeParameters = { USERNAME: request.userAttributes.email }; } else if (request.session.length === 0) { // 已有用户:首次请求发起验证码挑战 response.challengeName = 'CUSTOM_CHALLENGE'; response.challengeParameters = { USERNAME: request.userAttributes.email }; } else { // 验证阶段:检查最后一次挑战结果 const lastChallenge = request.session[request.session.length - 1]; if (lastChallenge.challengeName === 'CUSTOM_CHALLENGE' && lastChallenge.challengeResult) { response.issueTokens = true; response.failAuthentication = false; } else { response.issueTokens = false; response.failAuthentication = true; } } return event; }; // 辅助函数:检查用户是否存在 async function checkUserExists(userPoolId, username) { const AWS = require('aws-sdk'); const cognito = new AWS.CognitoIdentityServiceProvider(); try { await cognito.adminGetUser({ UserPoolId: userPoolId, Username: username }).promise(); return true; } catch (err) { return err.code !== 'UserNotFoundException'; } } // 辅助函数:创建并启用新用户 async function createAndEnableUser(userPoolId, username) { const AWS = require('aws-sdk'); const cognito = new AWS.CognitoIdentityServiceProvider(); await cognito.adminCreateUser({ UserPoolId: userPoolId, Username: username, UserAttributes: [{ Name: 'email', Value: username }], MessageAction: 'SUPPRESS' // 禁用Cognito默认欢迎邮件 }).promise(); await cognito.adminEnableUser({ UserPoolId: userPoolId, Username: username }).promise(); }
2. Create Auth Challenge(生成并发送验证码)
该函数生成随机验证码并发送到用户邮箱:
exports.handler = async (event) => { const { request, response } = event; // 生成6位随机验证码 const verificationCode = Math.floor(100000 + Math.random() * 900000).toString(); // 发送验证码(替换为你的SES或邮件服务配置) await sendVerificationEmail(request.userAttributes.email, verificationCode); // 将验证码存入私有会话,供后续验证使用 response.privateChallengeParameters = { verificationCode }; response.challengeMetadata = 'CUSTOM_VERIFICATION_CODE'; return event; }; // 辅助函数:发送验证码邮件 async function sendVerificationEmail(email, code) { const AWS = require('aws-sdk'); const ses = new AWS.SES(); await ses.sendEmail({ Source: 'your-verification-email@example.com', Destination: { ToAddresses: [email] }, Message: { Subject: { Data: '登录/注册验证码' }, Body: { Text: { Data: `你的验证码是:${code}` } } } }).promise(); }
3. Verify Auth Challenge Response(验证验证码)
该函数对比用户输入的验证码与会话中存储的验证码:
exports.handler = async (event) => { const { request, response } = event; const userInputCode = request.challengeAnswer; const correctCode = request.privateChallengeParameters.verificationCode; response.answerCorrect = userInputCode === correctCode; return event; };
步骤3:前端调用流程
前端只需两次API调用即可完成认证:
- 首次调用触发自定义认证流:
const cognito = new AWS.CognitoIdentityServiceProvider(); const initiateResponse = await cognito.initiateAuth({ AuthFlow: 'CUSTOM_AUTH', ClientId: 'your-cognito-client-id', AuthParameters: { USERNAME: 'user-input-email@example.com' } }).promise();
- 用户输入验证码后,完成验证并获取令牌:
const challengeResponse = await cognito.respondToAuthChallenge({ ClientId: 'your-cognito-client-id', ChallengeName: 'CUSTOM_CHALLENGE', Session: initiateResponse.Session, ChallengeResponses: { USERNAME: 'user-input-email@example.com', ANSWER: 'user-input-verification-code' } }).promise(); // 获取令牌 const accessToken = challengeResponse.AuthenticationResult.AccessToken; const idToken = challengeResponse.AuthenticationResult.IdToken;
关键注意事项
- 给Lambda函数附加
AmazonCognitoPowerUser策略或自定义权限,需包含cognito-idp:AdminGetUser、cognito-idp:AdminCreateUser、cognito-idp:AdminEnableUser等权限 - 配置SES域名验证,避免验证码邮件进入垃圾箱
- 可在
Define Auth Challenge中添加请求频率限制,防止恶意刷验证码
内容的提问来源于stack exchange,提问作者Norbert
相关产品推荐
相关产品推荐

