Page updated Nov 3, 2023

Multi-step sign-in

Amplify iOS v1 is now in Maintenance Mode until May 31st, 2024. This means that we will continue to include updates to ensure compatibility with backend services and security. No new features will be introduced in v1.

Please use the latest version (v2) of Amplify Library for Swift to get started.

If you are currently using v1, follow these instructions to upgrade to v2.

Amplify libraries should be used for all new cloud connected applications. If you are currently using the AWS Mobile SDK for iOS, you can access the documentation here.

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 ran amplify add auth. 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 in the signin result.

When called successfully, the signin APIs will return an AuthSignInResult. Inspect the nextStep property in the result to see if additional signin steps are required.

1func signIn(username: String, password: String) {
2 Amplify.Auth.signIn(username: username, password: password) { result in
3 do {
4 let signinResult = try result.get()
5 switch signinResult.nextStep {
6 case .confirmSignInWithSMSMFACode(let deliveryDetails, let info):
7 print("SMS code send to \(deliveryDetails.destination)")
8 print("Additional info \(info)")
9
10 // Prompt the user to enter the SMSMFA code they received
11 // Then invoke `confirmSignIn` api with the code
12
13 case .confirmSignInWithCustomChallenge(let info):
14 print("Custom challenge, additional info \(info)")
15
16 // Prompt the user to enter custom challenge answer
17 // Then invoke `confirmSignIn` api with the answer
18
19 case .confirmSignInWithNewPassword(let info):
20 print("New password additional info \(info)")
21
22 // Prompt the user to enter a new password
23 // Then invoke `confirmSignIn` api with new password
24
25 case .resetPassword(let info):
26 print("Reset password additional info \(info)")
27
28 // User needs to reset their password.
29 // Invoke `resetPassword` api to start the reset password
30 // flow, and once reset password flow completes, invoke
31 // `signIn` api to trigger signin flow again.
32
33 case .confirmSignUp(let info):
34 print("Confirm signup additional info \(info)")
35
36 // User was not confirmed during the signup process.
37 // Invoke `confirmSignUp` api to confirm the user if
38 // they have the confirmation code. If they do not have the
39 // confirmation code, invoke `resendSignUpCode` to send the
40 // code again.
41 // After the user is confirmed, invoke the `signIn` api again.
42 case .done:
43
44 // Use has successfully signed in to the app
45 print("Signin complete")
46 }
47 } catch {
48 print ("Sign in failed \(error)")
49 }
50 }
51}

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

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.

1func confirmSignIn(confirmationCodeFromUser: String) {
2 Amplify.Auth.confirmSignIn(challengeResponse: confirmationCodeFromUser) { result in
3 switch result {
4 case .success(let signInResult):
5 if signInResult.isSignedIn {
6 print("Confirm sign in succeeded. The user is signed in.")
7 } else {
8 print("Confirm sign in succeeded.")
9 print("Next step: \(signInResult.nextStep)")
10 // Switch on the next step to take appropriate actions.
11 // If `signInResult.isSignedIn` is true, the next step
12 // is 'done', and the user is now signed in.
13 }
14 case .failure(let error):
15 print("Confirm sign in failed \(error)")
16 }
17 }
18}
1func confirmSignIn(confirmationCodeFromUser: String) -> AnyCancellable {
2 Amplify.Auth.confirmSignIn(challengeResponse: confirmationCodeFromUser)
3 .resultPublisher
4 .sink {
5 if case let .failure(authError) = $0 {
6 print("Confirm sign in failed \(authError)")
7 }
8 }
9 receiveValue: { signInResult in
10 if signInResult.isSignedIn {
11 print("Confirm sign in succeeded. The user is signed in.")
12 } else {
13 print("Confirm sign in succeeded.")
14 print("Next step: \(signInResult.nextStep)")
15 // Switch on the next step to take appropriate actions.
16 // If `signInResult.isSignedIn` is true, the next step
17 // is 'done', and the user is now signed in.
18 }
19 }
20}

Confirm signin 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. To complete this step, you should prompt the user for the custom challenge answer, and pass the answer to the confirmSignIn API.

