Multi-step sign-in
After a user has finished signup, they can proceed to sign in. Amplify Auth signin flows can be multi-step processes. The required steps are determined by the configuration provided when you define your auth resources. See the multi-factor authentication page for more information.
Depending on the configuration, you may need to call various APIs to finish authenticating a user's signin attempt. To identify the next step in a signin flow, inspect the nextStep
parameter of the signin result.
import { confirmSignIn, confirmSignUp, resetPassword, signIn,} from 'aws-amplify/auth';
const { nextStep } = await signIn({ username: 'hello@mycompany.com', password: 'hunter2',});
if ( nextStep.signInStep === 'CONFIRM_SIGN_IN_WITH_SMS_CODE' || nextStep.signInStep === 'CONFIRM_SIGN_IN_WITH_EMAIL_CODE' || nextStep.signInStep === 'CONFIRM_SIGN_IN_WITH_TOTP_CODE') { // collect OTP from user await confirmSignIn({ challengeResponse: '123456', });}
if (nextStep.signInStep === 'CONTINUE_SIGN_IN_WITH_MFA_SELECTION') { // present nextStep.allowedMFATypes to user // collect user selection await confirmSignIn({ challengeResponse: 'EMAIL', // 'EMAIL', 'SMS', or 'TOTP' });}
if (nextStep.signInStep === 'CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION') { // present nextStep.allowedMFATypes to user // collect user selection await confirmSignIn({ challengeResponse: 'EMAIL', // 'EMAIL' or 'TOTP' });}
if (nextStep.signInStep === 'CONTINUE_SIGN_IN_WITH_EMAIL_SETUP') { // collect email address from user await confirmSignIn({ challengeResponse: 'hello@mycompany.com', });}
if (nextStep.signInStep === 'CONTINUE_SIGN_IN_WITH_TOTP_SETUP') { // present nextStep.totpSetupDetails.getSetupUri() to user // collect OTP from user await confirmSignIn({ challengeResponse: '123456', });}
if (nextStep.signInStep === 'CONFIRM_SIGN_IN_WITH_PASSWORD') { // collect password from user await confirmSignIn({ challengeResponse: 'hunter2', });}
if (nextStep.signInStep === 'CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION') { // present nextStep.availableChallenges to user // collect user selection await confirmSignIn({ challengeResponse: 'SMS_OTP', // or 'EMAIL_OTP', 'WEB_AUTHN', 'PASSWORD', 'PASSWORD_SRP' });}
if (nextStep.signInStep === 'CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE') { // collect custom challenge answer from user await confirmSignIn({ challengeResponse: 'custom-challenge-answer', });}
if (nextStep.signInStep === 'CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED') { // collect new password from user await confirmSignIn({ challengeResponse: 'new-password', });}
if (nextStep.signInStep === 'RESET_PASSWORD') { // initiate reset password flow await resetPassword({ username: 'username', });}
if (nextStep.signInStep === 'CONFIRM_SIGN_UP') { // user was not confirmed during sign up process // if user has confirmation code, invoke `confirmSignUp` api // otherwise, invoke `resendSignUpCode` to resend the code await confirmSignUp({ username: 'username', confirmationCode: '123456', });}
if (nextStep.signInStep === 'DONE') { // signin complete}
Confirm sign-in with SMS MFA
If the next step is CONFIRM_SIGN_IN_WITH_SMS_CODE
, Amplify Auth has sent the user a random code over SMS and is waiting for the user to verify that code. To handle this step, your app's UI must prompt the user to enter the code. After the user enters the code, pass the value to the confirmSignIn
API.
import { type SignInOutput, confirmSignIn } from '@aws-amplify/auth';
async function handleSignInResult(result: SignInOutput) { switch (result.nextStep.signInStep) { case 'CONFIRM_SIGN_IN_WITH_SMS_CODE': { const { codeDeliveryDetails } = result.nextStep; // OTP has been delivered to user via SMS // Inspect codeDeliveryDetails for additional delivery information console.log( `A confirmation code has been sent to ${codeDeliveryDetails?.destination}`, ); console.log( `Please check your ${codeDeliveryDetails?.deliveryMedium} for the code.`, ); break; } }}
async function confirmMfaCode(mfaCode: string) { const result = await confirmSignIn({ challengeResponse: mfaCode });
return handleSignInResult(result);}
Confirm sign-in with TOTP MFA
If the next step is CONFIRM_SIGN_IN_WITH_TOTP_CODE
, you should prompt the user to enter the TOTP code from their associated authenticator app during set up. The code is a six-digit number that changes every 30 seconds. The user must enter the code before the 30-second window expires.
After the user enters the code, your implementation must pass the value to Amplify Auth confirmSignIn
API.
import { type SignInOutput, confirmSignIn } from '@aws-amplify/auth';
async function handleSignInResult(result: SignInOutput) { switch (result.nextStep.signInStep) { case 'CONFIRM_SIGN_IN_WITH_TOTP_CODE': { // Prompt user to open their authenticator app to retrieve the code console.log( `Enter a one-time code from your registered authenticator app`, ); break; } }}// Then, pass the TOTP code to `confirmSignIn`async function confirmTotpCode(totpCode: string) { const result = await confirmSignIn({ challengeResponse: totpCode });
return handleSignInResult(result);}
Confirm sign-in with Email MFA
If the next step is CONFIRM_SIGN_IN_WITH_EMAIL_CODE
, Amplify Auth has sent the user a random code to their email address and is waiting for the user to verify that code. To handle this step, your app's UI must prompt the user to enter the code. After the user enters the code, pass the value to the confirmSignIn
API.
import { type SignInOutput, confirmSignIn } from '@aws-amplify/auth';
async function handleSignInResult(result: SignInOutput) { switch (result.nextStep.signInStep) { case 'CONFIRM_SIGN_IN_WITH_EMAIL_CODE': { const { codeDeliveryDetails } = result.nextStep; // OTP has been delivered to user via Email // Inspect codeDeliveryDetails for additional delivery information console.log( `A confirmation code has been sent to ${codeDeliveryDetails?.destination}`, ); console.log( `Please check your ${codeDeliveryDetails?.deliveryMedium} for the code.`, ); break; } }}
async function confirmMfaCode(mfaCode: string) { const result = await confirmSignIn({ challengeResponse: mfaCode });
return handleSignInResult(result);}
Continue sign-in with MFA Selection
If the next step is CONTINUE_SIGN_IN_WITH_MFA_SELECTION
, the user must select the MFA method to use. Amplify Auth currently supports SMS, TOTP, and EMAIL as MFA methods. After the user selects an MFA method, your implementation must pass the selected MFA method to Amplify Auth using confirmSignIn
API.
The MFA types which are currently supported by Amplify Auth are:
SMS
TOTP
EMAIL
Once Amplify receives the users selection, you can expect to handle a follow up nextStep
corresponding with the selected MFA type for setup:
- If
SMS
is selected,CONFIRM_SIGN_IN_WITH_SMS_CODE
will be the next step. - If
TOTP
is selected,CONFIRM_SIGN_IN_WITH_TOTP_CODE
will be the next step. - If
EMAIL
is selected,CONFIRM_SIGN_IN_WITH_EMAIL_CODE
will be the next step.
import { type SignInOutput, confirmSignIn } from '@aws-amplify/auth';
async function handleSignInResult(result: SignInOutput) { switch (result.nextStep.signInStep) { case 'CONTINUE_SIGN_IN_WITH_MFA_SELECTION': { const { allowedMFATypes } = result.nextStep; // Present available MFA options to user // Prompt for selection console.log(`There are multiple MFA options available for sign in.`); console.log(`Select an MFA type from the allowedMfaTypes list.`); break; } }}
type MfaType = 'SMS' | 'TOTP' | 'EMAIL';
async function handleMfaSelection(mfaType: MfaType) { const result = await confirmSignIn({ challengeResponse: mfaType });
return handleSignInResult(result);}
Continue sign-in with Email Setup
If the next step is CONTINUE_SIGN_IN_WITH_EMAIL_SETUP
, then the user must provide an email address to complete the sign in process. Once this value has been collected from the user, call the confirmSignIn
API to continue.
import { type SignInOutput, confirmSignIn } from '@aws-amplify/auth';
async function handleSignInResult(result: SignInOutput) { switch (result.nextStep.signInStep) { case 'CONTINUE_SIGN_IN_WITH_EMAIL_SETUP': { // Prompt the user to enter an email address they would like to use for MFA break; } }}
// Then, pass the email address to `confirmSignIn`async function confirmEmail(email: string) { const result = await confirmSignIn({ challengeResponse: email });
return handleSignInResult(result);}
Continue sign-in with TOTP Setup
The CONTINUE_SIGN_IN_WITH_TOTP_SETUP
step signifies that the user must set up TOTP before they can sign in. The step returns an associated value of type TOTPSetupDetails which must be used to configure an authenticator app like Microsoft Authenticator or Google Authenticator. TOTPSetupDetails provides a helper method called getSetupURI which generates a URI that can be used, for example, in a button to open the user's installed authenticator app. For more advanced use cases, TOTPSetupDetails also contains a sharedSecret which can be used to either generate a QR code or be manually entered into an authenticator app.
Once the authenticator app is set up, the user can generate a TOTP code and provide it to the library to complete the sign in process.
import { type SignInOutput, confirmSignIn } from '@aws-amplify/auth';
async function handleSignInResult(result: SignInOutput) { switch (result.nextStep.signInStep) { case 'CONTINUE_SIGN_IN_WITH_TOTP_SETUP': { const { totpSetupDetails } = result.nextStep; const appName = 'my_app_name'; const setupUri = totpSetupDetails.getSetupUri(appName); // Open setupUri with an authenticator app // Prompt user to enter OTP code to complete setup break; } }}
// Then, pass the collected OTP code to `confirmSignIn`async function confirmTotpCode(totpCode: string) { const result = await confirmSignIn({ challengeResponse: totpCode });
return handleSignInResult(result);}
Continue sign-in with MFA Setup Selection
If the next step is CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION
, then the user must indicate which of the available MFA methods they would like to setup. After the user selects an MFA method to setup, your implementation must pass the selected MFA method to the confirmSignIn
API.
The MFA types which are currently supported by Amplify Auth for setup are:
TOTP
EMAIL
Once Amplify receives the users selection, you can expect to handle a follow up nextStep
corresponding with the selected MFA type for setup:
- If
EMAIL
is selected,CONTINUE_SIGN_IN_WITH_EMAIL_SETUP
will be the next step. - If
TOTP
is selected,CONTINUE_SIGN_IN_WITH_TOTP_SETUP
will be the next step.
import { type SignInOutput, confirmSignIn } from '@aws-amplify/auth';
async function handleSignInResult(result: SignInOutput) { switch (result.nextStep.signInStep) { case 'CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION': { const { allowedMFATypes } = result.nextStep; // Present available MFA options to user // Prompt for selection console.log(`There are multiple MFA options available for setup.`); console.log(`Select an MFA type from the allowedMFATypes list.`); break; } }}
type MfaType = 'SMS' | 'TOTP' | 'EMAIL';
async function handleMfaSelection(mfaType: MfaType) { const result = await confirmSignIn({ challengeResponse: mfaType });
return handleSignInResult(result);}
Confirm sign-in with Password
If the next step is CONFIRM_SIGN_IN_WITH_PASSWORD
, the user must provide their password as the first factor authentication method. To handle this step, your implementation should prompt the user to enter their password. After the user enters the password, pass the value to the confirmSignIn
API.
import { type SignInOutput, confirmSignIn } from '@aws-amplify/auth';
async function handleSignInResult(result: SignInOutput) { switch (result.nextStep.signInStep) { case 'CONFIRM_SIGN_IN_WITH_PASSWORD': { // Prompt user to enter their password console.log(`Please enter your password.`); break; } }}
async function confirmWithPassword(password: string) { const result = await confirmSignIn({ challengeResponse: password });
return handleSignInResult(result);}
Continue sign-in with First Factor Selection
If the next step is CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION
, the user must select a first factor method for authentication. After the user selects an option, your implementation should pass the selected method to the confirmSignIn
API.
The first factor types which are currently supported by Amplify Auth are:
SMS_OTP
EMAIL_OTP
WEB_AUTHN
PASSWORD
PASSWORD_SRP
Depending on your configuration and what factors the user has previously setup, not all options may be available. Only the available options will be presented in availableChallenges
for selection.
Once Amplify receives the user's selection via the confirmSignIn
API, you can expect to handle a follow up nextStep
corresponding with the first factor type selected:
- If
SMS_OTP
is selected,CONFIRM_SIGN_IN_WITH_SMS_CODE
will be the next step. - If
EMAIL_OTP
is selected,CONFIRM_SIGN_IN_WITH_EMAIL_CODE
will be the next step. - If
PASSWORD
orPASSWORD_SRP
is selected,CONFIRM_SIGN_IN_WITH_PASSWORD
will be the next step. - If
WEB_AUTHN
is selected, Amplify Auth will initiate the authentication ceremony on the user's device. If successful, the next step will beDONE
.
import { type SignInOutput, confirmSignIn } from '@aws-amplify/auth';
async function handleSignInResult(result: SignInOutput) { switch (result.nextStep.signInStep) { case 'CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION': { const { availableChallenges } = result.nextStep; // Present available first factor options to user // Prompt for selection console.log( `There are multiple first factor options available for sign in.`, ); console.log( `Select a first factor type from the availableChallenges list.`, ); break; } }}
async function handleFirstFactorSelection(firstFactorType: string) { const result = await confirmSignIn({ challengeResponse: firstFactorType });
return handleSignInResult(result);}
Confirm sign-in with custom challenge
If the next step is CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE
, Amplify Auth is awaiting completion of a custom authentication challenge. The challenge is based on the AWS Lambda trigger you configured as part of a custom sign in flow.
For example, your custom challenge Lambda may pass a prompt to the frontend which requires the user to enter a secret code.
import { type SignInOutput, confirmSignIn } from '@aws-amplify/auth';
async function handleSignInResult(result: SignInOutput) { switch (result.nextStep.signInStep) { case 'CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE': { const params = result.nextStep.additionalInfo; const hint = params.hint!; // Prompt user to enter custom challenge response console.log(hint); // `Enter the secret code` break; } }}
To complete this step, you should prompt the user for the custom challenge answer, and pass the answer to the confirmSignIn
API.
async function confirmCustomChallenge(answer: string) { const result = await confirmSignIn({ challengeResponse: answer });
return handleSignInResult(result);}
Confirm sign-in with new password
If the next step is CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED
, Amplify Auth requires the user choose a new password they proceeding with the sign in.
Prompt the user for a new password and pass it to the confirmSignIn
API.
See the sign-in and manage-password docs for more information.
import { type SignInOutput, confirmSignIn } from '@aws-amplify/auth';
async function handleSignInResult(result: SignInOutput) { switch (result.nextStep.signInStep) { case 'CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED': { // Prompt user to enter a new password console.log(`Please enter a new password.`); break; } }}
async function confirmNewPassword(newPassword: string) { const result = await confirmSignIn({ challengeResponse: newPassword });
return handleSignInResult(result);}
Reset password
If the next step is RESET_PASSWORD
, Amplify Auth requires that the user reset their password before proceeding.
Use the resetPassword
API to guide the user through resetting their password, then call signIn
to restart the sign-in flow.
See the reset password docs for more information.
import { type ResetPasswordOutput, type SignInOutput, resetPassword,} from '@aws-amplify/auth';
async function handleSignInResult(result: SignInOutput) { switch (result.nextStep.signInStep) { case 'RESET_PASSWORD': { const resetPasswordResult = await resetPassword({ username }); // initiate reset password flow await handleResetPasswordResult(resetPasswordResult); break; } }}
async function handleResetPasswordResult( resetPasswordResult: ResetPasswordOutput,) { switch (resetPasswordResult.nextStep.resetPasswordStep) { case 'CONFIRM_RESET_PASSWORD_WITH_CODE': { const { codeDeliveryDetails } = resetPasswordResult.nextStep; console.log( `A confirmation code has been sent to ${codeDeliveryDetails.destination}.`, ); console.log( `Please check your ${codeDeliveryDetails.destination} for the code.`, ); break; } case 'DONE': { console.log(`Successfully reset password.`); break; } }}
Confirm Signup
If the next step is CONFIRM_SIGN_UP
, Amplify Auth requires that the user confirm their email or phone number before proceeding.
Use the resendSignUpCode
API to send a new sign up code to the registered email or phone number, followed by confirmSignUp
to complete the sign up.
See the sign up docs for more information.
import { type SignInOutput, confirmSignUp, resendSignUpCode,} from '@aws-amplify/auth';
async function handleSignInResult(result: SignInOutput) { switch (result.nextStep.signInStep) { case 'CONFIRM_SIGN_UP': { // Resend sign up code to the registered user const { destination, deliveryMedium } = await resendSignUpCode({ username, }); console.log(`A confirmation code has been sent to ${destination}.`); console.log(`Please check your ${deliveryMedium} for the code.`); break; } }}
async function handleConfirmSignUp(username: string, confirmationCode: string) { await confirmSignUp({ username, confirmationCode, });}
Once the sign up is confirmed, call signIn
again to restart the sign-in flow.
Done
The sign-in flow is complete when the next step is DONE
, which means the user is successfully authenticated.
As a convenience, the SignInResult
also provides the isSignedIn
property, which will be true if the next step is DONE
.
import { type SignInOutput } from '@aws-amplify/auth';
async function handleSignInResult(result: SignInOutput) { switch (result.nextStep.signInStep) { case 'DONE': { // `result.isSignedIn` is `true` console.log(`Sign in is complete.`); break; } }}