AWS Cognito listUsers方法调用后回调函数未执行问题
问题背景
当前正在学习AWS相关服务,基于API Gateway + Cognito Authorizer实现用户身份认证,目前认证模块已运行正常:可正常生成令牌返回前端,也能正常接收前端回传至API的合法令牌。
后续需要从Cognito用户池中拉取指定用户属性,存入DynamoDB的非规范化字段中,经评估在POST请求处理阶段拉取用户属性是较合理的实现方式,该操作需要调用cognitoidentityserviceprovider.listUsers方法。
遇到的问题:该方法的回调函数始终不会执行。最初调用时返回未授权错误,更新对应IAM策略后该错误已消除,但目前仅能打印回调函数外部的日志,回调函数内部的日志完全无法输出。
相关代码片段
let cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'}); let sub = event.requestContext.authorizer.claims.sub; let userpool = event.stageVariables.userpool; const cogParams = { UserPoolId: userpool, Filter: "sub=\""+ sub + "\"", Limit: 1 }; console.log('listing',JSON.stringify(cogParams)); cognitoidentityserviceprovider.listUsers(cogParams, function(err, data) { console.info('entered callback'); if (err) { console.log(err, err.stack); } else { console.log('data',data); } console.info('end callback'); }); console.log('listed');
日志输出对比
预期日志输出
listing {...} entered callback data {...} end callback listed
实际运行输出
listing {...} listed
问题原因
该问题是Lambda Node.js运行时的执行机制导致的:Lambda会在主处理函数逻辑执行完成、返回响应后立刻冻结执行环境,不会等待事件循环中未完成的异步任务执行。当前代码调用listUsers发起异步请求后,没有等待请求返回就继续执行完了主流程的所有逻辑、提前返回,导致异步回调根本没有机会被执行。
解决方案
- 优先使用
async/await搭配AWS SDK的Promise模式处理异步请求,确保所有异步操作执行完成后再结束主函数逻辑,参考修正代码:
// Lambda处理函数需要添加async关键字 exports.handler = async (event) => { const AWS = require('aws-sdk'); let cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'}); let sub = event.requestContext.authorizer.claims.sub; let userpool = event.stageVariables.userpool; const cogParams = { UserPoolId: userpool, Filter: `sub="${sub}"`, Limit: 1 }; console.log('listing',JSON.stringify(cogParams)); // 等待listUsers请求完成 const userData = await cognitoidentityserviceprovider.listUsers(cogParams).promise(); console.log('entered callback'); console.log('data', userData); console.log('end callback'); // 后续写入DynamoDB、构造API响应的逻辑全部放在await之后 console.log('listed'); return { statusCode: 200, body: JSON.stringify({message: 'success'}) } }
- 如果要保留回调写法,必须把所有后续业务逻辑(包括给API Gateway返回响应的操作)全部放到
listUsers的回调函数内部,确保回调执行完成前主函数不会返回。
优化建议
不需要额外调用listUsers拉取用户属性:Cognito Authorizer校验令牌通过后,会自动把ID Token中包含的所有用户属性解析到event.requestContext.authorizer.claims对象中,常用的sub、邮箱、用户名以及自定义属性只要存在于令牌中,可直接从该对象读取,能省掉一次Cognito API调用,降低接口延迟。如果需要的属性没有出现在claims中,可以到Cognito用户池的应用客户端配置页,把对应属性加入ID令牌的返回声明列表即可。
内容的提问来源于stack exchange,提问作者meteorainer

