Name:
interface
Value:
Amplify has re-imagined the way frontend developers build fullstack applications. Develop and deploy without the hassle.

Page updated Dec 9, 2024

Switching authentication flows

AWSCognitoAuthPlugin allows you to switch between different auth flows while initiating signIn. You can configure the flow in the amplify_outputs.json file or pass the authFlowType as a option to the signIn api call.

For client side authentication there are four different flows that can be configured during runtime:

  1. USER_SRP_AUTH: The USER_SRP_AUTH flow uses the SRP protocol (Secure Remote Password) where the password never leaves the client and is unknown to the server. This is the recommended flow and is used by default.

  2. USER_PASSWORD_AUTH: The USER_PASSWORD_AUTH flow will send user credentials unencrypted to the back-end. If you want to migrate users to Cognito using the "Migration" trigger and avoid forcing users to reset their passwords, you will need to use this authentication type because the Lambda function invoked by the trigger needs to verify the supplied credentials.

  3. CUSTOM_AUTH_WITH_SRP: The CUSTOM_AUTH_WITH_SRP flow is used to start with SRP authentication and then switch to custom authentication. This is useful if you want to use SRP for the initial authentication and then use custom authentication for subsequent authentication attempts.

  4. CUSTOM_AUTH_WITHOUT_SRP: The CUSTOM_AUTH_WITHOUT_SRP flow is used to start authentication flow WITHOUT SRP and then use a series of challenge and response cycles that can be customized to meet different requirements.

  5. USER_AUTH: The USER_AUTH flow is a choice-based authentication flow that allows the user to choose from the list of available authentication methods. This flow is useful when you want to provide the user with the option to choose the authentication method. The choices that may be available to the user are EMAIL_OTP, SMS_OTP, WEB_AUTHN, PASSWORD or PASSWORD_SRP.

Auth can be configured to use the different flows at runtime by calling signIn with AWSCognitoAuthSignInOptions's authFlowType as AuthFlowType.USER_PASSWORD_AUTH, AuthFlowType.CUSTOM_AUTH_WITHOUT_SRP, AuthFlowType.CUSTOM_AUTH_WITH_SRP, or AuthFlowType.USER_AUTH. If you do not specify the AuthFlowType in AWSCognitoAuthSignInOptions, the default flow specified in amplify_outputs.json will be used.

Runtime configuration will take precedence and will override any auth flow type configuration present in amplify_outputs.json.

For more information about authentication flows, please visit Amazon Cognito developer documentation

USER_AUTH (Choice-based authentication) flow

A use case for the USER_AUTH authentication flow is to provide the user with the option to choose the authentication method. The choices that may be available to the user are EMAIL_OTP, SMS_OTP, WEB_AUTHN, PASSWORD or PASSWORD_SRP.

Amplify requires an Activity reference to attach the PassKey UI to your Application's Task when using WebAuthn - if an Activity is not supplied then the UI will appear in a separate Task. For this reason, we strongly recommend passing the callingActivity option to both the signIn and confirmSignIn APIs if your application uses the USER_AUTH flow.

If the desired first factor is known before the sign in flow is initiated it can be passed to the initial sign in call.

