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

Page updated May 17, 2024

Custom auth flows

The Auth category can be configured to perform a custom authentication flow defined by you. The following guide shows how to setup a simple passwordless authentication flow.

Prerequisites

An application with Amplify libraries integrated and a minimum target of any of the following:

  • iOS 13.0, using Xcode 14.1 or later.
  • macOS 10.15, using Xcode 14.1 or later.
  • tvOS 13.0, using Xcode 14.3 or later.
  • watchOS 9.0, using Xcode 14.3 or later.
  • visionOS 1.0, using Xcode 15 beta 2 or later. (Preview support - see below for more details.)

For a full example, please follow the project setup walkthrough.

visionOS support is currently in preview and can be used by targeting the visionos-preview branch. As new Xcode 15 beta versions are released, the branch will be updated with any necessary fixes on a best effort basis.

For more information on how to use the visionos-preview branch, see Platform Support.

To use Auth in a macOS project, you'll need to enable the Keychain Sharing capability. In Xcode, navigate to your application target > Signing & Capabilities > + Capability, then select Keychain Sharing.

This capability is required because Auth uses the Data Protection Keychain on macOS as a platform best practice. See TN3137: macOS keychain APIs and implementations for more information on how Keychain works on macOS and the Keychain Sharing entitlement.

For more information on adding capabilities to your application, see Xcode Capabilities.

Configure Auth

The custom auth flow can be configured manually.

Sign in a user

Implement a UI to get the username from the user. After the user enters the username you can start the sign in flow by calling the following method:

func signIn(username: String) async {
do {
let options = AWSAuthSignInOptions(authFlowType: .customWithoutSRP)
let signInResult = try await Amplify.Auth.signIn(username: username,
options: .init(pluginOptions: options))
if case .confirmSignInWithCustomChallenge(_) = signInResult.nextStep {
// Ask the user to enter the custom challenge.
} else {
print("Sign in succeeded")
}
} catch let error as AuthError {
print("Sign in failed \(error)")
} catch {
print("Unexpected error: \(error)")
}
}
func signIn(username: String) -> AnyCancellable {
Amplify.Publisher.create {
let options = AWSAuthSignInOptions(authFlowType: .customWithoutSRP)
try await Amplify.Auth.signIn(username: username,
options: .init(pluginOptions: options))
}.sink {
if case let .failure(authError) = $0 {
print("Sign in failed \(authError)")
}
}
receiveValue: { result in
if case .confirmSignInWithCustomChallenge(_) = result.nextStep {
// Ask the user to enter the custom challenge.
} else {
print("Sign in succeeded")
}
}
}

Since this is a custom authentication flow with a challenge, the result of the signin process has a next step .confirmSignInWithCustomChallenge. Implement a UI to allow the user to enter the custom challenge.

Confirm sign in with custom challenge

To get a custom challenge from the user, create an appropriate UI for the user to submit the required value, and pass that value into the confirmSignin() API.

func customChallenge(response: String) async {
do {
_ = try await Amplify.Auth.confirmSignIn(challengeResponse: response)
print("Confirm sign in succeeded")
} catch let error as AuthError {
print("Confirm sign in failed \(error)")
} catch {
print("Unexpected error: \(error)")
}
}
func customChallenge(response: String) -> AnyCancellable {
Amplify.Publisher.create {
try await Amplify.Auth.confirmSignIn(challengeResponse: response)
}.sink {
if case let .failure(authError) = $0 {
print("Confirm sign in failed \(authError)")
}
}
receiveValue: { _ in
print("Confirm sign in succeeded")
}
}

You will know the sign in flow is complete if you see the following in your console window:

Confirm sign in succeeded

Lambda Trigger Setup

AWS Amplify now supports creating functions as part of its new backend experience. For more information on the Functions and how to start with them check out Functions documentation. In addition, more information on available triggers can be found in the Cognito documentation.

Custom Auth Flow with Secure Remote Password (SRP)

Cognito User Pool allows to start the custom authentication flow with SRP as the first step. If you would like to use this flow, setup Define Auth Lambda trigger to handle SRP_A as the first challenge as shown below:

exports.handler = (event, context) => {
if (event.request.session.length == 1 &&
event.request.session[0].challengeName == 'SRP_A') {
event.response.issueTokens = false;
event.response.failAuthentication = false;
event.response.challengeName = 'PASSWORD_VERIFIER';
} else if (event.request.session.length == 2 &&
event.request.session[1].challengeName == 'PASSWORD_VERIFIER' &&
event.request.session[1].challengeResult == true) {
event.response.issueTokens = false;
event.response.failAuthentication = false;
event.response.challengeName = 'CUSTOM_CHALLENGE';
} else if (event.request.session.length == 3 &&
event.request.session[2].challengeName == 'CUSTOM_CHALLENGE' &&
event.request.session[2].challengeResult == true) {
event.response.issueTokens = true;
event.response.failAuthentication = false;
} else {
event.response.issueTokens = false;
event.response.failAuthentication = true;
}
context.done(null, event);
};

If your lambda is setup to start with SRP as the first step, make sure to initiate the signIn process with customWithSRP as the authentication flow:

let options = AWSAuthSignInOptions(
authFlowType: .customWithSRP)
let signInResult = try await Amplify.Auth.signIn(
username: username,
password: password,
options: .init(pluginOptions: options))