AWS Cognito custom auth flow with USER_PASSWORD_AUTH - amazon-web-services

I'm currently working on a custom authentication flow, using the define, create and verify triggers. However, the users password isn't checked during the flow. We use the USER_PASSWORD_AUTH option on our clients, so no SRP.
I saw this question Can I use the migration trigger in a Custom auth flow and didn't quite make out if it answered my question:
Is it possible to use custom auth flow in combination with username-password (non-SRP) flow? And if so, what is the challenge name that I have to return?
Here is stated that combinations can be used, but it seems to me that the PASSWORD_VERIFIER only works with the SRP auth:
A custom authentication flow can also use a combination of built-in challenges such as SRP password verification and MFA via SMS, and custom challenges such as CAPTCHA or secret questions.

So I managed to add the password challenge to the custom auth flow, by returning it as the first challenge in the DefineAuthChallenge lambda trigger, like this:
// Add the password verifier to verify the password first.
if (input.Request?.Session == null || !input.Request.Session.Any(s => s.ChallengeName == "PASSWORD_VERIFIER"))
{
input.Response.ChallengeName = AuthChallengeNames.AWS_PasswordVerifier;
input.Response.FailAuthentication = false;
input.Response.IssueTokens = false;
return input;
}
No challenges are given in the session, as this should be the first challenge to be returned by the custom auth flow, as described here (section 'Custom Authentication Flow'):
If you want to include SRP in a custom authentication flow, you need to start with it.
However, at the moment, if a user is forced to change their password, the custom auth flow is skipped afterwards, which is a bug at the moment, confirmed by AWS. See related post here.
The example here (section 'Define Auth Challenge Example') proved to be blatantly wrong, as there are no challenges in the session the first time the define auth challenge trigger is hit.

Related

Is it necessary to use triggers in CUSTOM_AUTH of AWS cognito?

I want to create OTP based authentication using AWS cognito (CUSTOM_AUTH), all resources I saw were having 3 triggers(defineauth, createauth etc). Can I implement custom auth without using them ?
You need all three. The first (Define Auth Challenge) lets you define the cognito auth statemachine execution (can include built in challenges). The second (Create Auth Challenge) sets up the challenge and defines the values to check against. The last (Verify Auth Challenge Response) lets you perform tests against the response and define whether the challenge succeeded. That gets passed back into the state machine which calls back to the first handler (Define Auth Challenge) which is where you can declare whether the user successfully authenticated.
Amazon has some examples of their own. I've seen a few random tutorials post flows that have security flaws so I'd stick with AWS for this.
Email OTP example can be found here.

AWS Cognito - run another lambda after migration lambda has run

Cognito has a migration lambda that allows us to confirm a user in our db. They send the email and PW to Cognito, the lambda fires, we verify matches, and the user is entered into Cognito.
At this point - behind the scenes - Cognito generates a username of some kind (UUID). The problem is, I need a way to get this username into our existing database, because our systems going forward will no longer rely on email and instead rely on this username.
Ideal flow:
Sign In
Migration Succeeds
Cognito generates username
Username is sent to our server.
Now because we have email set to auto-verified, no post-confirmation lambda can be called. The only way I see to do this with Cognito as-is is to either:
Ask users who already exist in our system to confirm their email again. This is a non-starter
Create a post-auth lambda, check user login count through a custom attribute, and if 0 (or if not already registered with the service, etc.) migrate the username to the new service.
If there is any other way to do this, please let me know.
After the user migration lambda is called your pre sign-up lambda will be called, assuming you have implemented it. The parameters received by your lambda will include username with the value being the UID you referenced. Parameters will also include user attributes containing email. You can use this information to update your database.
I did not want to add the PreSignup trigger, its a complicated way of doing it if you already rely on PostConfirmation, and if the majority of new users won't be migrations. My use case has a frontend initiate the signup process as well, which I use here.
Instead, I set a Cognito attribute on the new user during the UserMigration trigger. It could be 'user_migration': <oldUserSub>, or however you want to mark it. Just make sure you allow this property within the Cognito user pool settings.
When the UserMigration trigger returns, this information is now accessible through verifying the IdToken, or found in the JWT on the frontend if you're using that. So, when the user is migrated into Cognito and the response gets back to the Cognito client on the frontend, I can now recognize this user needs to be migrated into my personal database. Seeing this, I'll call a new endpoint on my backend to handle this. This new endpoint does exactly what PostConfirmation would typically do.
Then just delete the 'user_migration' property from the Cognito user, return the new user data to the frontend and everything should be set up.
You can use Pre sign-up trigger. In order to detect if the trigger event came from your migration trigger, you can check at the trigger_source value from the event object. In my case (i'm using migration trigger) the value is PreSignUp_AdminCreateUser. By knowing the value of trigger_source you can differentiate if it was migrated or regular user. You can also check the user attributes to know whether the email or phone is verified or not.
Here's my sample code on python:
def lambda_handler(event, context):
trigger_source = event.get('triggerSource')
user_attributes = request.get('userAttributes')
email_verified = user_attributes.get('email_verified')
if trigger_source == 'PreSignUp_AdminCreateUser' and email_verified == 'true':
# create user on db

Validating the password requested during sign up with AWS Cognito User Pool

Is there a way to see the password within the Lambda triggers that can be fired off by a sign-up or password change on AWS Cognito User Pool?
I want to get the password and compare it to lists of previously compromised passwords (the haveibeenpwned lists) to ensure password strength at a much higher level than what is possible by the arbitrary complexity rules that can be defeated by garbage like "Password!23"
The answer is that it currently is not possible to do as simply as I had hoped.
What you would have to do is to turn on the USER_PASSWORD_AUTH type authentication flow which means the password is sent in the HTTP POST request and setup your own reverse proxy to take the request, check the password details and then forward the request onto Cognito, accept the response and then send it back to the browser.
Or just turn on the "Advanced Security Features" option that they give you and tick the box for checking passwords against compromised lists... which is what I imagine AWS is trying to force you to do.
If you are using Amazon Cognito than you should not take worry about passwords and its complexity. It is totally managed by Cognito.
Your authentication will be based on Tokens only :)
There is no way to retrieve the password from lambda trigger.
But yes, You can do one thing, Set status to FORCE CHANGE PASSWORD. So that user must have to reset password after first login.
After that, whenever your user try to change password, You can add your own constrains in your UI before calling ChangePassword API
I hope this helps!

