AWS cognito 用户迁移池触发器不适用于登录流程

Posted

技术标签:

【中文标题】AWS cognito 用户迁移池触发器不适用于登录流程【英文标题】:AWS cognito user migration pool trigger not working on login flow 【发布时间】:2020-10-02 22:24:54 【问题描述】:

我正在使用具有认知执行角色的 Lambda 函数使用 AWS 认知池迁移 以下是我的新池应用程序客户端设置

AWS 文档说

用户迁移身份验证流程 用户迁移 Lambda 触发器 允许从旧的用户管理系统轻松迁移用户 进入您的用户池。避免让您的用户重置密码 在用户迁移期间,选择 USER_PASSWORD_AUTH 身份验证 流动。此流程通过 身份验证期间加密的 SSL 连接。

当您完成所有用户的迁移后,我们建议您 将流切换到更安全的 SRP 流。 SRP 流程不 通过网络发送任何密码。

我创建了角色为“AmazonCognitoPowerUser”的 lambda 函数

        async function authenticateUser(cognitoISP: CognitoIdentityServiceProvider, username: string, password: string): Promise<User | undefined> 
            console.log(`authenticateUser: user='$username'`);

            const params: AdminInitiateAuthRequest = 
                AuthFlow: 'ADMIN_USER_PASSWORD_AUTH',
                AuthParameters: 
                    PASSWORD: password,
                    USERNAME: username,
                ,
                ClientId: OLD_CLIENT_ID,
                UserPoolId: OLD_USER_POOL_ID,
            ;
            const cognitoResponse = await cognitoISP.adminInitiateAuth(params).promise();
            const awsError: AWSError = cognitoResponse as any as AWSError;
            if (awsError.code && awsError.message) 
                console.log(`authenticateUser: error $JSON.stringify(awsError)`);
                return undefined;
            
            console.log(`authenticateUser: found $JSON.stringify(cognitoResponse)`);

            return lookupUser(cognitoISP, username);
        

        async function lookupUser(cognitoISP: CognitoIdentityServiceProvider, username: string): Promise<User | undefined> 
            console.log(`lookupUser: user='$username'`);
            const params = 
                UserPoolId: OLD_USER_POOL_ID,
                Username: username,
            ;
            const cognitoResponse = await cognitoISP.adminGetUser(params).promise();
            const awsError: AWSError = cognitoResponse as any as AWSError;
            if (awsError.code && awsError.message) 
                console.log(`lookupUser: error $JSON.stringify(awsError)`);
                return undefined;
            
            console.log(`lookupUser: found $JSON.stringify(cognitoResponse)`);

            const userAttributes = cognitoResponse.UserAttributes ? cognitoResponse.UserAttributes.reduce((acc, entry) => (
                ...acc,
                [entry.Name]: entry.Value,
            ),  as [key: string]: string | undefined) : ;
            const user: User = 
                userAttributes,
                userName: cognitoResponse.Username,
            ;
            console.log(`lookupUser: response $JSON.stringify(user)`);
            return user;
        

        async function onUserMigrationAuthentication(cognitoISP: CognitoIdentityServiceProvider, event: CognitoUserPoolTriggerEvent) 
            // authenticate the user with your existing user directory service
            const user = await authenticateUser(cognitoISP, event.userName!, event.request.password!);
            if (!user) 
                throw new Error('Bad credentials');
            

            event.response.userAttributes = 
                // old_username: user.userName,
                // 'custom:tenant': user.userAttributes['custom:tenant'],
                email: user.userAttributes.email!,
                email_verified: 'true',
                preferred_username: user.userAttributes.preferred_username!,
            ;
            event.response.finalUserStatus = 'CONFIRMED';
            event.response.messageAction = 'SUPPRESS';

            console.log(`Authentication - response: $JSON.stringify(event.response)`);
            return event;
        

        async function onUserMigrationForgotPassword(cognitoISP: CognitoIdentityServiceProvider, event: CognitoUserPoolTriggerEvent) 
            // Lookup the user in your existing user directory service
            const user = await lookupUser(cognitoISP, event.userName!);
            if (!user) 
                throw new Error('Bad credentials');
            

            event.response.userAttributes = 
                // old_username: user.userName,
                // 'custom:tenant': user.userAttributes['custom:tenant'],
                email: user.userAttributes.email!,
                email_verified: 'true',
                preferred_username: user.userAttributes.preferred_username!,
            ;
            event.response.messageAction = 'SUPPRESS';

            console.log(`Forgot password - response: $JSON.stringify(event.response)`);

            return event;
        

        export const handler = async (event: CognitoUserPoolTriggerEvent, context: Context): Promise<CognitoUserPoolTriggerEvent> => 
            const options: CognitoIdentityServiceProvider.Types.ClientConfiguration = 
                region: OLD_USER_POOL_REGION,
            ;
            if (OLD_ROLE_ARN) 
                options.credentials = new ChainableTemporaryCredentials(
                    params: 
                        ExternalId: OLD_EXTERNAL_ID,
                        RoleArn: OLD_ROLE_ARN,
                        RoleSessionName: context.awsRequestId,
                    ,
                );
            
            const cognitoIdentityServiceProvider = new CognitoIdentityServiceProvider(options);

            switch (event.triggerSource) 
                case 'UserMigration_Authentication':
                    return onUserMigrationAuthentication(cognitoIdentityServiceProvider, event);
                case 'UserMigration_ForgotPassword':
                    return onUserMigrationForgotPassword(cognitoIdentityServiceProvider, event);
                default:
                    throw new Error(`Bad triggerSource $event.triggerSource`);
            
        

