AWS Cognito user migration email bug - amazon-cognito

I am testing Cognito for replacement of our existing auth code and have found a bug. I have a user pool and implemented the user migration lambda to test the migration process. The user pool is setup to use email as username.
From my client I login with existing credentials, the user migration lambda does the following:
event['response']['userAttributes'] = {
'preferred_username': "migrated guy"
}
event["finalUserStatus"] = "CONFIRMED"
event["messageAction"] = "SUPPRESS"
return event
Login is successful and my user is migrated to Cognito. However, the user immediately receives and email with the subject "Your temporary password" and body "Your username is *****#******.com and temporary password is ********." The temporary password in the email is one generated by Cognito, not the one entered/migrated. I can subsequently log in again with the original password while the one from the email does not work. I have tried this multiple times with the same result. This happens whether or not the migrated password meets my password strength requirements.
Is there at least a way to avoid having this erroneous email sent? Am I doing something wrong?

If you manage to get this resolved, the finalUserStatus and messageAction need to be set on event.response instead of on event:
event.response.finalUserStatus = "CONFIRMED";
event.response.messageAction = "SUPPRESS";
The event structure can be found here: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-migrate-user.html#cognito-user-pools-lambda-trigger-syntax-user-migration

Related

Passwordless Authentication with Cognito - How to determine if a user signed up with email or phone number

We have implemented the Custom Auth Triggers as described link here. We have the user pool set up to let users log in with either phone number or email.
The provided case is - the user has email & phone both verified in their Cognito account
The problem I am having is determining what medium (email or phone number) the user signed in
When observing the event passed into the define / create/verify auth triggers, it seems like doesn't pass through what the username was used to initiate the authentication flow.. only the user attributes which in my case there could be both email or phone. I need to know which one it is so I know if I need to send the code through SMS or Email.
I also read about ClientMetadata this key we can pass from in InitiateAuthCommandInput but it will provide a client metadata key only below these triggers
Pre signup
Pre-authentication
User migration
but it will not provide ClientMetadata in these triggers
Post authentication
Custom message
Pre token generation
Create auth challenge
Define auth challenge
Verify auth challenge
After googling it too much, I found an article which had a tricky solution:
here is the link
I am not able to implement the provided solution.
I found a similar question in stack overflow too Link but there is also no answer, Can anyone please help me with this.
This is a workaround by adding a custom attribute during passwordless login
Actually, the authenticationUser function needs to identify whether the user is adding email or phone during login
Step 1: during login process, before calling initiateAuthCommand, First set a custom attribute in Cognito user object - logged_in_by - email or phone
Step 2: once you add a key after that InitiateAuthCommand will be started and call the triggers
Step 3:
When createAuthChallenge runs at the time we will have userAttributes.logged_in_by.
If this attribute contains email this indicates that the user is trying to login with the email and we need to send OTP over email.
If this attribute contains phone this indicates that the user is trying to log in with the phone and we need to send OTP over the phone number.

Keycloak Action Tokens: How to invalidate user's previous action when they request multiple (e.g. Reset Password)

I want to be able to invalidate any action token for a user within an authentication flow.
The scenario is the user sends a reset password and receives an email with an associated action token. The user then sends another reset password and gets another email with a different action token associated. For the length of the first action token expiry time the user can utilise the links in both emails - however I'd like to be able to identify within my custom reset password authentication flow that the user is requesting a duplicate action request and invalidate their earlier action token(s) so that only their latest reset password link works.
I've been looking at the below objects but had no luck finding an action token store associated with all the user's activity rather than just their current authenticated session.
AuthenticationFlowContext context;
List<UserSessionModel> sessions = context.getSession().sessions().getUserSessions(context.getRealm(), user);
RootAuthenticationSessionModel parentSessions = context.getAuthenticationSession().getParentSession();
ActionTokenStoreProvider actionTokenStore = session.getProvider(ActionTokenStoreProvider.class);
Thanks in advance.
I've resolved this by maintaining a Table of users and action tokens per flow. This means when the user initiates a new action flow I can grab the previous token if still valid and use the ActionTokenStoreProvider to invalidate it replacing it with the new token. I am still hoping keycloak has some internal mechanism to manage this rather than my own custom code. Drop a solution if you know of this!

How to disable AWS Cognito User Pool account created via Identity Provider?

