AWS Cognito not prompting MFA on first login - amazon-web-services

We have an application using AWS Cognito (+ hosted web UI) where users are only created by administrators. MFA with SMS is required. This is our current flow for new users:
Admin creates a new user using AdminCreateUser of aws-sdk. Email, name and phone number are given.
A new account is created with FORCE_CHANGE_PASSWORD status. Username and a temporary password are sent to the user via email.
User signs in for the first time with the temporary password.
Cognito asks for a new password.
User sets their new password and proceeds to log in.
MFA code is sent to user via SMS. However, Cognito does not ask for the MFA code. User simply gets logged in. Account status is now CONFIRMED but phone_number_verified is not set.
However, the MFA challenge works fine starting from the second login. In other words, user's phone number only gets verified if they manage to log in for the second time. This means that a user who forgets their password after the initial login is unable to reset their password (as it requires a verified phone number).
Any idea why this is happening and what settings should I look into? I'm aware I can avoid the main problem by programmatically setting phone_number_verified as true, but I'd like to know why the MFA challenge fails on first login.

I've been really disappointed in the AWS hostsed auth UI. It's ugly and very limited. As you've discovered, for example, it doesn't handle MFA at all.
The best alternative I've found is to use the Authenticator Amplify UI component. It's possible to use Amplify UI without using the Amplify CLI or hosting your site on AWS, so it's pretty well a drop-in solution. Authenticator handles setting up software TOTP tokens and the TOTP challenge as needed. I haven't used it for SMS, but this page implies it's supported.
You can learn how to use Amplify UI components standalone (without the CLI and AWS hosting) in this StackOverflow answer.

Related

AWS Cognito: How to remember device in CUSTOM_AUTH authentication workflow?

I have followed this AWS blog to implement the Custom Authentication Flow for my website with Email One-Time-Password as a second Factor:
https://aws.amazon.com/blogs/mobile/extending-amazon-cognito-with-email-otp-for-2fa-using-amazon-ses/
Basically it explains that on Cognito, how to use the CUSTOM_CHALLENGE and 3 lambda functions as triggers for Cognito to:
Define custom auth challenge
Create custom auth challenge code and send to user's email address through AWS SES;
Verify that the verification code the user submitted is correct;
The authenticationFlowType would be CUSTOM_AUTH in this case, which is different from USER_SRP_AUTH in the normal username/password only scenario.
Now, I want cognito to remember the device that the user is on.
In this doc:
How do I use remembered devices in my Amazon Cognito user pool?
It says:
Note: The remembered devices functionality works only with the
USER_SRP_AUTH authentication flow. Also, this functionality requires
multi-factor authentication (MFA) to be enabled for the user pool.
So how can I remember device when it comes to CUSTOM_AUTH authentication workflow?
If there is no easy way, then I need a way to send the device info or IP address to the Lambda function triggers that Cognito is hooked with when calling:
const user = await Auth.signIn(formData.username, formData.password);
to sign in.
How can I do that with AWS Amplify?

Best way to change a users password with AWS Cognito Hosted UI?

We use AWS Cognito with the Host UI for login and signup.
We require a user to be able to change their password.
There is an auth flow state in Cognito called FORCE_CHANGE_PASSWORD. When the user is in this state, upon successful login via Hosted UI, the user is then instructed to change their password before they continue.
The issue is that we cant find a way to force a users state to be FORCE_CHANGE_PASSWORD. We are instead having to use AWS API to change a password.
We chose the Hosted UI option because we wanted to offload auth responsibility from our app. Having to use the AWS API to change a password means we are now pulling in 'auth stuff' into our stack (auth forms, val, api services, testing, ongoing maintenance) which we want to avoid/minimise.
Is there some way to set the users state to FORCE_CHANGE_PASSWORD? As this would keep everything in the Hosted UI, and solve our problem.
We were hoping to find an api method like adminSetUserState??

AWS Cognito - migrate users from external provider

I have a web application which uses Cognito to allow users to login. Cognito uses external provider (Okta). When user login first time user entry is created in user pool. I also have second application which has to use the same user pool, but because of specifics of this application I have to use email and password authentication.
I would like users to receive an email asking them to set (reset) their password when they first log in web app. What's a best way to achieve that?
As far as i know it's impossible to set password for EXTERNAL_PROVIDER users, but I can accept creating new (duplicating) users. But still I'd like to ask about best way to do this.
Instructions below are for migrating between cognito instances, but you might find them useful.
there is only one way to get this done is migration lambda trigger. In short:
create new cognito
create migration lambda
add this lambda as a trigger to login and/or forgotten passwords
point users at cognito (this might not be necessary in your instance)
upon login, Cognito will check locally and if user is not found, will use the trigger to check programmatically in another source.
If authentication is successful, old cognito will return object with all properties, incl passwords, which you can then insert into new cognito.
more info here: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-migrate-user.html

How to require admin confirmation of registered users with AWS Cognito?