// PASSWORD_SRP / PASSWORD
// Sign in with preferred challenge as password
// NOTE: Password must be provided in the same step
AuthSignInOptions options = AWSCognitoAuthSignInOptions.builder()
.callingActivity(activity)
.authFlowType(AuthFlowType.USER_AUTH)
.preferredFirstFactor(AuthFactorType.PASSWORD_SRP) // or "PASSWORD"
.build();
Amplify.Auth.signIn(
username,
password,
options,
result -> Log.i("AuthQuickStart", "Next step for sign in is " + result.getNextStep()),
error -> Log.e("AuthQuickStart", "Failed to confirm sign in", error)
);
// WEB_AUTHN / EMAIL_OTP / SMS_OTP
// Sign in with preferred passwordless challenge
// No user input is required at this step
AuthSignInOptions options = AWSCognitoAuthSignInOptions.builder()
.callingActivity(activity)
.authFlowType(AuthFlowType.USER_AUTH)
.preferredFirstFactor(AuthFactorType.WEB_AUTHN) // or "EMAIL_OTP" or "SMS_OTP"
.build();
Amplify.Auth.signIn(
username,
null,
options,
result -> Log.i("AuthQuickStart", "Next step for sign in is " + result.getNextStep()),
error -> Log.e("AuthQuickStart", "Failed to confirm sign in", error)
);
// PASSWORD_SRP / PASSWORD
// Sign in with preferred challenge as password
// NOTE: Password must be provided in the same step
val options = AWSCognitoAuthSignInOptions.builder()
.callingActivity(activity)
.authFlowType(AuthFlowType.USER_AUTH)
.preferredFirstFactor(AuthFactorType.PASSWORD_SRP) // or "PASSWORD"
.build()
Amplify.Auth.signIn(
username,
password,
options,
{ result -> Log.i("AuthQuickStart", "Next step for sign in is ${result.nextStep}") },
{ error -> Log.e("AuthQuickStart", "Failed to confirm sign in", error) }
)
// WEB_AUTHN / EMAIL_OTP / SMS_OTP
// Sign in with preferred passwordless challenge
// No user input is required at this step
val options = AWSCognitoAuthSignInOptions.builder()
.callingActivity(activity)
.authFlowType(AuthFlowType.USER_AUTH)
.preferredFirstFactor(AuthFactorType.WEB_AUTHN) // or "EMAIL_OTP" or "SMS_OTP"
.build()
Amplify.Auth.signIn(
username,
null,
options,
{ result -> Log.i("AuthQuickStart", "Next step for sign in is ${result.nextStep}") },
{ error -> Log.e("AuthQuickStart", "Failed to confirm sign in", error) }
)
// PASSWORD_SRP / PASSWORD
// Sign in with preferred challenge as password
// NOTE: Password must be provided in the same step
try {
val options = AWSCognitoAuthSignInOptions.builder()
.callingActivity(activity)
.authFlowType(AuthFlowType.USER_AUTH)
.preferredFirstFactor(AuthFactorType.PASSWORD_SRP) // or "PASSWORD"
.build()
val result = Amplify.Auth.signIn(
username = "hello@example.com",
password = "password",
options = options
)
Log.i("AuthQuickstart", "Next step for sign in is ${result.nextStep}")
} catch (error: AuthException) {
Log.e("AuthQuickstart", "Sign in failed", error)
}
// WEB_AUTHN / EMAIL_OTP / SMS_OTP
// Sign in with preferred passwordless challenge
// No user input is required at this step
try {
val options = AWSCognitoAuthSignInOptions.builder()
.callingActivity(activity)
.authFlowType(AuthFlowType.USER_AUTH)
.preferredFirstFactor(AuthFactorType.WEB_AUTHN) // or "EMAIL_OTP" or "SMS_OTP"
.build()
val result = Amplify.Auth.signIn(
username = "hello@example.com",
password = null,
options = options
)
Log.i("AuthQuickstart", "Next step for sign in is ${result.nextStep}")
} catch (error: AuthException) {
Log.e("AuthQuickstart", "Sign in failed", error)
}
// PASSWORD_SRP / PASSWORD
// Sign in with preferred challenge as password
// NOTE: Password must be provided in the same step
AuthSignInOptions options = AWSCognitoAuthSignInOptions.builder()
.callingActivity(activity)
.authFlowType(AuthFlowType.USER_AUTH)
.preferredFirstFactor(AuthFactorType.PASSWORD_SRP) // or "PASSWORD"
.build();
RxAmplify.Auth.signIn(username, password, options)
.subscribe(
result -> Log.i("AuthQuickstart", "Next step for sign in is " + result.getNextStep()),
error -> Log.e("AuthQuickstart", "Failed to confirm sign in", error))
);
// WEB_AUTHN / EMAIL_OTP / SMS_OTP
// Sign in with preferred passwordless challenge
// No user input is required at this step
AuthSignInOptions options = AWSCognitoAuthSignInOptions.builder()
.callingActivity(activity)
.authFlowType(AuthFlowType.USER_AUTH)
.preferredFirstFactor(AuthFactorType.WEB_AUTHN) // or "EMAIL_OTP" or "SMS_OTP"
.build();
RxAmplify.Auth.signIn(username, null, options)
.subscribe(
result -> Log.i("AuthQuickstart", "Next step for sign in is " + result.getNextStep()),
error -> Log.e("AuthQuickstart", "Failed to confirm sign in", error))
);

If the preferred first factor is not supplied or is unavailable, and the user has multiple factors available, the flow will continue to select an available first factor by returning an AuthNextSignInStep.signInStep value of CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION, and a list of AuthNextSignInStep.availableFactors.

The selection of the authentication method is done by the user. The user can choose from the available factors and proceed with the selected factor. You should call the confirmSignIn API with the selected factor to continue the sign-in process. Following is an example if you want to proceed with the WEB_AUTHN factor selection:

AuthConfirmSignInOptions options = AWSCognitoAuthConfirmSignInOptions.builder()
.callingActivity(activity)
.build();
Amplify.Auth.confirmSignIn(
AuthFactorType.WEB_AUTHN.getChallengeResponse(),
options,
result -> Log.i("AuthQuickStart", "Next step for sign in is " + result.getNextStep()),
error -> Log.e("AuthQuickStart", "Failed to confirm sign in", error)
);
val options = AWSCognitoAuthConfirmSignInOptions.builder()
.callingActivity(activity)
.build()
Amplify.Auth.confirmSignIn(
AuthFactorType.WEB_AUTHN.challengeResponse,
options,
{ result -> Log.i("AuthQuickStart", "Next step for sign in is ${result.nextStep}") },
{ error -> Log.e("AuthQuickStart", "Failed to confirm sign in", error) }
)
try {
val options = AWSCognitoAuthConfirmSignInOptions.builder()
.callingActivity(activity)
.build()
val result = Amplify.Auth.confirmSignIn(
challengeResponse = AuthFactorType.WEB_AUTHN.challengeResponse,
options = options
)
Log.i("AuthQuickstart", "Next step for sign in is ${result.nextStep}")
} catch (error: AuthException) {
Log.e("AuthQuickstart", "Sign in failed", error)
}
AuthConfirmSignInOptions options = AWSCognitoAuthConfirmSignInOptions.builder()
.callingActivity(activity)
.build();
RxAmplify.Auth.confirmSignIn(AuthFactorType.WEB_AUTHN.getChallengeResponse(), options)
.subscribe(
result -> Log.i("AuthQuickstart", "Next step for sign in is " + result.getNextStep()),
error -> Log.e("AuthQuickstart", "Failed to confirm sign in", error))
);