Any Cognito User Pool gurus out there? I've been using Cognito for a while now but this one has me a bit stumped.
We allow users to sign up and sign in using social accounts like Facebook which are set up as Identity Providers in the User Pool.
Users need to complete a custom registration form before they can use the main app - we don't use the hosted UI for login or signup
One step of the custom registration process allows the user to indicate which social provider then want to use
This allows us to pull back the users email, first and last names from the social provider which is great - we use a cognito client and callback to do this currently
But in doing so, this provisions a user within the Userpool before the registration process is complete - in fact this makes sense- in order for Cognito to provide us the user info it needs to have called into the social providers /userinfo endpoint to populate the user data
So, the issue we now have is that whilst the user is half way through the registration process I have a confirmed user account - eg. before the user has completed the registration process
This is an issue because a user could sign into the the app using their social login without ever have completed the registration process
So as I see it I have two options:
PostConfirmation Lambda trigger which uses the cognito-idp SDK to disable the user just after it was confirmed
Don't use Cognito to obtain the user info like firstname, lastname, email, picture etc - however this would require us to write a solution for every current and future social provider which isn't something I'm keen on
Am I missing something obvious?
Thanks in advance!
I would say PostConfirmation Lambda trigger is a good approach - however instead use adminDisableProviderForUser to disable the user from signing in with the specified external (SAML or social) identity provider
adminDisableProviderForUser
You can later call adminLinkProviderForUser to link the existing user account in the user pool to the external identity provider.
adminLinkProviderForUser
An alternative solution is to prevent the user from signing in if they have not fully completed the registration process via a Pre Authentication Lambda Trigger checking for a unique identifier with respect to your completed registration process
The simplest solution in the end for us was a Pre Token Generation Trigger in Cognito like this:
exports.handler = async (event) => {
if(event.triggerSource==="TokenGeneration_HostedAuth") {
//check db/api etc to see if we have a valid registration stored for user
if(!hasCompletedRegistration) {
//throw auth exception which we can catch on the frontend to inform user
throw new Error("REGISTRATION_NOT_COMPLETE")
}
}
return event
};
For username/password sign ins the TriggerSource will be TokenGeneration_Authentication
For federated/social sign ins the TriggerSource will be TokenGeneration_HostedAuth

How to update user password in Cognito user pool without old password using Amplify?

We have multistep register form where user can set their password in step 2.
(User register should happen in step1 itself) So, we will set random password during step 1 and registering user details in Cognito user pool.
But end user submitting actual password from step 2.
Cognito will not update password (from step 2) without sending old password (random generated from step1). Cognito considers this process will be password update.
So how we need to handle this situation? or Is there any option / tricks which amplify provides to overcome this case?
More context is needed to fully understand why a random password is necessary for the user in this situation.
For example, if you are trying to create a multi-screen signup process and you don't want the user to get to the end of the process only to find out their password doesn't meet standards, e-mail already exists, etc., it may be more conducive to the user experience to check if the user already exists in the user pool first using ListUsers, collect the data as they move through the steps, and finally call the SignUp API call.
While I would highly recommend reconsidering the approach taken, the AdminSetUserPassword is a backend API call that can be used to set a permanent password for the user, although extreme care should be taken with this method to prevent the API call from being used maliciously on another user.

"Forgot username" flow for AWS Cognito?

I'm using ASW Cognito for authenticating users. Cognito has a well-documented flow to handle users who have forgotten their passwords.
How do I handle users who have forgotten their usernames? Is there a built-in flow that lets the user enter their email or phone number, and then receive an email or text with their associated username? I found the ListUser API, which returns all the users in a userpool. I could write a Lambda function that filters through all my users, looking for a match on email or phone number. But this seems like overkill.
Unfortunately, there is no default out of the box workflow of "Forgot Username".
I am implementing similar workflow. We ask user for their registered phone number/email, and we retrieve username based on that number and send it to email/phone according to configuration. If user is configured to use email and phone both, we send SMS to phone if user forget username (which is email id they used during sign up).
One major drawback of this approach is that, we need to provide ListUsers API call access to anonymous user which is a potential security issue but can't seem to find any other way by which we inform user about their login details.
For those, who are looking for the solution, don't give the anonymous user access to ListUser API as suggested in the accepted answer.
There are two ways to implement 'Forgot username flow'.
Enable email as an alias for your Cognito User Pool:
Calling this API causes a message to be sent to the end user with a
confirmation code that is required to change the user's password. For
the Username parameter, you can use the username or user alias. The
method used to send the confirmation code is sent according to the
specified AccountRecoverySetting.
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_ForgotPassword.html
The user will be able to reset the password with their email and code delivered to provided email address. If you still want to remind the username, you can use Lambda trigger to generate the password reset email with both username and verification code.
Use the backend (web server or lambda) which will receive the email address as an input to the 'Forgot username flow'. The backend will have permissions to invoke List Users API (https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_ListUsers.html) and will perform user lookup using the email. You now can go into Forgot Password flow using the retrieved username. Lambda trigger will be used to generate password reset email with username and verification code.
You can protect this API from abuse using WAF and/or captcha.