I am using AWS cognito pool migration using Lambda function with cognito execution role Following is my new pool app client setting
or
User migration authentication flow A user migration Lambda trigger allows easy migration of users from a legacy user management system into your user pool. To avoid making your users reset their passwords during user migration, choose the USER_PASSWORD_AUTH authentication flow. This flow sends your users' passwords to the service over an encrypted SSL connection during authentication.
When you have completed migrating all your users, we recommend switching flows to the more secure SRP flow. The SRP flow does not send any passwords over the network.
I have created lambda function with role "AmazonCognitoPowerUser"
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}`);
}
}
and added trigger into new pool,
After many attempt Lambda trigger is not working on login always getting error .
{__type: "NotAuthorizedException", message: "Incorrect username or password."}
message: "Incorrect username or password."
__type: "NotAuthorizedException"
Though its working fine if we use forget passwords flow after reset password user migrated to new pool
UPDATED :
When directly run test on lambda using following json
{
"version": "1",
"triggerSource": "UserMigration_Authentication",
"region": "ap-south-1",
"userPoolId": "ap-XXXXXXXXX2",
"userName": "[email protected]",
"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
}
}
Then getting following response and user migrated into new pool means we have issue in trigger during login.
INFO Authentication - response: {"userAttributes":{"email":"[email protected]","email_verified":"true"},"forceAliasCreation":null,"finalUserStatus":"CONFIRMED","messageAction":"SUPPRESS","desiredDeliveryMediums":null}