USER_PASSWORD_AUTH flow

A use case for the USER_PASSWORD_AUTH authentication flow is migrating users into Amazon Cognito

A user migration Lambda trigger helps migrate users from a legacy user management system into your user pool. If you choose the USER_PASSWORD_AUTH authentication flow, users don't have to reset their passwords during user migration. This flow sends your user's password to the service over an encrypted SSL connection during authentication.

When you have migrated all your users, switch flows to the more secure SRP flow. The SRP flow doesn't send any passwords over the network.

AuthSignInOptions options = AWSCognitoAuthSignInOptions.builder()
.authFlowType(AuthFlowType.USER_PASSWORD_AUTH)
.build();
Amplify.Auth.signIn(
"hello@example.com",
"password",
options,
result -> Log.i("AuthQuickStart", "Sign in succeeded with result " + result),
error -> Log.e("AuthQuickStart", "Failed to sign in", error)
);
val options = AWSCognitoAuthSignInOptions.builder()
.authFlowType(AuthFlowType.USER_PASSWORD_AUTH)
.build()
Amplify.Auth.signIn(
"hello@example.com",
"password",
options,
{ result ->
Log.i("AuthQuickstart", "Next step for sign in is ${result.nextStep}")
},
{ error ->
Log.e("AuthQuickstart", "Failed to sign in", error)
}
)
try {
val options = AWSCognitoAuthSignInOptions.builder()
.authFlowType(AuthFlowType.USER_PASSWORD_AUTH)
.build()
val result = Amplify.Auth.signIn(
username = "hello@example.com",
password = "password",
options = options
)
Log.i("AuthQuickstart", "Next step for sign in is ${result.nextStep}")
} catch (error: AuthException) {
Log.e("AuthQuickstart", "Sign in failed", error)
}
AuthSignInOptions options = AWSCognitoAuthSignInOptions.builder()
.authFlowType(AuthFlowType.USER_PASSWORD_AUTH)
.build();
RxAmplify.Auth.signIn("hello@example.com", "password", options)
.subscribe(
result -> Log.i("AuthQuickstart", "Next step for sign in is " + result.getNextStep()),
error -> Log.e("AuthQuickstart", error.toString())
);

Set up auth backend

In order to use the authentication flow USER_PASSWORD_AUTH, your Cognito app client has to be configured to allow it. Amplify Gen 2 enables SRP auth by default. To enable USER_PASSWORD_AUTH, you can update the backend.ts file with the following changes:

amplify/backend.ts
import { defineBackend } from '@aws-amplify/backend'
import { auth } from './auth/resource'
import { data } from './data/resource'
const backend = defineBackend({
auth,
data,
});
backend.auth.resources.cfnResources.cfnUserPoolClient.explicitAuthFlows = [
"ALLOW_USER_PASSWORD_AUTH",
"ALLOW_USER_SRP_AUTH",
"ALLOW_USER_AUTH",
"ALLOW_REFRESH_TOKEN_AUTH"
];

Migrate users with Amazon Cognito

Amazon Cognito provides a trigger to migrate users from your existing user directory seamlessly into Cognito. You achieve this by configuring your User Pool's "Migration" trigger which invokes a Lambda function whenever a user that does not already exist in the user pool authenticates, or resets their password.

In short, the Lambda function will validate the user credentials against your existing user directory and return a response object containing the user attributes and status on success. An error message will be returned if an error occurs. There's documentation around how to set up this migration flow and more detailed instructions on how the lambda should handle request and response objects.

CUSTOM_AUTH flow

Amazon Cognito User Pools supports customizing the authentication flow to enable custom challenge types, in addition to a password in order to verify the identity of users. The custom authentication flow is a series of challenge and response cycles that can be customized to meet different requirements. These challenge types may include CAPTCHAs or dynamic challenge questions.

To define your challenges for custom authentication flow, you need to implement three Lambda triggers for Amazon Cognito.

The flow is initiated by calling signIn with AWSCognitoAuthSignInOptions configured with AuthFlowType.CUSTOM_AUTH_WITH_SRP OR AuthFlowType.CUSTOM_AUTH_WITHOUT_SRP.

Follow the instructions in Custom Auth Sign In to learn about how to integrate custom authentication flow in your application with the Auth APIs.

For more information about working with Lambda Triggers for custom authentication challenges, please visit Amazon Cognito Developer Documentation.