配置AWS Cognito用户池迁移后遇验证错误求助
AWS Cognito用户池迁移触发器无日志且报错:
No email provided but email_verified was true & UserNotFoundException 问题背景
已完成两个AWS Cognito用户池的迁移配置,操作步骤:
- 基于官方迁移示例创建Lambda函数
- 在目标用户池配置迁移触发器并关联该Lambda
- 为相关IAM角色添加对应权限策略
测试时出现两个核心问题:
- Cognito返回错误提示:
No email provided but email_verified was true - 迁移触发器Lambda未生成CloudWatch日志,同时客户端收到
UserNotFoundException响应
相关代码片段
用户登录代码
const authenticationData = { Username: email, Email: email, Password: userPassword, }; const authDetails = AWS.authenticationDetails(authenticationData); const userPool = AWS.cognitoUserPool(poolData); const userData = { Username: email, Email: email, Pool: userPool, }; const cognitoUser = AWS.cognitoUser(userData); cognitoUser.setAuthenticationFlowType('USER_PASSWORD_AUTH'); authDetails.email = email; try { const authResult = await utilsHelper.promisifySF(cognitoUser.authenticateUser.bind(cognitoUser))(authDetails); console.log('authResult : ', authResult); } catch (e) { console.log('user verify exception : ', e); }
Lambda迁移函数代码
'use strict'; var CLIENT_ID = ''; var USER_POOL_ID = ''; var OLD_CLIENT_ID = ''; var OLD_USER_POOL_ID = ''; var OLD_USER_POOL_REGION = ''; var OLD_ROLE_ARN = ''; var OLD_EXTERNAL_ID = ''; var AWS = require('aws-sdk'); exports.handler = (event, context, callback) => { var user; if ( event.triggerSource == "UserMigration_Authentication" ) { // authenticate the user with your existing user directory service user = authenticateUser(event.userName, event.request.password); if ( user ) { event.response.userAttributes = { "email": user.emailAddress, "email_verified": "true" }; event.response.finalUserStatus = "CONFIRMED"; event.response.messageAction = "SUPPRESS"; context.succeed(event); } else { // Return error to Amazon Cognito callback("Bad password"); } } else { // Return error to Amazon Cognito callback("Bad triggerSource " + event.triggerSource); } }; async function authenticateUser(username, password) { const isp = new AWS.CognitoIdentityServiceProvider(); // Validate username/password const resAuth = await isp.adminInitiateAuth({ AuthFlow: 'ADMIN_USER_PASSWORD_AUTH', AuthParameters: { PASSWORD: password, USERNAME: username, }, ClientId: OLD_CLIENT_ID, UserPoolId: OLD_USER_POOL_ID, }).promise(); if (resAuth.code && resAuth.message) { return undefined; } // Load user data const resGet = await isp.adminGetUser({ UserPoolId: OLD_USER_POOL_ID, Username: username, }).promise(); if (resGet.code && resGet.message) { return undefined; } return { emailAddress: resGet.UserAttributes.find(e => e.Name === 'email').Value, }; }
客户端错误响应
{ "code": "UserNotFoundException", "name": "UserNotFoundException", "message": "User does not exist." }
问题排查与修复方案
1. Lambda无日志+UserNotFoundException:迁移触发器未触发
这两个问题的核心是Cognito根本没有触发迁移流程,需检查以下配置:
- 目标用户池客户端认证流程:确保目标用户池的客户端已启用
ALLOW_USER_PASSWORD_AUTH流程(在用户池客户端的「认证流程」设置中勾选) - 迁移触发器状态:确认目标用户池的「触发器」配置里,「用户迁移」已关联正确的Lambda函数且状态为启用
- 用户名匹配规则:如果旧池的用户名不是邮箱格式,而你用邮箱作为登录用户名,Cognito无法匹配触发迁移,需保证新旧池的用户名规则一致
2. No email provided but email_verified was true:Lambda异步处理缺陷
当前Lambda handler存在异步调用错误:authenticateUser是异步函数,但handler中同步调用导致user始终是Promise对象,无法正确设置用户属性。修复后的Lambda代码:
'use strict'; var OLD_CLIENT_ID = ''; // 填写旧池客户端ID var OLD_USER_POOL_ID = ''; // 填写旧池ID var OLD_USER_POOL_REGION = ''; // 填写旧池区域 var AWS = require('aws-sdk'); const isp = new AWS.CognitoIdentityServiceProvider({ region: OLD_USER_POOL_REGION }); exports.handler = async (event) => { if (event.triggerSource !== "UserMigration_Authentication") { throw new Error("Bad triggerSource " + event.triggerSource); } try { const user = await authenticateUser(event.userName, event.request.password); if (!user) { throw new Error("Bad password"); } event.response.userAttributes = { "email": user.emailAddress, "email_verified": "true" }; event.response.finalUserStatus = "CONFIRMED"; event.response.messageAction = "SUPPRESS"; return event; } catch (err) { throw err; } }; async function authenticateUser(username, password) { try { await isp.adminInitiateAuth({ AuthFlow: 'ADMIN_USER_PASSWORD_AUTH', AuthParameters: { PASSWORD: password, USERNAME: username, }, ClientId: OLD_CLIENT_ID, UserPoolId: OLD_USER_POOL_ID, }).promise(); const resGet = await isp.adminGetUser({ UserPoolId: OLD_USER_POOL_ID, Username: username, }).promise(); const emailAttr = resGet.UserAttributes.find(e => e.Name === 'email'); if (!emailAttr) { throw new Error("User has no email attribute"); } return { emailAddress: emailAttr.Value }; } catch (err) { console.error("Authenticate user failed:", err); return undefined; } }
3. 额外优化与检查
- Lambda权限补充:确保Lambda角色拥有
cognito-idp:AdminInitiateAuth、cognito-idp:AdminGetUser权限,以及CloudWatch日志的logs:CreateLogGroup、logs:CreateLogStream、logs:PutLogEvents权限 - 登录代码修正:AWS SDK调用方式有误,正确写法如下:
const authenticationData = { Username: email, Password: userPassword, }; const authDetails = new AWS.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData); const userPool = new AWS.CognitoIdentityServiceProvider.CognitoUserPool(poolData); const userData = { Username: email, Pool: userPool, }; const cognitoUser = new AWS.CognitoIdentityServiceProvider.CognitoUser(userData); cognitoUser.setAuthenticationFlowType('USER_PASSWORD_AUTH');
无需手动设置authDetails.email,Username已传入邮箱信息
内容的提问来源于stack exchange,提问作者Michael Marshal
相关产品推荐
相关产品推荐

