Cognito:pre_authentication触发器中使用admin_user_global_sign_out因异步特性偶发失败
问题背景
我需要实现:用户用同一邮箱+密码启动新会话时,注销该用户在Cognito的所有旧会话(吊销刷新令牌),避免同一账号多端同时登录。注:我知道旧会话的ID和访问令牌在过期前仍能使用。
我采用的方案是通过Cognito的pre-authentication Lambda触发,测试代码如下:
import json import boto3 from time import sleep client = boto3.client('cognito-idp') def lambda_handler(event, context): user_pool_id = event['userPoolId'] email = event['request']['userAttributes']['email'] # 注销所有当前活跃会话,只保留本次创建的会话 client.admin_user_global_sign_out( Username=email, UserPoolId=user_pool_id) sleep(1) return event
最初没加sleep(1)时,授权操作偶发失败。我猜测admin_user_global_sign_out是异步操作,可能出现旧会话注销延迟,导致新会话创建后才执行注销,最终所有会话(包括新的)都被吊销。加了1秒休眠后,登录失败次数大幅减少,但担心分布式系统延迟超过1秒时问题复现。
我的问题:
admin_user_global_sign_out是否为异步操作?我的假设是否正确?- 能否让该全局注销操作同步?比如通过调用Cognito API等待状态变更?
解答
1. admin_user_global_sign_out的异步属性
你的假设完全正确,admin_user_global_sign_out是异步操作。AWS官方文档明确说明,这个API调用会立即返回成功响应,但实际的会话吊销逻辑是在后台异步执行的,不会等待所有会话处理完成再返回结果。这就是为什么不加休眠时会偶发新会话被误吊销的原因——新会话的创建流程可能比后台注销旧会话的速度更快,导致新生成的刷新令牌也被纳入了注销范围。
2. 实现可靠全局注销的替代方案
目前Cognito没有提供直接等待admin_user_global_sign_out执行完成的API,但可以通过以下两种方式规避异步延迟带来的问题:
改用
admin_revoke_token配合会话追踪:
如果你的系统可以追踪用户当前有效的刷新令牌(比如将令牌存储在数据库或缓存中),可以在pre-authentication触发时,调用admin_revoke_token逐个吊销已知的旧令牌。这个API是同步执行的,调用成功后对应的令牌立即失效,不会有延迟问题。但这种方案需要额外维护用户的令牌列表,增加了系统复杂度。调整Lambda触发时机,避免新会话被误杀:
不需要依赖固定时长的sleep,可以在pre-authentication阶段先执行全局注销,然后在post-authentication触发的Lambda中,验证当前新生成的会话是否有效。如果发现新会话被误吊销,可以重新生成令牌(不过这种方式需要额外处理令牌生成逻辑,实现起来比较复杂)。
另外,还可以利用Cognito的Device Tracking功能,强制用户每次登录都使用新设备,同时在pre-authentication阶段注销所有旧设备。不过这需要开启用户池的设备跟踪配置,且同样依赖异步操作,但可以通过设备ID来更精准地控制会话。
内容的提问来源于stack exchange,提问作者João Pedro Schmitt