1func confirmSignIn(challengeAnswerFromUser: String) {
2 Amplify.Auth.confirmSignIn(challengeResponse: challengeAnswerFromUser) { result in
3 switch result {
4 case .success(let signInResult):
5 if signInResult.isSignedIn {
6 print("Confirm sign in succeeded. The user is signed in.")
7 } else {
8 print("Confirm sign in succeeded.")
9 print("Next step: \(signInResult.nextStep)")
10 // Switch on the next step to take appropriate actions.
11 // If `signInResult.isSignedIn` is true, the next step
12 // is 'done', and the user is now signed in.
13 }
14 case .failure(let error):
15 print("Confirm sign in failed \(error)")
16 }
17 }
18}
1func confirmSignIn(challengeAnswerFromUser: String) -> AnyCancellable {
2 Amplify.Auth.confirmSignIn(challengeResponse: challengeAnswerFromUser)
3 .resultPublisher
4 .sink {
5 if case let .failure(authError) = $0 {
6 print("Confirm sign in failed \(authError)")
7 }
8 }
9 receiveValue: { signInResult in
10 if signInResult.isSignedIn {
11 print("Confirm sign in succeeded. The user is signed in.")
12 } else {
13 print("Confirm sign in succeeded.")
14 print("Next step: \(signInResult.nextStep)")
15 // Switch on the next step to take appropriate actions.
16 // If `signInResult.isSignedIn` is true, the next step
17 // is 'done', and the user is now signed in.
18 }
19 }
20}

Confirm signin 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.

1func confirmSignIn(newPasswordFromUser: String) {
2 Amplify.Auth.confirmSignIn(challengeResponse: newPasswordFromUser) { result in
3 switch result {
4 case .success(let signInResult):
5 if signInResult.isSignedIn {
6 print("Confirm sign in succeeded. The user is signed in.")
7 } else {
8 print("Confirm sign in succeeded.")
9 print("Next step: \(signInResult.nextStep)")
10 // Switch on the next step to take appropriate actions.
11 // If `signInResult.isSignedIn` is true, the next step
12 // is 'done', and the user is now signed in.
13 }
14 case .failure(let error):
15 print("Confirm sign in failed \(error)")
16 }
17 }
18}
1func confirmSignIn(newPasswordFromUser: String) -> AnyCancellable {
2 Amplify.Auth.confirmSignIn(challengeResponse: newPasswordFromUser)
3 .resultPublisher
4 .sink {
5 if case let .failure(authError) = $0 {
6 print("Confirm sign in failed \(authError)")
7 }
8 }
9 receiveValue: { signInResult in
10 if signInResult.isSignedIn {
11 print("Confirm sign in succeeded. The user is signed in.")
12 } else {
13 print("Confirm sign in succeeded.")
14 print("Next step: \(signInResult.nextStep)")
15 // Switch on the next step to take appropriate actions.
16 // If `signInResult.isSignedIn` is true, the next step
17 // is 'done', and the user is now signed in.
18 }
19 }
20}

Reset password

If you receive resetPassword, authentication flow could not proceed without resetting the password. The next step is to invoke resetPassword api and follow the reset password flow.

1func resetPassword(username: String) {
2 Amplify.Auth.resetPassword(for: username) { result in
3 switch result {
4 case .success(let resetPasswordResult):
5 print("Reset password succeeded."
6 print("Next step: \(resetPasswordResult.nextStep)")
7 case .failure(let error):
8 print("Reset password failed \(error)")
9 }
10 }
11}
1func resetPassword(username: String) -> AnyCancellable {
2 Amplify.Auth.resetPassword(for: username)
3 .resultPublisher
4 .sink {
5 if case let .failure(authError) = $0 {
6 print("Reset password failed \(authError)")
7 }
8 }
9 receiveValue: { resetPasswordResult in
10 print("Reset password succeeded."
11 print("Next step: \(resetPasswordResult.nextStep)")
12 }
13}

Confirm Signup

If you receive confirmSignUp as a next step, sign up could not proceed without confirming user information such as email or phone number. The next step is to invoke the confirmSignUp API and follow the confirm signup flow.

1func confirmSignUp(for username: String, with confirmationCode: String) {
2 Amplify.Auth.confirmSignUp(for: username, confirmationCode: confirmationCode) { result in
3 switch result {
4 case .success:
5 print("Confirm signUp succeeded")
6 case .failure(let error):
7 print("An error occurred while confirming sign up \(error)")
8 }
9 }
10}
1func confirmSignUp(for username: String, with confirmationCode: String) -> AnyCancellable {
2 Amplify.Auth.confirmSignUp(for: username, confirmationCode: confirmationCode)
3 .resultPublisher
4 .sink {
5 if case let .failure(authError) = $0 {
6 print("An error occurred while confirming sign up \(authError)")
7 }
8 }
9 receiveValue: { _ in
10 print("Confirm signUp succeeded")
11 }
12}

Done

Signin flow is complete when you get done. This 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.