I am using the Cognito Hosted UI option to register and sign-in users for my website. Currently, users who register are immediately able to sign-in using their username/password. This is an issue because this allows anyone to register and then to access restricted parts of the site. What I would like to do is require that the ADMIN manually confirm every registered user before they can login.
Is this possible to achieve?
If you want to manually confirm every user that has registered using your Cognito User Pool, you could perform the following steps:
Step 1: Ensure that E-Mail/SMS verification requirement is unchecked in the "MFA and Verifications" sidebar in your Amazon Cognito User Pool console.
Step 2: To improve user experience, utilize a custom UI for your web/mobile application. After your users sign-up, redirect them to a different web-page which states that they would require admin verification. If you are using the Cognito default UI, the message "User Pool not configured properly for confirmation code delivery" which doesn't necessarily deliver a seamless user experience.
Step 3: Now, your signed up users should have the "UNCONFIRMED" state in the Amazon Cognito User Pool.
Step 4: To manually confirm the user, you can use the AdminConfirmSignUp API call[1], from your application code or from the CLI. This requires the user-pool-id and the username, and would also need Administrator credentials for it to run successfully.
I tested this out on my end, and I was able to manually confirm all the users that had registered to my Amazon Cognito User Pool.
The API call I tried on my end is as follows(tested via the CLI):
aws cognito-idp admin-confirm-sign-up --user-pool-id us-east-1_XXXX --username XXXX
After the AdminConfirmSignUp API call, your "UNCONFIRMED" users should have the "CONFIRMED" status.
References
[1]. https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminConfirmSignUp.html

AWS cognito: sign in with usernam/password OR facebook

I want to integrate a pretty standard functionality: give option to user (mobile and web) to either login with email/password or with facebook (google) account with RBAC (different users may have different roles, like users, moderators, admins, creators, etc). Here is basically what I want from sign in:
I went through a number of AWS tutorials and other materials. I got some grasp on how to implement it, but I still don't have a full picture. Hope someone can help me here.
Here is my current understanding (please correct me where I'm wrong).
1) For the email/password signup/signin I use a User Pool. When user signs-in I call authenticateUser (I'm using JS SDK):
cognitoUser.authenticateUser(authenticationDetails, {
..
})
where onSuccess
I store identity, access and refresh tokens, so, user
doesn't have to enter his credentials every time
Because users will be accessing AWS servicess (e.g. S3) I exchange idToken to AWS credentials
Store AWS creds in LocalStore for further use, when access resources
2) For the facebook sign-in I use Federated Identity
get a facebook access token
with fb token get a cognito identity
exchange a cognito identity to AWS creds and store those in LocalStore
Questions:
Q1. Is it valid and fairly complete logic for sign-up/sign-in? Did I miss anything?
Q2. How should I store facebook users? Can I do it in User Pools? I have impression that it's not possible, but that means I have 2 different user directories: one in UserPool and another one in another place (lets say in DynamoDB)
Q3. If I have to store users in different places (UserPool and DynamoDB) that means I have 2 users for essentially one user, who first registered with email/password and then decided to use facebook - this is inconvenience for both me as app admin and user. How to deal with this situation?
Q4. How to manage groups for users, who signed-in with facebook token (like users, moderators, admins, creators, etc)?
Q5. How should I restrict access to resources other than AWS for facebook signed-in users?
Q6. Any working example for this?
Thanks!
We added support for Federation through Facebook, Google and LoginWithAmazon for User Pools. This will create a user in user pool when a user logs in with federation. You can also capture the attributes from the identity provider using the attribute mapping feature.
Also if you use the app integration feature, Amazon Cognito User Pools wil generate a sign-in page like this for you.
Steps to SignIn/SignUp with a social provider through Amazon Cognito Console:
Configure a domain for your user pool like .auth..amazoncognito.com
Add any social provider and configure attribute mapping.
Enable the provider on the App Client.
Configure the callback URI, OAuth response type and allowed scopes.
Access your hosted UI at https://.auth..amazoncognito.com/login?client_id=&response_type=&redirect_uri=
Click on the button to SignUp/SignIn with Facebook (or your provider).
Authenticate with the provider, you will be redirected to the callback URI with tokens/code.
Check the newly created user in Amazon Cognito console.
I'm human and may have missed something, but that sounds pretty good to me.
You can't store a federated identities login in user pools. Thing of user pools as another identity provider, just like Facebook is. Dynamo (or something else) would be the way to go.
If a user logged in with both, linking those logins, you might want to consider avoiding user pools attributes entirely and only using dynamo. With two logins linked, Cognito federated identities only requires one login token to proceed, but user pools requires it's login token to see/update attributes. The user would have to login with the user pool to touch those attributes, it'd get messy.
I don't know that this is supported out of the box, like it is with user pools. You might have to do this using your hypothetical user database described above.
You can also link your user pool to Cognito as a provider, much like you do for Facebook. That's how you exchange an id token for credentials.
No official example from the service, though I can't speak for others.