Combine client_credentials and Custom authentication flow in Cognito?

Wondering if it's possible to combine using the client credentials OAuth flow in Cognito with a custom authentication flow as described here: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-challenge.html
I know about custom authentication flows in Cognito, but I am not so well versed in OAuth itself. But as far as I understand it, the client credentials flow, is unrelated to a user? Because in that case, I would think it is impossible to use a custom authentication flow since the SDK documentation states the following (taken from the AWS node.js SDK):
The authentication parameters. These are inputs corresponding to the AuthFlow that you are invoking. The required values depend on the value of AuthFlow:
[...] For CUSTOM_AUTH: USERNAME (required)
If the value passed as USERNAME in the CUSTOM_AUTH flow is not a known user in Cognito, the lambda trigger for the custom flow will never be executed. And since it is never executed, it is not possible to use the USERNAME parameter to pass any other data to the lambda trigger (you can of course use custom authentication parameters, but you also need a valid USERNAME).

How do I get the Amazon Cognito hosted UI to prompt for TOTP?

I am assuming that I would be prompted based off of the documentation, which specifically states:
If your app is using the Amazon Cognito hosted UI to sign in users, the UI shows a second page for your user to enter the TOTP password after they submit their user name and password.
Under the "MFA and Verifications" section of the user pool, I have checked the following:
Do you want to enable Multi-Factor Authentication (MFA)?
Optional
Which second factors do you want to enable?
Time-based One-time Password
I have added a single test user that is verified.
From there, I followed the documentation to both Associate the TOTP Token and Verify the TOTP Token, confirming I got the secret code in the response for calling AssociateSoftwareToken and a 'SUCCESS' in the response for VerifySoftwareToken.
At this point, I believe when I use the hosted UI sign-in page, I should be prompted to enter a one-time-password after submitting my username/password, and upon successful verification of that, be redirected to the signin callback URL specfied in my app client.
However, I am being redirected immediately after submitting the username and password and there is no prompt for entering a TOTP.
I was able to get this to work by explicitly calling SetUserMFAPreference after setting up TOTP for the test account. My assumption that associating and verifying TOTP automatically changed Cognito's behavior with respect to the authentication flow of the user was mistaken. It also required me to tell Cognito to enable and use the TOTP for the user.
The crux of my original confusion was that generating and associating a software token to generate OTPs for a user did not enable it for the user. A call to SetUserMFAPreference to enable it for the user was also required. Once that was done, it worked as expected. For instance, to enable software MFA and set it as preferred:
{
"AccessToken": "xyz123",
"SoftwareTokenMfaSettings": {
"Enabled": true,
"PreferredMfa": true
}
}
There is also an admin version of the API call that can achieve the same result.
For anyone else who stumbles upon this and still isn't getting prompted for their TOTP, you may also need to clear your cookies. Even if your pool is not set up to remember user devices, without clearing the cookies you may still be able to log in without the TOTP.
After messing around with this problem, I reckon that AWS just gave up on this and moved towards using Amplify.
Use the Amplify libraries and their Amplify UI components.
The Auth component will prompt the user at first login with a QR code.
https://docs.amplify.aws/lib/auth/getting-started/q/platform/js/#option-1-use-pre-built-ui-components