diff --git a/cspell.json b/cspell.json index e4ee6df3832..80ac1464f23 100644 --- a/cspell.json +++ b/cspell.json @@ -851,6 +851,7 @@ "metadata", "mfaDescription", "mfaTypes", + "enabledMfas", "MiB", "middleware", "Millis", diff --git a/src/constants/versions.ts b/src/constants/versions.ts index 9d46720b5a8..dacf5ba9a81 100644 --- a/src/constants/versions.ts +++ b/src/constants/versions.ts @@ -1,10 +1,10 @@ export const versions = { - ANDROID_VERSION: '2.19.1', + ANDROID_VERSION: '2.24.0', ANDROID_DEVPREVIEW: '1.36.5-dev-preview.0', ANDROID_V1_VERSION: '1.38.8', ANDROID_V1_GEO_VERSION: '1.0.1', ANDROID_V1_KOTLIN_VERSION: '0.22.8', ANDROID_SDK_VERSION: '2.76.0', - KOTLIN_SDK_VERSION: '1.2.8', - ANDROID_AUTHENTICATOR_VERSION: '1.2.0' + KOTLIN_SDK_VERSION: '1.3.31', + ANDROID_AUTHENTICATOR_VERSION: '1.4.0' }; diff --git a/src/directory/directory.mjs b/src/directory/directory.mjs index d251f16f1be..5112f863ba0 100644 --- a/src/directory/directory.mjs +++ b/src/directory/directory.mjs @@ -122,6 +122,9 @@ export const directory = { }, { path: 'src/pages/[platform]/build-a-backend/auth/connect-your-frontend/delete-user-account/index.mdx' + }, + { + path: 'src/pages/[platform]/build-a-backend/auth/connect-your-frontend/multi-step-sign-in/index.mdx' } ] }, @@ -156,9 +159,6 @@ export const directory = { } ] }, - { - path: 'src/pages/[platform]/build-a-backend/auth/multi-step-sign-in/index.mdx' - }, { path: 'src/pages/[platform]/build-a-backend/auth/sign-in-with-web-ui/index.mdx' }, diff --git a/src/pages/[platform]/build-a-backend/auth/concepts/multi-factor-authentication/index.mdx b/src/pages/[platform]/build-a-backend/auth/concepts/multi-factor-authentication/index.mdx index 122d25d0f6b..731e5b31427 100644 --- a/src/pages/[platform]/build-a-backend/auth/concepts/multi-factor-authentication/index.mdx +++ b/src/pages/[platform]/build-a-backend/auth/concepts/multi-factor-authentication/index.mdx @@ -28,11 +28,15 @@ export function getStaticProps() { }; } -Amplify Auth supports Multi-factor Authentication (MFA) for user sign-in flows. MFA is an extra layer of security used to make sure that users trying to gain access to an account are who they say they are. It requires users to provide additional information to verify their identity. Amplify Auth supports the MFA methods with Time-based-One-Time Passwords (TOTP) as well as text messages (SMS). In this guide we will review how you can set up MFA using TOTP and SMS and the tradeoffs between these methods to help you choose the right set up for your application. We will also review how to set up MFA to remember a device and reduce sign-in friction for your users. +Amplify Auth supports multi-factor authentication (MFA) for user sign-in flows. MFA is an extra layer of security used to make sure that users trying to gain access to an account are who they say they are. It requires users to provide additional information to verify their identity. Amplify Auth supports MFA with time-based one-time passwords (TOTP), text messages (SMS), and email. + +In this guide we will review how you can set up MFA with each of these methods and the discuss tradeoffs between them to help you choose the right setup for your application. We will also review how to set up MFA to remember a device and reduce sign-in friction for your users. ## Configure multi-factor authentication -Use `defineAuth` to enable MFA for your app. The example below is setting up MFA with TOTP but not SMS as you can see that the phone number is not a required attribute. If you are using SMS, then the `PhoneNumber` attribute must be `true`. +Use `defineAuth` to enable MFA for your app. The example below is setting up MFA with TOTP but not SMS as you can see that the phone number is not a required attribute. +- If you plan to use SMS for MFA, then the `phoneNumber` attribute must be marked as required in your `userAttributes`. Note that if you have `loginWith.phone` as `true` this attribute will automatically be marked as required. +- If you plan to use email for MFA, then the `email` attribute must also be `true` must be marked as required in your `userAttributes`. Note that if you have `loginWith.email` as `true` this attribute will automatically be marked as required. ```ts title="amplify/auth/resource.ts" import { defineAuth } from '@aws-amplify/backend'; @@ -44,31 +48,208 @@ export const auth = defineAuth({ // highlight-start multifactor: { mode: 'OPTIONAL', - totp: true - } + totp: true, + }, // highlight-end + userAttributes: { + phoneNumber: { + required: true + } + } }); ``` -When multi-factor authentication (MFA) is REQUIRED with SMS in your backend auth resource, you will need to pass the phone number during sign-up API call. If you are using the `email` or `username` as the primary sign-in mechanism, you will need to pass the `phone_number` attribute as a user attribute. This will change depending on if you enable SMS, TOTP, or both. Visit the [multi-factor authentication documentation](/[platform]/build-a-backend/auth/concepts/multi-factor-authentication/) to learn more about enabling MFA on your backend auth resource. + +**Note:** Email-based MFA is currently not supported with `defineAuth`. We are working towards supporting this feature. For more information, visit the [feature request in GitHub](https://github.com/aws-amplify/amplify-backend/issues/2159). + +To take advantage of this feature with an Amplify generated backend, the underlying CDK construct can be extended manually. See [overriding Cognito User Pool multi-factor authentication options](/[platform]/build-a-backend/auth/modify-resources-with-cdk/#override-cognito-userpool-multi-factor-authentication-options) for more information. + + +When MFA is `REQUIRED` with SMS in your backend auth resource, you will need to pass the phone number during sign-up API call. If you are using the `email` or `username` as the primary sign-in mechanism, you will need to pass the `phone_number` attribute as a user attribute. + +Similarly, when MFA is `REQUIRED` with email as your delivery mechanism, you will need to pass an email address during the sign-up API call. If you are using `phoneNumber` or `username` as the primary sign-in mechanism, you will need to pass the `email` attribute as a user attribute. + +This configuration may change depending on the combination of MFA methods enabled in your user pool. ### Understand your MFA options When enabling MFA you will have two key decisions to make: -- **MFA enforcement:** As part of this setup you will determine how MFA is enforced. If you require MFA by setting MFA login to "ON", all your users will need to complete MFA to sign in. If you keep it "Optional", your users will have the choice whether to enable MFA or not for their account. -- **MFA methods:** You will also specify which MFA method you are using - TOTP (Time-based One-time Password), SMS (text message), or both. We recommend that you use TOTP-based MFA as it is more secure and you can reserve SMS for account recovery. +- **MFA enforcement:** As part of this setup you will determine how MFA is enforced. If you require MFA by setting MFA mode to `REQUIRED`, all your users will need to complete MFA to sign in. If you keep it `OPTIONAL`, your users will have the choice whether to enable MFA or not for their account. +- **MFA methods:** You will also specify which MFA method you are using: TOTP (Time-based One-time Password), SMS (text message), email, or any combination thereof. We recommend that you use TOTP-based MFA as it is more secure and you can reserve SMS or email for account recovery. - + -| | Time-based One-time Password (TOTP) | Short Message Service (SMS) | -| --- | --- | --- | -| **Description** | Generates a short-lived numeric code for user authentication that includes a shared secret key and current time using an authenticator app. | Generates a one-time code shared via text message that is entered with other credentials for user authentication. | -| **Benefits** | More secure than SMS since the code is generated locally and not transmitted over a network. TOTP also works without cell service as long as the TOTP app is installed. | Easy to set up with a user-provided phone number and is familiar to users as a common authentication method. | -| **Constraints** | Requires an app to generate codes and adds to the initial setup of an account. Codes also expire quickly and must be used promptly after it is generated. | SMS requires cell service and can include an additional cost for the user. Although rare, SMS messages can also be intercepted. | +| | Time-based One-time Password (TOTP) | Short Message Service (SMS) | Email | +| --- | --- | --- | --- | +| **Description** | Generates a short-lived numeric code for user authentication that includes a shared secret key and current time using an authenticator app. | Generates a one-time code shared via text message that is entered with other credentials for user authentication. | Generates a one-time code sent to the user's registered email address. The user must access the email and enter the code to complete the authentication process. | +| **Benefits** | More secure than SMS since the code is generated locally and not transmitted over a network. TOTP also works without cell service as long as the TOTP app is installed. | Easy to set up with a user-provided phone number and is familiar to users as a common authentication method. | Email is a widely used and familiar communication channel that requires no additional hardware or software requirements on the user's end. | +| **Constraints** | Requires an app to generate codes and adds to the initial setup of an account. Codes also expire quickly and must be used promptly after it is generated. | SMS requires cell service and can include an additional cost for the user. Although rare, SMS messages can also be intercepted. | Depends on the availability and reliability of email services. Although rare, emails can be intercepted or accounts can become compromised. | + + +If multiple MFA methods are enabled for the user, and none are set as preferred, the `signIn` API will return `CONTINUE_SIGN_IN_WITH_MFA_SELECTION` as the next step in the auth flow. During this scenario, the user should be prompted to select the MFA method they want to use to sign in and their preference should be passed to `confirmSignIn`. + + + + +```ts +import { confirmSignIn, type SignInOutput } from 'aws-amplify/auth'; + +function handleSignInNextSteps(output: SignInOutput) { + const { nextStep } = output; + switch (nextStep.signInStep) { + // ... + case 'CONTINUE_SIGN_IN_WITH_MFA_SELECTION': + const allowedMFATypes = nextStep.allowedMFATypes; + const mfaType = promptUserForMFAType(allowedMFATypes); + case 'CONFIRM_SIGN_IN_WITH_SMS_CODE': + // prompt user to enter otp code delivered via SMS + break; + case 'CONFIRM_SIGN_IN_WITH_TOTP_CODE': + // prompt user to enter otp code from their authenticator app + break; + case 'CONFIRM_SIGN_IN_WITH_EMAIL_CODE': + // prompt user to enter otp code delivered via EMAIL + break; + // ... + } +} + +type MfaType = 'SMS' | 'TOTP' | 'EMAIL'; + +function promptUserForMFAType(allowedMFATypes?: MfaType[]): MfaType { + // Prompt user to select MFA type +} + +async function handleMFASelection(mfaType: MfaType) { + try { + const output = await confirmSignIn({ + challengeResponse: mfaType, + }); + handleSignInNextSteps(output); + } catch (error) { + console.log(error); + } +} +``` + + + +```kotlin +fun signIn(username: String, password: String) { + val result: AuthSignInResult + try { + result = Amplify.Auth.signIn(username, password) + } catch (e: AuthException) { + Log.e("MFASelection", "Failed to sign in", e) + } + handleNextSignInStep(username, result.nextStep) +} + +fun handleNextSignInStep( + username: String, + nextStep: AuthNextSignInStep +) { + when (nextStep.signInStep) { + AuthSignInStep.CONTINUE_SIGN_IN_WITH_MFA_SELECTION -> { + // User has multiple MFA methods and none are preferred + promptUserForMfaType(nextStep.allowedMFATypes) + } + else -> { + // Handle other SignInSteps + } + } +} + +fun promptUserForMfaType(mfaTypes: Set?) { + // Prompt user to select one of the passed-in MFA Types + // Then invoke Amplify.Auth.confirmSignIn(selectedMfaType.challengeResponse) +} +``` + + + +If multiple MFA methods are enabled for the user, and none are set as preferred, the `signIn` API will return `continueSignInWithMFASelection` as the next step in the auth flow. During this scenario, the user should be prompted to select the MFA method they want to use to sign in and their preference should be passed to `confirmSignIn`. + + + + +```dart +Future _handleSignInResult(SignInResult result) async { + switch (result.nextStep.signInStep) { + // ··· + case AuthSignInStep.continueSignInWithMfaSelection: + final allowedMfaTypes = result.nextStep.allowedMfaTypes!; + final selection = await _promptUserPreference(allowedMfaTypes); + return _handleMfaSelection(selection); + // ··· + } +} + +Future _promptUserPreference(Set allowedTypes) async { + // ··· +} + +Future _handleMfaSelection(MfaType selection) async { + try { + final result = await Amplify.Auth.confirmSignIn( + confirmationValue: selection.confirmationValue, + ); + return _handleSignInResult(result); + } on AuthException catch (e) { + safePrint('Error sending MFA selection: ${e.message}'); + } +} +``` + + + + +```swift +func signIn(username: String, password: String) async { + do { + let signInResult = try await Amplify.Auth.signIn(username: username, password: password) + switch signInResult.nextStep { + + case .continueSignInWithMFASelection(let allowedMFATypes): + print("Received next step as continue sign in by selecting MFA type") + print("Allowed MFA types \(allowedMFATypes)") + + // Prompt the user to select the MFA type they want to use + // Then invoke `confirmSignIn` api with the MFA type + + default: + + // Use has successfully signed in to the app + print("Step: \(signInResult.nextStep)") + } + } catch let error as AuthError{ + print ("Sign in failed \(error)") + } catch { + print("Unexpected error: \(error)") + } +} + +func confirmSignInWithTOTPAsMFASelection() async { + do { + let signInResult = try await Amplify.Auth.confirmSignIn( + challengeResponse: MFAType.totp.challengeResponse) + + if case .confirmSignInWithTOTPCode = signInResult.nextStep { + print("Received next step as confirm sign in with TOTP") + } + + } catch { + print("Confirm sign in failed \(error)") + } +} +``` + + + ## Multi-factor authentication with SMS @@ -674,72 +855,504 @@ If a user loses access to their TOTP device, they will need to contact an admini In a scenario where MFA is marked as "Required" in the Cognito User Pool and another MFA method is not set up, the administrator would need to first initiate an [`AdminUpdateUserAttributes`](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminUpdateUserAttributes.html) call and update the user's phone number attribute. Once this is complete, the administrator can continue changing the MFA preference to SMS as suggested above. -## Set up a user's preferred MFA method +## Multi-factor authentication with EMAIL + + +**Note:** Email-based MFA is currently not supported in the [Authenticator component](https://ui.docs.amplify.aws/react/connected-components/authenticator). We are working towards supporting this feature. For more information, visit the [feature request in GitHub](https://github.com/aws-amplify/amplify-ui/issues/5983). -### Fetch the current user's MFA preferences + + -Invoke the following API to get the current MFA preference and enabled MFA types, if any, for the current user. + + - +If you are using the [Authenticator component](https://ui.docs.amplify.aws/swift/connected-components/authenticator) with Amplify, this feature works without any additional code. The guide below is for writing your own implementation. + + + + + + + +If you are using the [Authenticator component](https://ui.docs.amplify.aws/flutter/connected-components/authenticator) with Amplify, this feature works without any additional code. The guide below is for writing your own implementation. + + + + + + + +If you are using the [Authenticator component](https://ui.docs.amplify.aws/android/connected-components/authenticator) with Amplify, this feature works without any additional code. The guide below is for writing your own implementation. + + + + +Once you have setup email as your second layer of authentication with MFA as shown above, your users will get an authentication code via email to complete sign-in after they sign in with their username and password. + + + +In order to send email authentication codes, the following prerequisites must be met: +- Cognito must be configured to send emails using [Amazon Simple Email Service (Amazon SES)](/[platform]/build-a-backend/auth/moving-to-production/#email). +- [Advanced Security Features (ASF)](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-settings-advanced-security.html) must be enabled in your user pool. +- If account recovery is enabled in Cognito, the delivery method for recovery messages cannot be set to `Email only` + + + + +Additional pricing applies for ASF. [Learn more about Amazon Cognito pricing](https://aws.amazon.com/cognito/pricing/) + + +### Enable EMAIL MFA during sign-up + +You will need to pass `email` as a user attribute to enable email MFA for your users during sign-up. However, if the primary sign-in mechanism for your Cognito resource is already `email` (without enabling `username`), then you do not need to pass it as an attribute. + + ```ts -import { fetchMFAPreference } from 'aws-amplify/auth'; +import { signUp } from 'aws-amplify/auth'; -const { enabled, preferred } = await fetchMFAPreference(); +await signUp({ + username: "+15555555555", + password: "hunter2", + options: { + userAttributes: { + email: "hello@mycompany.com", + }, + }, +}); +``` + + + + + + + +```java +ArrayList attributes = new ArrayList<>(); +attributes.add(new AuthUserAttribute(AuthUserAttributeKey.email(), "my@email.com")); +attributes.add(new AuthUserAttribute(AuthUserAttributeKey.phoneNumber(), "+15551234567")); + +Amplify.Auth.signUp( + "username", + "Password123", + AuthSignUpOptions.builder().userAttributes(attributes).build(), + result -> Log.i("AuthQuickstart", result.toString()), + error -> Log.e("AuthQuickstart", error.toString()) +); +``` + + + + +```kotlin +val attrs = mapOf( + AuthUserAttributeKey.email() to "my@email.com", + AuthUserAttributeKey.phoneNumber() to "+15551234567" +) +val options = AuthSignUpOptions.builder() + .userAttributes(attrs.map { AuthUserAttribute(it.key, it.value) }) + .build() +Amplify.Auth.signUp("username", "Password123", options, + { Log.i("AuthQuickstart", "Sign up result = $it") }, + { Log.e("AuthQuickstart", "Sign up failed", it) } +) ``` + + + + +```kotlin +val attrs = mapOf( + AuthUserAttributeKey.email() to "my@email.com", + AuthUserAttributeKey.phoneNumber() to "+15551234567" +) +val options = AuthSignUpOptions.builder() + .userAttributes(attrs.map { AuthUserAttribute(it.key, it.value) }) + .build() +try { + val result = Amplify.Auth.signUp("username", "Password123", options) + Log.i("AuthQuickstart", "Sign up OK: $result") +} catch (error: AuthException) { + Log.e("AuthQuickstart", "Sign up failed", error) +} +``` + + + + +```java +ArrayList attributes = new ArrayList<>(); +attributes.add(new AuthUserAttribute(AuthUserAttributeKey.email(), "my@email.com")); +attributes.add(new AuthUserAttribute(AuthUserAttributeKey.phoneNumber(), "+15551234567")); + +RxAmplify.Auth.signUp( + "username", + "Password123", + AuthSignUpOptions.builder().userAttributes(attributes).build()) + .subscribe( + result -> Log.i("AuthQuickstart", result.toString()), + error -> Log.e("AuthQuickstart", error.toString()) + ); +``` + + + + ```dart -Future getCurrentMfaPreference() async { - final cognitoPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey); - - final currentPreference = await cognitoPlugin.fetchMfaPreference(); - safePrint('Enabled MFA types for user: ${currentPreference.enabled}'); - safePrint('Preferred MFA type for user: ${currentPreference.preferred}'); +Future signUpWithEmailVerification( + String username, + String password, +) async { + await Amplify.Auth.signUp( + username: username, + password: password, + options: SignUpOptions( + userAttributes: { + AuthUserAttributeKey.email: 'test@example.com', + // ... if required + AuthUserAttributeKey.phoneNumber: '+18885551234', + }, + ), + ); } ``` ```swift -func getMFAPreferences() async throws { - let authCognitoPlugin = try Amplify.Auth.getPlugin( - for: "awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin +func signUp(username: String, password: String, email: String, phonenumber: String) async { + do { + let signUpResult = try await Amplify.Auth.signUp( + username: username, + password: password, + options: .init(userAttributes: [ + AuthUserAttribute(.email, value: email), + AuthUserAttribute(.phoneNumber, value: phonenumber) + ]) + ) + if case let .confirmUser(deliveryDetails, _, userId) = signUpResult.nextStep { + print("Delivery details \(String(describing: deliveryDetails)) for userId: \(String(describing: userId)))") + } else { + print("SignUp Complete") + } + } catch let error as AuthError { + print("An error occurred while registering a user \(error)") + } catch { + print("Unexpected error: \(error)") + } +} +``` + - let result = try await authCognitoPlugin?.fetchMFAPreference() +By default, you have to verify a user account after they sign up using the `confirmSignUp` API. Following the initial `signUp` request, a one-time passcode will be sent to the user's phone number or email, depending on your Amazon Cognito configuration. - print("Enabled MFA types: \(result?.enabled)") - print("Preferred MFA type: \(result?.preferred)") + +```ts +import { confirmSignUp } from 'aws-amplify/auth'; + +await confirmSignUp({ + username: "+15555555555", + confirmationCode: "123456", +}) +``` + + + + + + + +```java + try { + Amplify.Auth.confirmSignUp( + "username", + "confirmation code", + result -> Log.i("AuthQuickstart", "Confirm signUp result completed: " + result.isSignUpComplete()), + error -> Log.e("AuthQuickstart", "An error occurred while confirming sign up: " + error) + ); +} catch (Exception error) { + Log.e("AuthQuickstart", "unexpected error: " + error); +} +``` + + + + +```kotlin + try { + Amplify.Auth.confirmSignUp( + "username", + "confirmation code", + { result -> + Log.i("AuthQuickstart", "Confirm signUp result completed: ${result.isSignUpComplete}") + } + ) { error -> + Log.e("AuthQuickstart", "An error occurred while confirming sign up: $error") + } +} catch (error: Exception) { + Log.e("AuthQuickstart", "unexpected error: $error") +} +``` + + + + +```kotlin +try { + val result = Amplify.Auth.confirmSignUp( + "username", + "confirmation code" + ) + Log.i("AuthQuickstart", "Confirm signUp result completed: ${result.isSignUpComplete}") +} catch (error: Exception) { + Log.e("AuthQuickstart", "unexpected error: $error") } ``` + + + + +```java +RxAmplify.Auth.confirmSignUp( + "username", + "confirmation code").subscribe( + result -> Log.i("AuthQuickstart", "Confirm signUp result completed: " + result.isSignUpComplete()), + error -> Log.e("AuthQuickstart", "An error occurred while confirming sign up: " + error) +); +``` + + + -### Update the current user's MFA preferences + +```dart +Future confirmSignUpEmailVerification( + String username, + String otpCode, +) async { + await Amplify.Auth.confirmSignUp( + username: username, + confirmationCode: otpCode, + ); +} +``` + -Invoke the following API to update the MFA preference for the current user. - + +```swift +func confirmSignUp(for username: String, with confirmationCode: String) async { + do { + let confirmSignUpResult = try await Amplify.Auth.confirmSignUp( + for: username, + confirmationCode: confirmationCode + ) + print("Confirm sign up result completed: \(confirmSignUpResult.isSignUpComplete)") + } catch let error as AuthError { + print("An error occurred while confirming sign up \(error)") + } catch { + print("Unexpected error: \(error)") + } +} +``` + -Only one MFA method can be marked as preferred at a time. If the user has multiple MFA methods enabled and tries to mark more than one MFA method as preferred, the API will throw an error. +### Manage EMAIL MFA during sign-in - +After a user signs in, if they have MFA enabled for their account, a challenge will be issued that requires calling the `confirmSignIn` API with the user provided confirmation code sent to their email address. - +If MFA is **ON** or enabled for the user, you must call `confirmSignIn` with the OTP sent to their email address. + + +```ts +import { confirmSignIn } from 'aws-amplify/auth'; + +await confirmSignIn({ + challengeResponse: "123456" +}); +``` + + + + + + +```java +try { + Amplify.Auth.confirmSignIn( + "confirmation code", + result -> { + if (result.isSignedIn()) { + Log.i("AuthQuickstart", "Confirm signIn succeeded"); + } else { + Log.i("AuthQuickstart", "Confirm sign in not complete. There might be additional steps: " + result.getNextStep()); + // Switch on the next step to take appropriate actions. + // If `result.isSignedIn` is true, the next step + // is 'done', and the user is now signed in. + } + }, + error -> Log.e("AuthQuickstart", "Confirm sign in failed: " + error) + ); +} catch (Exception error) { + Log.e("AuthQuickstart", "Unexpected error: " + error); +} +``` + + + + +```kotlin +try { + Amplify.Auth.confirmSignIn( + "confirmation code", + { result -> + if (result.isSignedIn) { + Log.i("AuthQuickstart","Confirm signIn succeeded") + } else { + Log.i("AuthQuickstart", "Confirm sign in not complete. There might be additional steps: ${result.nextStep}") + // Switch on the next step to take appropriate actions. + // If `result.isSignedIn` is true, the next step + // is 'done', and the user is now signed in. + } + } + ) { error -> Log.e("AuthQuickstart", "Confirm sign in failed: $error")} +} catch (error: Exception) { + Log.e("AuthQuickstart", "Unexpected error: $error") +} +``` + + + + +```kotlin +try { + val result = Amplify.Auth.confirmSignIn( + "confirmation code" + ) + if (result.isSignedIn) { + Log.i("AuthQuickstart", "Confirm signIn succeeded") + } else { + Log.i("AuthQuickstart", "Confirm sign in not complete. There might be additional steps: ${result.nextStep}" + ) + // Switch on the next step to take appropriate actions. + // If `result.isSignedIn` is true, the next step + // is 'done', and the user is now signed in. + } +} catch (error: Exception) { + Log.e("AuthQuickstart", "Unexpected error: $error") +} +``` + + + + +```java + +RxAmplify.Auth.confirmSignIn( + "confirmation code").subscribe( + result -> { + if (result.isSignedIn()) { + Log.i("AuthQuickstart", "Confirm signIn succeeded"); + } else { + Log.i("AuthQuickstart", "Confirm sign in not complete. There might be additional steps: " + result.getNextStep()); + // Switch on the next step to take appropriate actions. + // If `result.isSignedIn` is true, the next step + // is 'done', and the user is now signed in. + } + }, + error -> Log.e("AuthQuickstart", "Confirm sign in failed: " + error) + ); +``` + + + + + + +```dart +Future confirmSignInEmailVerification(String otpCode) async { + await Amplify.Auth.confirmSignIn( + confirmationValue: otpCode, + ); +} +``` + + + +```swift +func confirmSignIn() async { + do { + let signInResult = try await Amplify.Auth.confirmSignIn( + challengeResponse: "") + print("Confirm sign in succeeded. Next step: \(signInResult.nextStep)") + } catch let error as AuthError { + print("Confirm sign in failed \(error)") + } catch { + print("Unexpected error: \(error)") + } +} +``` + + +After a user has been signed in, call `updateMFAPreference` to record the MFA type as enabled for the user and optionally set it as preferred so that subsequent logins default to using this MFA type. + + ```ts import { updateMFAPreference } from 'aws-amplify/auth'; -await updateMFAPreference({ sms: 'ENABLED', totp: 'PREFERRED' }); +await updateMFAPreference({ email: 'PREFERRED' }); ``` + + + + +```java +if (Amplify.Auth.getPlugin("awsCognitoAuthPlugin") instanceof AWSCognitoAuthPlugin) { + AWSCognitoAuthPlugin plugin = (AWSCognitoAuthPlugin) Amplify.Auth.getPlugin("awsCognitoAuthPlugin"); + plugin.updateMFAPreference( + MFAPreference.DISABLED, // SMS Preference + MFAPreference.DISABLED, // TOTP Preference + MFAPreference.PREFERRED, // Email Preference + () -> Log.i("AuthQuickstart", "MFA preference updated successfully"), + e -> Log.e("AuthQuickstart", "Failed to update MFA preference.", e) + ); + } +``` + + + + +```kotlin +if (Amplify.Auth.getPlugin("awsCognitoAuthPlugin") is AWSCognitoAuthPlugin) { + val plugin = Amplify.Auth.getPlugin("awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin + plugin?.updateMFAPreference( + MFAPreference.DISABLED, // SMS Preference + MFAPreference.DISABLED, // TOTP Preference + MFAPreference.PREFERRED, // Email Preference + { Log.i("AuthQuickstart", "MFA preference updated successfully" ) }, + { e: AuthException? -> Log.e("AuthQuickstart", "Failed to update MFA preference", e) } + ) +} +``` + + + + + + ```dart Future updateMfaPreferences() async { final cognitoPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey); await cognitoPlugin.updateMfaPreference( - sms: MfaPreference.enabled, - totp: MfaPreference.preferred, + email: MfaPreference.enabled, // or .preferred ); } ``` @@ -751,132 +1364,167 @@ func updateMFAPreferences() async throws { let authCognitoPlugin = try Amplify.Auth.getPlugin( for: "awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin - let smsMfaPreference: MFAPreference = .enabled - let totpMfaPreference: MFAPreference = .preferred + let emailMfaPreference: MFAPreference = .preferred try await authCognitoPlugin?.updateMFAPreference( - sms: smsMfaPreference, - totp: totpMfaPreference) + email: emailMfaPreference) } ``` - -If multiple MFA methods are enabled for the user, the `signIn` API will return `CONTINUE_SIGN_IN_WITH_MFA_SELECTION` as the next step in the auth flow. During this scenario, the user should be prompted to select the MFA method they want to use to sign in and their preference should be passed to `confirmSignIn`. +## Set up a user's preferred MFA method + +Depending on your user pool configuration, it's possible that multiple MFA options may be available to a given user. In order to avoid requiring your users to select an MFA method each time they sign-in to your application, Amplify provides two utility APIs to manage an individual user's MFA preferences. + +### Fetch the current user's MFA preferences +Invoke the following API to get the current MFA preference and enabled MFA types, if any, for the current user. + + ```ts -import { confirmSignIn, SignInOutput } from 'aws-amplify/auth'; +import { fetchMFAPreference } from 'aws-amplify/auth'; -function handleSignInNextSteps(output: SignInOutput) { - const { nextStep } = output; - switch (nextStep.signInStep) { - // ... - case 'CONTINUE_SIGN_IN_WITH_MFA_SELECTION': - const allowedMFATypes = nextStep.allowedMFATypes; - const mfaType = promptUserForMFAType(allowedMFATypes); - case 'CONFIRM_SIGN_IN_WITH_SMS_CODE': - // display user to enter otp code; - break; - case 'CONFIRM_SIGN_IN_WITH_TOTP_CODE': - // display user to enter otp code; - break; - // ... - } -} +const { enabled, preferred } = await fetchMFAPreference(); +``` + -function promptUserForMFAType( - allowedMFATypes?: ('SMS' | 'TOTP')[] -): 'SMS' | 'TOTP' { - // Prompt user to select MFA type + + + + +```java +if (Amplify.Auth.getPlugin("awsCognitoAuthPlugin") instanceof AWSCognitoAuthPlugin) { + AWSCognitoAuthPlugin plugin = (AWSCognitoAuthPlugin) Amplify.Auth.getPlugin("awsCognitoAuthPlugin"); + plugin.fetchMFAPreference( + preference -> Log.i( + "AuthQuickStart", + "Fetched MFA preference, enabled: " + preference.getEnabled() + ", preferred: " + preference.getPreferred() + ), + e -> Log.e("AuthQuickStart", "Failed to fetch MFA preference.", e) + ); } +``` -async function handleMFASelection(mfaType: 'SMS' | 'TOTP') { - try { - const output = await confirmSignIn({ - challengeResponse: mfaType - }); - handleSignInNextSteps(output); - } catch (error) { - console.log(error); - } -} + + + +```kotlin +val cognitoAuthPlugin = Amplify.Auth.getPlugin("awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin +cognitoAuthPlugin?.fetchMFAPreference( + { Log.d("AuthQuickStart", "Fetched MFA preference, enabled: ${it.enabled}, preferred: ${it.preferred}") }, + { Log.e("AuthQuickStart", "Failed to fetch MFA preference.", it) } +) ``` - - -If multiple MFA methods are enabled for the user, the `signIn` API will return `continueSignInWithMFASelection` as the next step in the auth flow. During this scenario, the user should be prompted to select the MFA method they want to use to sign in and their preference should be passed to `confirmSignIn`. + + - ```dart -Future _handleSignInResult(SignInResult result) async { - switch (result.nextStep.signInStep) { - // ··· - case AuthSignInStep.continueSignInWithMfaSelection: - final allowedMfaTypes = result.nextStep.allowedMfaTypes!; - final selection = await _promptUserPreference(allowedMfaTypes); - return _handleMfaSelection(selection); - // ··· - } +Future getCurrentMfaPreference() async { + final cognitoPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey); + + final currentPreference = await cognitoPlugin.fetchMfaPreference(); + safePrint('Enabled MFA types for user: ${currentPreference.enabled}'); + safePrint('Preferred MFA type for user: ${currentPreference.preferred}'); } +``` + -Future _promptUserPreference(Set allowedTypes) async { - // ··· + +```swift +func getMFAPreferences() async throws { + let authCognitoPlugin = try Amplify.Auth.getPlugin( + for: "awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin + + let result = try await authCognitoPlugin?.fetchMFAPreference() + + print("Enabled MFA types: \(result?.enabled)") + print("Preferred MFA type: \(result?.preferred)") } +``` + -Future _handleMfaSelection(MfaType selection) async { - try { - final result = await Amplify.Auth.confirmSignIn( - confirmationValue: selection.confirmationValue, +### Update the current user's MFA preferences + +Invoke the following API to update the MFA preference for the current user. + + + +Only one MFA method can be marked as preferred at a time. If the user has multiple MFA methods enabled and tries to mark more than one MFA method as preferred, the API will throw an error. + + + + +```ts +import { updateMFAPreference } from 'aws-amplify/auth'; + +await updateMFAPreference({ sms: 'ENABLED', totp: 'PREFERRED' }); +``` + + + + + + +```java +if (Amplify.Auth.getPlugin("awsCognitoAuthPlugin") instanceof AWSCognitoAuthPlugin) { + AWSCognitoAuthPlugin plugin = (AWSCognitoAuthPlugin) Amplify.Auth.getPlugin("awsCognitoAuthPlugin"); + plugin.updateMFAPreference( + MFAPreference.DISABLED, // SMS Preference + MFAPreference.PREFERRED, // TOTP Preference + null // Email Preference + () -> Log.i( "AuthQuickStart", "Preference updated successfully"), + e -> Log.e("AuthQuickStart", "Failed to update MFA preference.", e) ); - return _handleSignInResult(result); - } on AuthException catch (e) { - safePrint('Error resending code: ${e.message}'); - } } ``` - - + + + +```kotlin +val cognitoAuthPlugin = Amplify.Auth.getPlugin("awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin +cognitoAuthPlugin?.updateMFAPreference( + MFAPreference.DISABLED, // SMS Preference + MFAPreference.PREFERRED, // TOTP Preference + null, // Email Preference + { Log.d("AuthQuickStart", "Preference updated successfully") }, + { Log.e("AuthQuickStart", "Failed to update MFA preference.", it) } +) +``` -```swift -func signIn(username: String, password: String) async { - do { - let signInResult = try await Amplify.Auth.signIn(username: username, password: password) - switch signInResult.nextStep { + - case .continueSignInWithMFASelection(let allowedMFATypes): - print("Received next step as continue sign in by selecting MFA type") - print("Allowed MFA types \(allowedMFATypes)") - - // Prompt the user to select the MFA type they want to use - // Then invoke `confirmSignIn` api with the MFA type - - default: - - // Use has successfully signed in to the app - print("Step: \(signInResult.nextStep)") - } - } catch let error as AuthError{ - print ("Sign in failed \(error)") - } catch { - print("Unexpected error: \(error)") - } + + + + +```dart +Future updateMfaPreferences() async { + final cognitoPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey); + + await cognitoPlugin.updateMfaPreference( + sms: MfaPreference.enabled, + totp: MfaPreference.preferred, + ); } +``` + -func confirmSignInWithTOTPAsMFASelection() async { - do { - let signInResult = try await Amplify.Auth.confirmSignIn( - challengeResponse: MFAType.totp.challengeResponse) + +```swift +func updateMFAPreferences() async throws { + let authCognitoPlugin = try Amplify.Auth.getPlugin( + for: "awsCognitoAuthPlugin") as? AWSCognitoAuthPlugin - if case .confirmSignInWithTOTPCode = signInResult.nextStep { - print("Received next step as confirm sign in with TOTP") - } + let smsMfaPreference: MFAPreference = .enabled + let totpMfaPreference: MFAPreference = .preferred - } catch { - print("Confirm sign in failed \(error)") - } + try await authCognitoPlugin?.updateMFAPreference( + sms: smsMfaPreference, + totp: totpMfaPreference) } ``` diff --git a/src/pages/[platform]/build-a-backend/auth/multi-step-sign-in/index.mdx b/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/multi-step-sign-in/index.mdx similarity index 63% rename from src/pages/[platform]/build-a-backend/auth/multi-step-sign-in/index.mdx rename to src/pages/[platform]/build-a-backend/auth/connect-your-frontend/multi-step-sign-in/index.mdx index 0e624c40ff9..eee41bf9520 100644 --- a/src/pages/[platform]/build-a-backend/auth/multi-step-sign-in/index.mdx +++ b/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/multi-step-sign-in/index.mdx @@ -4,7 +4,7 @@ export const meta = { title: 'Multi-step sign-in', description: 'Use Amazon Cognito Auth plugin to complete a multi step authentication flow', - platforms: ['android', 'swift', 'flutter'] + platforms: ['android', 'swift', 'flutter', 'react', 'nextjs', 'javascript', 'react-native', 'vue', 'angular'] }; export const getStaticPaths = async () => { @@ -20,6 +20,532 @@ export function getStaticProps(context) { }; } + +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](/[platform]/build-a-backend/auth/concepts/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. + +```typescript +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_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. + + + +The result includes an `AuthCodeDeliveryDetails` member. It includes additional information about the code delivery, such as the partial phone number of the SMS recipient, which can be used to prompt the user on where to look for the code. + + + +```ts +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. + + +```ts +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. + + + +The result includes an `AuthCodeDeliveryDetails` member. It includes additional information about the code delivery, such as the partial email address of the recipient, which can be used to prompt the user on where to look for the code. + + + +```ts +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. + + +```ts +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. + +```ts +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. + + +```ts +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. + +```ts +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 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. + +```ts +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. + +```ts +async function confirmCustomChallenge(answer: string) { + const result = await confirmSignIn({ challengeResponse: answer }); + + return handleSignInResult(result); +} +``` + + + +**Special Handling on `confirmSignIn`** + +If `failAuthentication=true` is returned by the Lambda, Cognito will invalidate the session of the request. This is represented by a `NotAuthorizedException` and requires restarting the sign-in flow by calling `signIn` again. + + + +## 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](/[platform]/build-a-backend/auth/connect-your-frontend/sign-in/) and [manage-password](/[platform]/build-a-backend/auth/manage-users/manage-passwords/) docs for more information. + +```ts +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](/[platform]/build-a-backend/auth/manage-users/manage-passwords/) docs for more information. + +```ts +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](/[platform]/build-a-backend/auth/connect-your-frontend/sign-up/) docs for more information. + + + +The result includes an `AuthCodeDeliveryDetails` member. It includes additional information about the code delivery, such as the partial phone number of the SMS recipient, which can be used to prompt the user on where to look for the code. + + + +```ts +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`. + +```ts +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; + } + } +} + +``` + + + 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 you provided when you define your auth resources like described on [Manage MFA Settings](/[platform]/build-a-backend/auth/concepts/multi-factor-authentication/) page. @@ -63,6 +589,12 @@ Future _handleSignInResult(SignInResult result) async { switch (result.nextStep.signInStep) { case AuthSignInStep.continueSignInWithMfaSelection: // Handle select from MFA methods case + case AuthSignInStep.continueSignInWithMfaSetupSelection: + // Handle select from MFA methods available to setup + case AuthSignInStep.continueSignInWithEmailMfaSetup: + // Handle email setup case + case AuthSignInStep.confirmSignInWithOtpCode: + // Handle email MFA case case AuthSignInStep.continueSignInWithTotpSetup: // Handle TOTP setup case case AuthSignInStep.confirmSignInWithTotpMfaCode: @@ -82,10 +614,9 @@ Future _handleSignInResult(SignInResult result) async { } } ``` -## Confirm signin with SMS MFA +## Confirm sign-in with SMS MFA -If the next step is `confirmSignInWithSmsMfaCode`, 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. +If the next step is `confirmSignInWithSmsMfaCode`, 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. @@ -125,7 +656,7 @@ Future confirmMfaUser(String mfaCode) async { } ``` -## Confirm signin with TOTP MFA +## Confirm sign-in with TOTP MFA If the next step is `confirmSignInWithTOTPCode`, 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. @@ -156,14 +687,58 @@ Future confirmTotpUser(String totpCode) async { } ``` -## Continue signin with MFA Selection +## Confirm sign-in with Email MFA -If the next step is `continueSignInWithMFASelection`, the user must select the MFA method to use. Amplify Auth currently only supports SMS and TOTP as MFA methods. After the user selects an MFA method, your implementation must pass the selected MFA method to Amplify Auth using `confirmSignIn` API. +If the next step is `confirmSignInWithOtpCode`, 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. + + + +The result includes an `AuthCodeDeliveryDetails` member. It includes additional information about the code delivery, such as the partial email address of +the recipient, which can be used to prompt the user on where to look for the code. + + + +```dart +Future _handleSignInResult(SignInResult result) async { + switch (result.nextStep.signInStep) { + case AuthSignInStep.confirmSignInWithOtpCode: + final codeDeliveryDetails = result.nextStep.codeDeliveryDetails!; + _handleCodeDelivery(codeDeliveryDetails); + // ... + } +} + +void _handleCodeDelivery(AuthCodeDeliveryDetails codeDeliveryDetails) { + safePrint( + 'A confirmation code has been sent to ${codeDeliveryDetails.destination}. ' + 'Please check your ${codeDeliveryDetails.deliveryMedium.name} for the code.', + ); +} +``` + +```dart +Future confirmMfaUser(String mfaCode) async { + try { + final result = await Amplify.Auth.confirmSignIn( + confirmationValue: mfaCode, + ); + return _handleSignInResult(result); + } on AuthException catch (e) { + safePrint('Error confirming MFA code: ${e.message}'); + } +} +``` + + +## Continue sign-in with MFA Selection + +If the next step is `continueSignInWithMFASelection`, 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: - `MfaType.sms` - `MfaType.totp` +- `MfaType.email` ```dart Future _handleSignInResult(SignInResult result) async { @@ -193,7 +768,35 @@ Future _handleMfaSelection(MfaType selection) async { } ``` -## Continue signin with TOTP Setup +## Continue sign-in with Email Setup + +If the next step is `continueSignInWithEmailMfaSetup`, 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. + +```dart +Future _handleSignInResult(SignInResult result) async { + switch (result.nextStep.signInStep) { + // ··· + case AuthSignInStep.continueSignInWithEmailMfaSetup: + // Prompt user to enter an email address they would like to use for MFA + // ··· + } +} + +// Then, pass the email address to `confirmSignIn` + +Future confirmEmailUser(String emailAddress) async { + try { + final result = await Amplify.Auth.confirmSignIn( + confirmationValue: emailAddress, + ); + return _handleSignInResult(result); + } on AuthException catch (e) { + safePrint('Error confirming email address: ${e.message}'); + } +} +``` + +## Continue sign-in with TOTP Setup If the next step is `continueSignInWithTOTPSetup`, then the user must provide a TOTP code to complete the sign in process. The step returns an associated value of type `TOTPSetupDetails` which would be used for generating TOTP. `TOTPSetupDetails` provides a helper method called `getSetupURI` that can be used to generate a URI, which can be used by native password managers for TOTP association. For example. if the URI is used on Apple platforms, it will trigger the platform's native password manager to associate TOTP with the account. For more advanced use cases, `TOTPSetupDetails` also contains the `sharedSecret` that will be used to either generate a QR code or can be manually entered into an authenticator app. @@ -225,7 +828,45 @@ Future confirmTotpUser(String totpCode) async { } } ``` -## Confirm signin with custom challenge + +## Continue sign-in with MFA Setup Selection +If the next step is `continueSignInWithMfaSetupSelection`, 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 are: + +- `MfaType.sms` +- `MfaType.totp` +- `MfaType.email` + +```dart +Future _handleSignInResult(SignInResult result) async { + switch (result.nextStep.signInStep) { + // ··· + case AuthSignInStep.continueSignInWithMfaSetupSelection: + final allowedMfaTypes = result.nextStep.allowedMfaTypes!; + final selection = await _promptUserPreference(allowedMfaTypes); + return _handleMfaSelection(selection); + // ··· + } +} + +Future _promptUserPreference(Set allowedTypes) async { + // ··· +} + +Future _handleMfaSelection(MfaType selection) async { + try { + final result = await Amplify.Auth.confirmSignIn( + confirmationValue: selection.confirmationValue, + ); + return _handleSignInResult(result); + } on AuthException catch (e) { + safePrint('Error selecting MFA method: ${e.message}'); + } +} +``` + +## Confirm sign-in with custom challenge If the next step is `confirmSignInWithCustomChallenge`, 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](/[platform]/build-a-backend/auth/customize-auth-lifecycle/custom-auth-flows/#sign-in-a-user). @@ -267,7 +908,7 @@ If `failAuthentication=true` is returned by the Lambda, Cognito will invalidate -## Confirm signin with new password +## Confirm sign-in with new password If the next step is `confirmSignInWithNewPassword`, 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. @@ -435,20 +1076,33 @@ try { { AuthNextSignInStep nextStep = result.getNextStep(); switch (nextStep.getSignInStep()) { - case CONFIRM_SIGN_IN_WITH_TOTP_CODE -> { + case CONFIRM_SIGN_IN_WITH_TOTP_CODE: { Log.i("AuthQuickstart", "Received next step as confirm sign in with TOTP code"); // Prompt the user to enter the TOTP code generated in their authenticator app // Then invoke `confirmSignIn` api with the code break; } - case CONTINUE_SIGN_IN_WITH_TOTP_SETUP -> { + case CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION: { + Log.i("AuthQuickstart", "Received next step as continue sign in by selecting an MFA method to setup"); + Log.i("AuthQuickstart", "Allowed MFA types for setup" + nextStep.getAllowedMFATypes()); + // Prompt the user to select the MFA type they want to setup + // Then invoke `confirmSignIn` api with the MFA type + break; + } + case CONTINUE_SIGN_IN_WITH_EMAIL_MFA_SETUP: { + Log.i("AuthQuickstart", "Received next step as continue sign in by setting up email MFA"); + // Prompt the user to enter the email address they would like to use to receive OTPs + // Then invoke `confirmSignIn` api with the email address + break; + } + case CONTINUE_SIGN_IN_WITH_TOTP_SETUP: { Log.i("AuthQuickstart", "Received next step as continue sign in by setting up TOTP"); Log.i("AuthQuickstart", "Shared secret that will be used to set up TOTP in the authenticator app" + nextStep.getTotpSetupDetails().getSharedSecret()); // Prompt the user to enter the TOTP code generated in their authenticator app // Then invoke `confirmSignIn` api with the code break; } - case CONTINUE_SIGN_IN_WITH_MFA_SELECTION -> { + case CONTINUE_SIGN_IN_WITH_MFA_SELECTION: { Log.i("AuthQuickstart", "Received next step as continue sign in by selecting MFA type"); Log.i("AuthQuickstart", "Allowed MFA type" + nextStep.getAllowedMFATypes()); // Prompt the user to select the MFA type they want to use @@ -462,6 +1116,13 @@ try { // Then invoke `confirmSignIn` api with the code break; } + case CONFIRM_SIGN_IN_WITH_OTP: { + Log.i("AuthQuickstart", "OTP code sent to " + nextStep.getCodeDeliveryDetails().getDestination()); + Log.i("AuthQuickstart", "Additional Info :" + nextStep.getAdditionalInfo()); + // Prompt the user to enter the OTP MFA code they received + // Then invoke `confirmSignIn` api with the code + break; + } case CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE: { Log.i("AuthQuickstart", "Custom challenge, additional info: " + nextStep.getAdditionalInfo()); // Prompt the user to enter custom challenge answer @@ -522,9 +1183,20 @@ try { val nextStep = result.nextStep when(nextStep.signInStep){ AuthSignInStep.CONFIRM_SIGN_IN_WITH_TOTP_CODE -> { - Log.i("AuthQuickstart", "Received next step as confirm sign in with TOTP code") - // Prompt the user to enter the TOTP code generated in their authenticator app - // Then invoke `confirmSignIn` api with the code + Log.i("AuthQuickstart", "Received next step as confirm sign in with TOTP code") + // Prompt the user to enter the TOTP code generated in their authenticator app + // Then invoke `confirmSignIn` api with the code + } + AuthSignInStep.CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION -> { + Log.i("AuthQuickstart", "Received next step as continue sign in by selecting an MFA method to setup"); + Log.i("AuthQuickstart", "Allowed MFA types for setup" + nextStep.getAllowedMFATypes()); + // Prompt the user to select the MFA type they want to setup + // Then invoke `confirmSignIn` api with the MFA type + } + AuthSignInStep.CONTINUE_SIGN_IN_WITH_EMAIL_MFA_SETUP -> { + Log.i("AuthQuickstart", "Received next step as continue sign in by setting up email MFA"); + // Prompt the user to enter the email address they would like to use to receive OTPs + // Then invoke `confirmSignIn` api with the email address } AuthSignInStep.CONTINUE_SIGN_IN_WITH_TOTP_SETUP -> { Log.i("AuthQuickstart", "Received next step as continue sign in by setting up TOTP") @@ -544,6 +1216,12 @@ try { // Prompt the user to enter the SMS MFA code they received // Then invoke `confirmSignIn` api with the code } + AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP -> { + Log.i("AuthQuickstart", "OTP code sent to " + nextStep.getCodeDeliveryDetails().getDestination()); + Log.i("AuthQuickstart", "Additional Info :" + nextStep.getAdditionalInfo()); + // Prompt the user to enter the OTP MFA code they received + // Then invoke `confirmSignIn` api with the code + } AuthSignInStep.CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE -> { Log.i("AuthQuickstart","Custom challenge, additional info: ${nextStep.additionalInfo}") // Prompt the user to enter custom challenge answer @@ -605,6 +1283,17 @@ try { // Prompt the user to enter the TOTP code generated in their authenticator app // Then invoke `confirmSignIn` api with the code } + AuthSignInStep.CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION -> { + Log.i("AuthQuickstart", "Received next step as continue sign in by selecting an MFA method to setup"); + Log.i("AuthQuickstart", "Allowed MFA types for setup" + nextStep.getAllowedMFATypes()); + // Prompt the user to select the MFA type they want to setup + // Then invoke `confirmSignIn` api with the MFA type + } + AuthSignInStep.CONTINUE_SIGN_IN_WITH_EMAIL_MFA_SETUP -> { + Log.i("AuthQuickstart", "Received next step as continue sign in by setting up email MFA"); + // Prompt the user to enter the email address they would like to use to receive OTPs + // Then invoke `confirmSignIn` api with the email address + } AuthSignInStep.CONTINUE_SIGN_IN_WITH_TOTP_SETUP -> { Log.i("AuthQuickstart", "Received next step as continue sign in by setting up TOTP") Log.i("AuthQuickstart", "Shared secret that will be used to set up TOTP in the authenticator app ${nextStep.totpSetupDetails.sharedSecret}") @@ -623,6 +1312,12 @@ try { // Prompt the user to enter the SMS MFA code they received // Then invoke `confirmSignIn` api with the code } + AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP -> { + Log.i("AuthQuickstart", "OTP code sent to ${nextStep.codeDeliveryDetails?.destination}") + Log.i("AuthQuickstart", "Additional Info ${nextStep.additionalInfo}") + // Prompt the user to enter the OTP MFA code they received + // Then invoke `confirmSignIn` api with the code + } AuthSignInStep.CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE -> { Log.i("AuthQuickstart", "Custom challenge, additional info: ${nextStep.additionalInfo}") // Prompt the user to enter custom challenge answer @@ -674,20 +1369,33 @@ RxAmplify.Auth.signIn("username", "password", options).subscribe( { AuthNextSignInStep nextStep = result.getNextStep(); switch (nextStep.getSignInStep()) { - case CONFIRM_SIGN_IN_WITH_TOTP_CODE -> { + case CONFIRM_SIGN_IN_WITH_TOTP_CODE: { Log.i("AuthQuickstart", "Received next step as confirm sign in with TOTP code"); // Prompt the user to enter the TOTP code generated in their authenticator app // Then invoke `confirmSignIn` api with the code break; } - case CONTINUE_SIGN_IN_WITH_TOTP_SETUP -> { + case CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION: { + Log.i("AuthQuickstart", "Received next step as continue sign in by selecting an MFA method to setup"); + Log.i("AuthQuickstart", "Allowed MFA types for setup" + nextStep.getAllowedMFATypes()); + // Prompt the user to select the MFA type they want to setup + // Then invoke `confirmSignIn` api with the MFA type + break; + } + case CONTINUE_SIGN_IN_WITH_EMAIL_MFA_SETUP: { + Log.i("AuthQuickstart", "Received next step as continue sign in by setting up email MFA"); + // Prompt the user to enter the email address they would like to use to receive OTPs + // Then invoke `confirmSignIn` api with the email address + break; + } + case CONTINUE_SIGN_IN_WITH_TOTP_SETUP: { Log.i("AuthQuickstart", "Received next step as continue sign in by setting up TOTP"); Log.i("AuthQuickstart", "Shared secret that will be used to set up TOTP in the authenticator app" + nextStep.getTotpSetupDetails().getSharedSecret()); // Prompt the user to enter the TOTP code generated in their authenticator app // Then invoke `confirmSignIn` api with the code break; } - case CONTINUE_SIGN_IN_WITH_MFA_SELECTION -> { + case CONTINUE_SIGN_IN_WITH_MFA_SELECTION: { Log.i("AuthQuickstart", "Received next step as continue sign in by selecting MFA type"); Log.i("AuthQuickstart", "Allowed MFA type" + nextStep.getAllowedMFATypes()); // Prompt the user to select the MFA type they want to use @@ -701,6 +1409,13 @@ RxAmplify.Auth.signIn("username", "password", options).subscribe( // Then invoke `confirmSignIn` api with the code break; } + case CONFIRM_SIGN_IN_WITH_OTP: { + Log.i("AuthQuickstart", "OTP code sent to " + nextStep.getCodeDeliveryDetails().getDestination()); + Log.i("AuthQuickstart", "Additional Info :" + nextStep.getAdditionalInfo()); + // Prompt the user to enter the OTP MFA code they received + // Then invoke `confirmSignIn` api with the code + break; + } case CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE: { Log.i("AuthQuickstart", "Custom challenge, additional info: " + nextStep.getAdditionalInfo()); // Prompt the user to enter custom challenge answer @@ -746,7 +1461,7 @@ RxAmplify.Auth.signIn("username", "password", options).subscribe( -## Confirm signin with SMS MFA +## Confirm sign-in with SMS MFA If the next step is `CONFIRM_SIGN_IN_WITH_SMS_MFA_CODE`, Amplify Auth has sent the user a random code over SMS, and is waiting to find out if the user successfully received it. To handle this step, your app's UI must prompt the user to enter the code. After the user enters the code, your implementation must pass the value to Amplify Auth `confirmSignIn` API. @@ -846,23 +1561,38 @@ RxAmplify.Auth.confirmSignIn( -## Confirm signin with TOTP MFA +## Confirm sign-in with TOTP MFA -If the next step is `confirmSignInWithTOTPCode`, 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. +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. -## Continue signin with MFA Selection -If the next step is `continueSignInWithMFASelection`, the user must select the MFA method to use. Amplify Auth currently only supports SMS and TOTP as MFA methods. After the user selects an MFA method, your implementation must pass the selected MFA method to Amplify Auth using `confirmSignIn` API. +## Confirm sign-in with Email MFA -## Continue signin with TOTP Setup +If the next step is `CONFIRM_SIGN_IN_WITH_EMAIL_MFA_CODE`, Amplify Auth has sent the user a random code to their email address and is waiting to find out if the user successfully received it. To handle this step, your app's UI must prompt the user to enter the code. After the user enters the code, your implementation must pass the value to Amplify Auth `confirmSignIn` API. -If the next step is `continueSignInWithTOTPSetup`, then the user must provide a TOTP code to complete the sign in process. The step returns an associated value of type `TOTPSetupDetails` which would be used for generating TOTP. `TOTPSetupDetails` provides a helper method called `getSetupURI` that can be used to generate a URI, which can be used by native password managers for TOTP association. For example. if the URI is used on Apple platforms, it will trigger the platform's native password manager to associate TOTP with the account. For more advanced use cases, `TOTPSetupDetails` also contains the `sharedSecret` that will be used to either generate a QR code or can be manually entered into an authenticator app. +Note: the signIn result also includes an `AuthCodeDeliveryDetails` member. It includes additional information about the code delivery such as the partial email address of the recipient. + +## 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. + +## Continue sign-in with Email Setup + +If the next step is `CONTINUE_SIGN_IN_WITH_EMAIL_MFA_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. + +## Continue sign-in with TOTP Setup + +If the next step is `CONTINUE_SIGN_IN_WITH_TOTP_SETUP`, then the user must provide a TOTP code to complete the sign in process. The step returns an associated value of type `TOTPSetupDetails` which would be used for generating TOTP. `TOTPSetupDetails` provides a helper method called `getSetupURI` that can be used to generate a URI, which can be used by native password managers for TOTP association. For example. if the URI is used on Apple platforms, it will trigger the platform's native password manager to associate TOTP with the account. For more advanced use cases, `TOTPSetupDetails` also contains the `sharedSecret` that will be used to either generate a QR code or can 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. -## Confirm signin with custom challenge +## Continue sign-in with MFA Setup Selection + +If the next step is `CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION`, the user must select the MFA method to setup. 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. + +## 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 Lambda trigger you setup when you configured a [custom sign in flow](/[platform]/build-a-backend/auth/customize-auth-lifecycle/custom-auth-flows/#sign-in-a-user). To complete this step, you should prompt the user for the custom challenge answer, and pass the answer to the `confirmSignIn` API. @@ -873,19 +1603,19 @@ If the next step is `CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE`, Amplify Auth is awa ```java try { Amplify.Auth.confirmSignIn( - "challenge answer", - result -> { - if (result.isSignedIn()) { - Log.i("AuthQuickstart", "Confirm signIn succeeded"); - } else { - Log.i("AuthQuickstart", "Confirm sign in not complete. There might be additional steps: " + result.getNextStep()); - // Switch on the next step to take appropriate actions. - // If `signInResult.isSignedIn` is true, the next step - // is 'done', and the user is now signed in. - } - }, - error -> Log.e("AuthQuickstart", "Confirm sign in failed: " + error) - ); + "challenge answer", + result -> { + if (result.isSignedIn()) { + Log.i("AuthQuickstart", "Confirm signIn succeeded"); + } else { + Log.i("AuthQuickstart", "Confirm sign in not complete. There might be additional steps: " + result.getNextStep()); + // Switch on the next step to take appropriate actions. + // If `signInResult.isSignedIn` is true, the next step + // is 'done', and the user is now signed in. + } + }, + error -> Log.e("AuthQuickstart", "Confirm sign in failed: " + error) + ); } catch (Exception error) { Log.e("AuthQuickstart", "Unexpected error: " + error); } @@ -897,21 +1627,21 @@ try { ```kotlin try { -Amplify.Auth.confirmSignIn( - "challenge answer", - { result -> - if (result.isSignedIn) { - Log.i("AuthQuickstart","Confirm signIn succeeded") - } else { - Log.i("AuthQuickstart", "Confirm sign in not complete. There might be additional steps: ${result.nextStep}") - // Switch on the next step to take appropriate actions. - // If `signInResult.isSignedIn` is true, the next step - // is 'done', and the user is now signed in. - } - } - ) { error -> - Log.e("AuthQuickstart", "Confirm sign in failed: $error") - } + Amplify.Auth.confirmSignIn( + "challenge answer", + { result -> + if (result.isSignedIn) { + Log.i("AuthQuickstart","Confirm signIn succeeded") + } else { + Log.i("AuthQuickstart", "Confirm sign in not complete. There might be additional steps: ${result.nextStep}") + // Switch on the next step to take appropriate actions. + // If `signInResult.isSignedIn` is true, the next step + // is 'done', and the user is now signed in. + } + } + ) { error -> + Log.e("AuthQuickstart", "Confirm sign in failed: $error") + } } catch (error: Exception) { Log.e("AuthQuickstart", "Unexpected error: $error") } @@ -946,19 +1676,19 @@ try { ```java RxAmplify.Auth.confirmSignIn( - "challenge answer").subscribe( - result -> { - if (result.isSignedIn()) { - Log.i("AuthQuickstart", "Confirm signIn succeeded"); - } else { - Log.i("AuthQuickstart", "Confirm sign in not complete. There might be additional steps: " + result.getNextStep()); - // Switch on the next step to take appropriate actions. - // If `signInResult.isSignedIn` is true, the next step - // is 'done', and the user is now signed in. - } - }, - error -> Log.e("AuthQuickstart", "Confirm sign in failed: " + error) - ); + "challenge answer").subscribe( + result -> { + if (result.isSignedIn()) { + Log.i("AuthQuickstart", "Confirm signIn succeeded"); + } else { + Log.i("AuthQuickstart", "Confirm sign in not complete. There might be additional steps: " + result.getNextStep()); + // Switch on the next step to take appropriate actions. + // If `signInResult.isSignedIn` is true, the next step + // is 'done', and the user is now signed in. + } + }, + error -> Log.e("AuthQuickstart", "Confirm sign in failed: " + error) +); ``` @@ -976,7 +1706,7 @@ NotAuthorizedException{message=Failed since user is not authorized., cause=NotAu -## Confirm signin with new password +## Confirm sign-in with new password If you receive a `UserNotConfirmedException` while signing in, Amplify Auth requires a new password for the user before they can proceed. Prompt the user for a new password and pass it to the `confirmSignIn` API. @@ -1283,7 +2013,7 @@ func signIn(username: String, password: String) async { let signInResult = try await Amplify.Auth.signIn(username: username, password: password) switch signInResult.nextStep { case .confirmSignInWithSMSMFACode(let deliveryDetails, let info): - print("SMS code send to \(deliveryDetails.destination)") + print("SMS code sent to \(deliveryDetails.destination)") print("Additional info \(String(describing: info))") // Prompt the user to enter the SMSMFA code they received @@ -1294,6 +2024,12 @@ func signIn(username: String, password: String) async { // Prompt the user to enter the TOTP code generated in their authenticator app // Then invoke `confirmSignIn` api with the code + + case .confirmSignInWithOTP(let deliveryDetails): + print("Email code sent to \(deliveryDetails.destination)") + + // Prompt the user to enter the Email MFA code they received + // Then invoke `confirmSignIn` api with the code case .continueSignInWithTOTPSetup(let setUpDetails): print("Received next step as continue sign in by setting up TOTP") @@ -1302,6 +2038,19 @@ func signIn(username: String, password: String) async { // Prompt the user to enter the TOTP code generated in their authenticator app // Then invoke `confirmSignIn` api with the code + case .continueSignInWithEmailMFASetup: + print("Received next step as continue sign in by setting up email MFA") + + // Prompt the user to enter the email address they wish to use for MFA + // Then invoke `confirmSignIn` api with the email address + + case .continueSignInWithMFASetupSelection(let allowedMFATypes): + print("Received next step as continue sign in by selecting MFA type to setup") + print("Allowed MFA types \(allowedMFATypes)") + + // Prompt the user to select the MFA type they want to setup + // Then invoke `confirmSignIn` api with the MFA type + case .continueSignInWithMFASelection(let allowedMFATypes): print("Received next step as continue sign in by selecting MFA type") print("Allowed MFA types \(allowedMFATypes)") @@ -1353,7 +2102,7 @@ func signIn(username: String, password: String) async { The `nextStep` property is of enum type `AuthSignInStep`. Depending on its value, your code should take one of the following actions: -## Confirm signin with SMS MFA +## Confirm sign-in with SMS MFA If the next step is `confirmSignInWithSMSMFACode`, Amplify Auth has sent the user a random code over SMS, and is waiting to find out if the user successfully received it. To handle this step, your app's UI must prompt the user to enter the code. After the user enters the code, your implementation must pass the value to Amplify Auth `confirmSignIn` API. Note: the signin result also includes an `AuthCodeDeliveryDetails` member. It includes additional information about the code delivery such as the partial phone number of the SMS recipient. @@ -1414,7 +2163,7 @@ func confirmSignIn(confirmationCodeFromUser: String) -> AnyCancellable { -## Confirm signin with TOTP MFA +## Confirm sign-in with TOTP MFA If the next step is `confirmSignInWithTOTPCode`, 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. @@ -1474,9 +2223,76 @@ func confirmSignIn(totpCode: String) -> AnyCancellable { -## Continue signin with MFA Selection -If the next step is `continueSignInWithMFASelection`, the user must select the MFA method to use. Amplify Auth currently only supports SMS and TOTP as MFA methods. After the user selects an MFA method, your implementation must pass the selected MFA method to Amplify Auth using `confirmSignIn` API. +## Confirm sign-in with Email MFA +If the next step is `confirmSignInWithOTP`, Amplify Auth has sent a random code to the user's email address, and is waiting to find out if the user successfully received it. To handle this step, your app's UI must prompt the user to enter the code. After the user enters the code, your implementation must pass the value to Amplify Auth `confirmSignIn` API. + + + +**Note:** the sign-in result also includes an `AuthCodeDeliveryDetails` member. It includes additional information about the code delivery such as the partial email address of the recipient. + + + + + + + +```swift +func confirmSignIn(confirmationCodeFromUser: String) async { + do { + let signInResult = try await Amplify.Auth.confirmSignIn(challengeResponse: confirmationCodeFromUser) + if signInResult.isSignedIn { + print("Confirm sign in succeeded. The user is signed in.") + } else { + print("Confirm sign in succeeded.") + print("Next step: \(signInResult.nextStep)") + // Switch on the next step to take appropriate actions. + // If `signInResult.isSignedIn` is true, the next step + // is 'done', and the user is now signed in. + } + } catch let error as AuthError { + print("Confirm sign in failed \(error)") + } catch { + print("Unexpected error: \(error)") + } +} +``` + + + + + +```swift +func confirmSignIn(confirmationCodeFromUser: String) -> AnyCancellable { + Amplify.Publisher.create { + try await Amplify.Auth.confirmSignIn(challengeResponse: confirmationCodeFromUser) + }.sink { + if case let .failure(authError) = $0 { + print("Confirm sign in failed \(authError)") + } + } + receiveValue: { signInResult in + if signInResult.isSignedIn { + print("Confirm sign in succeeded. The user is signed in.") + } else { + print("Confirm sign in succeeded.") + print("Next step: \(signInResult.nextStep)") + // Switch on the next step to take appropriate actions. + // If `signInResult.isSignedIn` is true, the next step + // is 'done', and the user is now signed in. + } + } +} +``` + + + + + + +## Continue sign-in with MFA Selection + +If the next step is `continueSignInWithMFASelection`, 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. @@ -1523,7 +2339,20 @@ func confirmSignInWithTOTPAsMFASelection() -> AnyCancellable { -## Continue signin with TOTP Setup + +## Continue sign-in with Email Setup +If the next step is `continueSignInWithEmailMFASetup`, 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. + +```swift +// Confirm sign in with Email Setup +case .continueSignInWithEmailMFASetup: + print("Received next step as continue sign in by setting up email MFA") + + // Prompt the user to enter the email address they wish to use for MFA + // Then invoke `confirmSignIn` api with the email address +``` + +## Continue sign-in with TOTP Setup If the next step is `continueSignInWithTOTPSetup`, then the user must provide a TOTP code to complete the sign in process. The step returns an associated value of type `TOTPSetupDetails` which would be used for generating TOTP. `TOTPSetupDetails` provides a helper method called `getSetupURI` that can be used to generate a URI, which can be used by native password managers for TOTP association. For example. if the URI is used on Apple platforms, it will trigger the platform's native password manager to associate TOTP with the account. For more advanced use cases, `TOTPSetupDetails` also contains the `sharedSecret` that will be used to either generate a QR code or can be manually entered into an authenticator app. @@ -1596,7 +2425,57 @@ func confirmSignInWithTOTPSetup(totpCodeFromAuthenticatorApp: String) -> AnyCanc -## Confirm signin with custom challenge +## Continue sign-in with MFA Setup Selection + +If the next step is `continueSignInWithMFASetupSelection`, 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. + + + + + +```swift +func continueSignInWithEmailMFASetupSelection() async { + do { + let signInResult = try await Amplify.Auth.confirmSignIn( + challengeResponse: MFAType.email.challengeResponse) + + if case .confirmSignInWithTOTPCode = signInResult.nextStep { + print("Received next step as confirm sign in with TOTP") + } + + } catch { + print("Confirm sign in failed \(error)") + } +} +``` + + + + + +```swift +func continueSignInWithEmailMFASetupSelection() -> AnyCancellable { + Amplify.Publisher.create { + try await Amplify.Auth.confirmSignIn( + challengeResponse: MFAType.email.challengeResponse) + }.sink { + if case let .failure(authError) = $0 { + print("Confirm sign in failed \(authError)") + } + } + receiveValue: { signInResult in + if case .confirmSignInWithTOTPCode = signInResult.nextStep { + print("Received next step as confirm sign in with TOTP") + } + } +} +``` + + + + + +## Confirm sign-in with custom challenge If the next step is `confirmSignInWithCustomChallenge`, Amplify Auth is awaiting completion of a custom authentication challenge. The challenge is based on the Lambda trigger you setup when you configured a [custom sign in flow](/[platform]/build-a-backend/auth/customize-auth-lifecycle/custom-auth-flows/#sign-in-a-user). To complete this step, you should prompt the user for the custom challenge answer, and pass the answer to the `confirmSignIn` API. @@ -1667,7 +2546,7 @@ Exception: notAuthorized{message=Failed since user is not authorized., cause=No -## Confirm signin with new password +## Confirm sign-in with new password If the next step is `confirmSignInWithNewPassword`, Amplify Auth requires a new password for the user before they can proceed. Prompt the user for a new password and pass it to the `confirmSignIn` API. diff --git a/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/sign-in/index.mdx b/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/sign-in/index.mdx index e497d3c9acf..d018ea92424 100644 --- a/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/sign-in/index.mdx +++ b/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/sign-in/index.mdx @@ -66,7 +66,7 @@ The quickest way to get started with Amplify Auth in your frontend application i {/* signIn api definition */} - + ```ts import { signIn } from 'aws-amplify/auth' @@ -253,52 +253,76 @@ func signIn(username: String, password: String) -> AnyCancellable { The `signIn` API response will include a `nextStep` property, which can be used to determine if further action is required. It may return the following next steps: - -- `CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED` - The user was created with a temporary password and must set a new one. Complete the process with `confirmSignIn`. -- `CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE` - The sign-in must be confirmed with a custom challenge response. Complete the process with `confirmSignIn`. -- `CONFIRM_SIGN_IN_WITH_TOTP_CODE` - The sign-in must be confirmed with a TOTP code from the user. Complete the process with `confirmSignIn`. -- `CONTINUE_SIGN_IN_WITH_TOTP_SETUP` - The TOTP setup process must be continued. Complete the process with `confirmSignIn`. -- `CONFIRM_SIGN_IN_WITH_SMS_CODE` - The sign-in must be confirmed with a SMS code from the user. Complete the process with `confirmSignIn`. -- `CONTINUE_SIGN_IN_WITH_MFA_SELECTION` - The user must select their mode of MFA verification before signing in. Complete the process with `confirmSignIn`. -- `RESET_PASSWORD` - The user must reset their password via `resetPassword`. -- `CONFIRM_SIGN_UP` - The user hasn't completed the sign-up flow fully and must be confirmed via `confirmSignUp`. -- `DONE` - The sign in process has been completed. + +| Next Step | Description | +| --------- | ----------- | +| `CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED` | The user was created with a temporary password and must set a new one. Complete the process with `confirmSignIn`. | +| `CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE` | The sign-in must be confirmed with a custom challenge response. Complete the process with `confirmSignIn`. | +| `CONFIRM_SIGN_IN_WITH_TOTP_CODE` | The sign-in must be confirmed with a TOTP code from the user. Complete the process with `confirmSignIn`. | +| `CONFIRM_SIGN_IN_WITH_SMS_CODE` | The sign-in must be confirmed with a SMS code from the user. Complete the process with `confirmSignIn`. | +| `CONFIRM_SIGN_IN_WITH_EMAIL_CODE` | The sign-in must be confirmed with a EMAIL code from the user. Complete the process with `confirmSignIn`. | +| `CONTINUE_SIGN_IN_WITH_MFA_SELECTION` | The user must select their mode of MFA verification before signing in. Complete the process with `confirmSignIn`. | +| `CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION` | The user must select their mode of MFA verification to setup. Complete the process by passing either `"EMAIL"` or `"TOTP"` to `confirmSignIn`. | +| `CONTINUE_SIGN_IN_WITH_TOTP_SETUP` | The TOTP setup process must be continued. Complete the process with `confirmSignIn`. | +| `CONTINUE_SIGN_IN_WITH_EMAIL_SETUP` | The EMAIL setup process must be continued. Complete the process by passing a valid email address to `confirmSignIn`. | +| `RESET_PASSWORD` | The user must reset their password via `resetPassword`. | +| `CONFIRM_SIGN_UP` | The user hasn't completed the sign-up flow fully and must be confirmed via `confirmSignUp`. | +| `DONE` | The sign in process has been completed. | - -- `confirmSignInWithNewPassword` - The user was created with a temporary password and must set a new one. Complete the process with `confirmSignIn`. -- `confirmSignInWithCustomChallenge` - The sign-in must be confirmed with a custom challenge response. Complete the process with `confirmSignIn`. -- `confirmSignInWithTOTPCode` - The sign-in must be confirmed with a TOTP code from the user. Complete the process with `confirmSignIn`. -- `continueSignInWithTOTPSetup` - The TOTP setup process must be continued. Complete the process with `confirmSignIn`. -- `confirmSignInWithSMSMFACode` - The sign-in must be confirmed with a SMS code from the user. Complete the process with `confirmSignIn`. -- `continueSignInWithMFASelection` - The user must select their mode of MFA verification before signing in. Complete the process with `confirmSignIn`. -- `resetPassword` - The user must reset their password via `resetPassword`. -- `confirmSignUp` - The user hasn't completed the sign-up flow fully and must be confirmed via `confirmSignUp`. -- `done` - The sign in process has been completed. + +| Next Step | Description | +| --------- | ----------- | +| `CONFIRM_SIGN_IN_WITH_NEW_PASSWORD` | The user was created with a temporary password and must set a new one. Complete the process with `confirmSignIn`. | +| `CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE` | The sign-in must be confirmed with a custom challenge response. Complete the process with `confirmSignIn`. | +| `CONFIRM_SIGN_IN_WITH_TOTP_CODE` | The sign-in must be confirmed with a TOTP code from the user. Complete the process with `confirmSignIn`. | +| `CONFIRM_SIGN_IN_WITH_SMS_MFA_CODE` | The sign-in must be confirmed with a SMS code from the user. Complete the process with `confirmSignIn`. | +| `CONFIRM_SIGN_IN_WITH_OTP` | The sign-in must be confirmed with a code from the user (sent via SMS or Email). Complete the process with `confirmSignIn`. | +| `CONTINUE_SIGN_IN_WITH_MFA_SELECTION` | The user must select their mode of MFA verification before signing in. Complete the process with `confirmSignIn`. | +| `CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION` | The user must select their mode of MFA verification to setup. Complete the process by passing either `MFAType.EMAIL.challengeResponse` or `MFAType.TOTP.challengeResponse` to `confirmSignIn`. | +| `CONTINUE_SIGN_IN_WITH_TOTP_SETUP` | The TOTP setup process must be continued. Complete the process with `confirmSignIn`. | +| `CONTINUE_SIGN_IN_WITH_EMAIL_MFA_SETUP` | The EMAIL setup process must be continued. Complete the process by passing a valid email address to `confirmSignIn`. | +| `RESET_PASSWORD` | The user must reset their password via `resetPassword`. | +| `CONFIRM_SIGN_UP` | The user hasn't completed the sign-up flow fully and must be confirmed via `confirmSignUp`. | +| `DONE` | The sign in process has been completed. | -For more information on handling the TOTP and MFA steps that may be returned, see [multi-factor authentication](/[platform]/build-a-backend/auth/concepts/multi-factor-authentication/). - -### Confirm sign-in - - - -```ts title="src/main.ts" -import { confirmSignIn, signIn } from 'aws-amplify/auth' + +| Next Step | Description | +| --------- | ----------- | +| `confirmSignInWithNewPassword` | The user was created with a temporary password and must set a new one. Complete the process with `confirmSignIn`. | +| `confirmSignInWithCustomChallenge` | The sign-in must be confirmed with a custom challenge response. Complete the process with `confirmSignIn`. | +| `confirmSignInWithTOTPCode` | The sign-in must be confirmed with a TOTP code from the user. Complete the process with `confirmSignIn`. | +| `confirmSignInWithSMSMFACode` | The sign-in must be confirmed with a SMS code from the user. Complete the process with `confirmSignIn`. | +| `confirmSignInWithOTP` | The sign-in must be confirmed with a code from the user (sent via SMS or Email). Complete the process with `confirmSignIn`. | +| `continueSignInWithMFASelection` | The user must select their mode of MFA verification before signing in. Complete the process with `confirmSignIn`. | +| `continueSignInWithMFASetupSelection` | The user must select their mode of MFA verification to setup. Complete the process by passing either `MFAType.email.challengeResponse` or `MFAType.totp.challengeResponse ` to `confirmSignIn`. | +| `continueSignInWithTOTPSetup` | The TOTP setup process must be continued. Complete the process with `confirmSignIn`. | +| `continueSignInWithEmailMFASetup` | The EMAIL setup process must be continued. Complete the process by passing a valid email address to `confirmSignIn`. | +| `resetPassword` | The user must reset their password via `resetPassword`. | +| `confirmSignUp` | The user hasn't completed the sign-up flow fully and must be confirmed via `confirmSignUp`. | +| `done` | The sign in process has been completed. | + -const { nextStep } = await signIn({ - username: "hello@mycompany.com", - password: "hunter2", -}) + +| Next Step | Description | +| --------- | ----------- | +| `confirmSignInWithNewPassword` | The user was created with a temporary password and must set a new one. Complete the process with `confirmSignIn`. | +| `confirmSignInWithCustomChallenge` | The sign-in must be confirmed with a custom challenge response. Complete the process with `confirmSignIn`. | +| `confirmSignInWithTotpMfaCode` | The sign-in must be confirmed with a TOTP code from the user. Complete the process with `confirmSignIn`. | +| `confirmSignInWithSmsMfaCode` | The sign-in must be confirmed with a SMS code from the user. Complete the process with `confirmSignIn`. | +| `confirmSignInWithOtpCode` | The sign-in must be confirmed with a code from the user (sent via SMS or Email). Complete the process with `confirmSignIn`. | +| `continueSignInWithMfaSelection` | The user must select their mode of MFA verification before signing in. Complete the process with `confirmSignIn`. | +| `continueSignInWithMfaSetupSelection` | The user must select their mode of MFA verification to setup. Complete the process by passing either `"EMAIL"` or `"TOTP"` to `confirmSignIn`. | +| `continueSignInWithTotpSetup` | The TOTP setup process must be continued. Complete the process with `confirmSignIn`. | +| `continueSignInWithEmailMfaSetup` | The EMAIL setup process must be continued. Complete the process by passing a valid email address to `confirmSignIn`. | +| `resetPassword` | The user must reset their password via `resetPassword`. | +| `confirmSignUp` | The user hasn't completed the sign-up flow fully and must be confirmed via `confirmSignUp`. | +| `done` | The sign in process has been completed. | + -if (nextStep === "CONFIRM_SIGN_IN_WITH_SMS_CODE") { - await confirmSignIn({ - challengeResponse: "12345" - }) -} -``` +For more information on handling the MFA steps that may be returned, see [multi-factor authentication](/[platform]/build-a-backend/auth/concepts/multi-factor-authentication/). - {/* @todo */} @@ -404,7 +428,13 @@ export default function App() { {/* with multi-factor auth */} ## With multi-factor auth enabled -When multi-factor authentication (MFA) is **required** with SMS in your backend auth resource, you will need to pass the phone number during sign-up API call. If you are using the `email` or `username` as the primary sign-in mechanism, you will need to pass the `phone_number` attribute as a user attribute. This will change depending on if you enable SMS, TOTP, or both. Visit the [multi-factor authentication documentation](/[platform]/build-a-backend/auth/concepts/multi-factor-authentication/) to learn more about enabling MFA on your backend auth resource. +When you have Email or SMS MFA enabled, Cognito will send messages to your users on your behalf. Email and SMS messages require that your users have email address and phone number attributes respectively. It is recommended to set these attributes as required in your user pool if you wish to use either Email MFA or SMS MFA. When these attributes are required, a user must provide these details before they can complete the sign up process. + +If you have set MFA to be required and you have activated more than one authentication factor, Cognito will prompt new users to select an MFA factor they want to use. Users must have a phone number to select SMS and an email address to select email MFA. + +If a user doesn't have the necessary attributes defined for any available message based MFA, Cognito will prompt them to set up TOTP. + +Visit the [multi-factor authentication documentation](/[platform]/build-a-backend/auth/concepts/multi-factor-authentication/) to learn more about enabling MFA on your backend auth resource. @@ -550,12 +580,58 @@ func signUp(username: String, password: String, email: String, phonenumber: Stri - -You will then confirm sign-up, sign in, and receive a `nextStep` in the sign-in result of type `CONFIRM_SIGN_IN_WITH_SMS_MFA_CODE`. A confirmation code will also be texted to the phone number provided above. Pass the code you received to the `confirmSignIn` API: +### Confirm sign-in + + +Following sign in, you will receive a `nextStep` in the sign-in result of one of the following types. Collect the user response and then pass to the `confirmSignIn` API to complete the sign in flow. +| Next Step | Description | +| --------- | ----------- | +| `CONFIRM_SIGN_IN_WITH_TOTP_CODE` | The sign-in must be confirmed with a TOTP code from the user. Complete the process with `confirmSignIn`. | +| `CONFIRM_SIGN_IN_WITH_SMS_CODE` | The sign-in must be confirmed with a SMS code from the user. Complete the process with `confirmSignIn`. | +| `CONFIRM_SIGN_IN_WITH_EMAIL_CODE` | The sign-in must be confirmed with a EMAIL code from the user. Complete the process with `confirmSignIn`. | +| `CONTINUE_SIGN_IN_WITH_MFA_SELECTION` | The user must select their mode of MFA verification before signing in. Complete the process with `confirmSignIn`. | +| `CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION` | The user must select their mode of MFA verification to setup. Complete the process by passing either `"EMAIL"` or `"TOTP"` to `confirmSignIn`. | +| `CONTINUE_SIGN_IN_WITH_TOTP_SETUP` | The TOTP setup process must be continued. Complete the process with `confirmSignIn`. | +| `CONTINUE_SIGN_IN_WITH_EMAIL_SETUP` | The EMAIL setup process must be continued. Complete the process by passing a valid email address to `confirmSignIn`. | + + + +Following sign in, you will receive a `nextStep` in the sign-in result of one of the following types. Collect the user response and then pass to the `confirmSignIn` API to complete the sign in flow. +| Next Step | Description | +| --------- | ----------- | +| `CONFIRM_SIGN_IN_WITH_TOTP_CODE` | The sign-in must be confirmed with a TOTP code from the user. Complete the process with `confirmSignIn`. | +| `CONFIRM_SIGN_IN_WITH_SMS_MFA_CODE` | The sign-in must be confirmed with a SMS code from the user. Complete the process with `confirmSignIn`. | +| `CONFIRM_SIGN_IN_WITH_OTP` | The sign-in must be confirmed with a code from the user (sent via SMS or Email). Complete the process with `confirmSignIn`. | +| `CONTINUE_SIGN_IN_WITH_MFA_SELECTION` | The user must select their mode of MFA verification before signing in. Complete the process with `confirmSignIn`. | +| `CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION` | The user must select their mode of MFA verification to setup. Complete the process by passing either `MFAType.EMAIL.challengeResponse` or `MFAType.TOTP.challengeResponse` to `confirmSignIn`. | +| `CONTINUE_SIGN_IN_WITH_TOTP_SETUP` | The TOTP setup process must be continued. Complete the process with `confirmSignIn`. | +| `CONTINUE_SIGN_IN_WITH_EMAIL_MFA_SETUP` | The EMAIL setup process must be continued. Complete the process by passing a valid email address to `confirmSignIn`. | -You will then confirm sign-up, sign in, and receive a `nextStep` in the sign-in result of type `confirmSignInWithSMSMFACode`. A confirmation code will also be texted to the phone number provided above. Pass the code you received to the `confirmSignIn` API: +Following sign in, you will receive a `nextStep` in the sign-in result of one of the following types. Collect the user response and then pass to the `confirmSignIn` API to complete the sign in flow. +| Next Step | Description | +| --------- | ----------- | +| `confirmSignInWithTOTPCode` | The sign-in must be confirmed with a TOTP code from the user. Complete the process with `confirmSignIn`. | +| `confirmSignInWithSMSMFACode` | The sign-in must be confirmed with a SMS code from the user. Complete the process with `confirmSignIn`. | +| `confirmSignInWithOTP` | The sign-in must be confirmed with a code from the user (sent via SMS or Email). Complete the process with `confirmSignIn`. | +| `continueSignInWithMFASelection` | The user must select their mode of MFA verification before signing in. Complete the process with `confirmSignIn`. | +| `continueSignInWithMFASetupSelection` | The user must select their mode of MFA verification to setup. Complete the process by passing either `MFAType.email.challengeResponse` or `MFAType.totp.challengeResponse ` to `confirmSignIn`. | +| `continueSignInWithTOTPSetup` | The TOTP setup process must be continued. Complete the process with `confirmSignIn`. | +| `continueSignInWithEmailMFASetup` | The EMAIL setup process must be continued. Complete the process by passing a valid email address to `confirmSignIn`. | + + + +Following sign in, you will receive a `nextStep` in the sign-in result of one of the following types. Collect the user response and then pass to the `confirmSignIn` API to complete the sign in flow. +| Next Step | Description | +| --------- | ----------- | +| `confirmSignInWithTotpMfaCode` | The sign-in must be confirmed with a TOTP code from the user. Complete the process with `confirmSignIn`. | +| `confirmSignInWithSmsMfaCode` | The sign-in must be confirmed with a SMS code from the user. Complete the process with `confirmSignIn`. | +| `confirmSignInWithOtpCode` | The sign-in must be confirmed with a code from the user (sent via SMS or Email). Complete the process with `confirmSignIn`. | +| `continueSignInWithMfaSelection` | The user must select their mode of MFA verification before signing in. Complete the process with `confirmSignIn`. | +| `continueSignInWithMfaSetupSelection` | The user must select their mode of MFA verification to setup. Complete the process by passing either `MfaType.email.confirmationValue` or `MfaType.totp.confirmationValue` to `confirmSignIn`. | +| `continueSignInWithTotpSetup` | The TOTP setup process must be continued. Complete the process with `confirmSignIn`. | +| `continueSignInWithEmailMfaSetup` | The EMAIL setup process must be continued. Complete the process by passing a valid email address to `confirmSignIn`. | @@ -566,6 +642,62 @@ You will then confirm sign-up, sign in, and receive a `nextStep` in the sign-in + + +```ts title="src/main.ts" +import { confirmSignIn, 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", + }); +} + +``` + + + diff --git a/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/sign-up/index.mdx b/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/sign-up/index.mdx index 794d6b3ddf3..692d8f82939 100644 --- a/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/sign-up/index.mdx +++ b/src/pages/[platform]/build-a-backend/auth/connect-your-frontend/sign-up/index.mdx @@ -285,15 +285,26 @@ func signUp(username: String, password: String, email: String, phonenumber: Stri The `signUp` API response will include a `nextStep` property, which can be used to determine if further action is required. It may return the following next steps: - -- `CONFIRM_SIGN_UP` - The sign up needs to be confirmed by collecting a code from the user and calling `confirmSignUp`. -- `DONE` - The sign up process has been fully completed. -- `COMPLETE_AUTO_SIGN_IN` - The sign up process needs to complete by invoking the `autoSignIn` API. + +| Next Step | Description | +| --------- | ----------- | +| `CONFIRM_SIGN_UP` | The sign up needs to be confirmed by collecting a code from the user and calling `confirmSignUp`. | +| `DONE` | The sign up process has been fully completed. | +| `COMPLETE_AUTO_SIGN_IN` | The sign up process needs to complete by invoking the `autoSignIn` API. | + + + +| Next Step | Description | +| --------- | ----------- | +| `CONFIRM_SIGN_UP_STEP` | The sign up needs to be confirmed by collecting a code from the user and calling `confirmSignUp`. | +| `DONE` | The sign up process has been fully completed. | -- `confirmUser` - The sign up needs to be confirmed by collecting a code from the user and calling `confirmSignUp` API. -- `done` - The sign up process has been fully completed. +| Next Step | Description | +| --------- | ----------- | +| `confirmSignUp` | The sign up needs to be confirmed by collecting a code from the user and calling `confirmSignUp`. | +| `done` | The sign up process has been fully completed. | ## Confirm sign-up @@ -308,7 +319,7 @@ By default, each user that signs up remains in the unconfirmed status until they You can confirm the sign-up after receiving a confirmation code from the user: - + ```ts import { confirmSignUp } from 'aws-amplify/auth'; diff --git a/src/pages/[platform]/build-a-backend/auth/modify-resources-with-cdk/index.mdx b/src/pages/[platform]/build-a-backend/auth/modify-resources-with-cdk/index.mdx index ceb39bdef66..3d5c3b8b143 100644 --- a/src/pages/[platform]/build-a-backend/auth/modify-resources-with-cdk/index.mdx +++ b/src/pages/[platform]/build-a-backend/auth/modify-resources-with-cdk/index.mdx @@ -57,6 +57,60 @@ cfnUserPool.policies = { }; ``` +## Override Cognito UserPool multi-factor authentication options +While Email MFA is not yet supported with `defineAuth`, this feature can be enabled by modifying the underlying CDK construct. + +Start by ensuring your `defineAuth` resource configuration includes a compatible account recovery option and a custom SES sender. + +```ts title="amplify/auth/resource.ts" +import { defineAuth } from "@aws-amplify/backend" + +/** + * Define and configure your auth resource + * @see https://docs.amplify.aws/gen2/build-a-backend/auth + */ +export const auth = defineAuth({ + loginWith: { + email: true, + phone: true, + }, + multifactor: { + mode: "OPTIONAL", + sms: true, + totp: false, + }, + // Important! The logic to resolve this value cannot determine whether email mfa is enabled when overriding the resource. + // Be sure to pick a recovery option appropriate for your application. + accountRecovery: "EMAIL_AND_PHONE_WITHOUT_MFA", + senders: { + email: { + fromEmail: "registrations@example.com", + }, + }, +}) +``` +Next, extend the underlying CDK construct by activating [Amazon Cognito's Advanced Security Features (ASF)](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-settings-advanced-security.html) and add `EMAIL_OTP` to the enabled MFA options. + +```ts title="amplify/backend.ts" +import { defineBackend } from "@aws-amplify/backend" +import { auth } from "./auth/resource" + +const backend = defineBackend({ + auth, +}) + +const { cfnUserPool } = backend.auth.resources.cfnResources + +// enable ASF +cfnUserPool.userPoolAddOns = { + advancedSecurityMode: "AUDIT", +} + +// add email mfa +// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cognito-userpool.html#cfn-cognito-userpool-enabledmfas +cfnUserPool.enabledMfas = [...(cfnUserPool.enabledMfas || []), "EMAIL_OTP"] +``` + {/* token validity */} {/* BYO custom idp construct */} {/* extend auth/unauth roles */}