并将触发器添加到新池中,

经过多次尝试 Lambda 触发器在登录时无法正常工作,总是出现错误。

  __type: "NotAuthorizedException", message: "Incorrect username or password."
       message: "Incorrect username or password."
     __type: "NotAuthorizedException"

如果我们在重置密码用户迁移到新池后使用忘记密码流程,它工作正常

https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-import-using-lambda.html

更新:

当使用以下 json 直接在 lambda 上运行测试时

  
   "version": "1",
   "triggerSource": "UserMigration_Authentication",
   "region": "ap-south-1",
   "userPoolId": "ap-XXXXXXXXX2",
   "userName": "vaquar.test@gmail.com",
   "callerContext": 
   "awsSdkVersion": "aws-sdk-unknown-unknown",
   "clientId": "1XXXXXXXXXXXXXXXXXXfgk"
   ,
   "request": 
   "password": "vkhan",
   "validationData": null,
   "userAttributes": null
    ,
   "response": 
    "userAttributes": null,
    "forceAliasCreation": null,
    "finalUserStatus": null,
    "messageAction": null,
    "desiredDeliveryMediums": null
    
  

然后得到以下响应并且用户迁移到新池中意味着我们在登录期间触发了问题。

 INFO   Authentication - response: "userAttributes":"email":"vaquar.test@gmail.com","email_verified":"true","forceAliasCreation":null,"finalUserStatus":"CONFIRMED","messageAction":"SUPPRESS","desiredDeliveryMediums":null

【问题讨论】:

【参考方案1】:

使用 Lambda 触发器,Cognito 服务调用 Lambda 函数。所以 Cognito 需要permission to invoke Lambda function。您如何在用户池上配置 Lambda 触发器?如果您使用的是 AWS Cognito 控制台,则应自动设置权限。

您也可以verify if the user already exists in the new userpool 可能导致此类行为。

【讨论】:

【参考方案2】:

请确认您的应用程序使用的是 OAUTH 流 USER_PASSWORD 而不是默认的 USER_SRP_AUTH。

供参考:link

【讨论】:

以上是关于AWS cognito 用户迁移池触发器不适用于登录流程的主要内容,如果未能解决你的问题,请参考以下文章

AWS Cognito 用户池的事件触发器对象

在 AWS Cognito 用户池中删除用户时触发 Lambda 函数

使用 Amplify 将现有 AWS Cognito 用户池集成到 iOS 项目中

将用户从 cognito 用户池迁移到身份池

AWS Cognito 用户池 + 用于身份验证/登录的社交提供商

AWS Cognito:使用邮递员测试触发器