diff --git a/src/pages/[platform]/build-a-backend/auth/concepts/passwordless/index.mdx b/src/pages/[platform]/build-a-backend/auth/concepts/passwordless/index.mdx
index 6fc490e7ca0..5b0aba2f516 100644
--- a/src/pages/[platform]/build-a-backend/auth/concepts/passwordless/index.mdx
+++ b/src/pages/[platform]/build-a-backend/auth/concepts/passwordless/index.mdx
@@ -42,7 +42,7 @@ Passwordless authentication removes the security risks and user friction associa
-Learn how to implement passwordless sign-in flows by [overriding the Cognito UserPool to enable the sign-in methods below](/[platform]/build-a-backend/auth/modify-resources-with-cdk/#override-cognito-userpool-to-enable-passwordless-sign-in-methods).
+Learn how to enable passwordless sign-in flows by [overriding the Cognito UserPool to enable the sign-in methods below](/[platform]/build-a-backend/auth/modify-resources-with-cdk/#override-cognito-userpool-to-enable-passwordless-sign-in-methods).
{/* need a section about what a "preferred" factor is */}
@@ -131,7 +131,17 @@ WebAuthn uses biometrics or security keys for authentication, leveraging device-
-{/* */}
+You can read more about how passkeys work in the [Android developer docs](https://developer.android.com/design/ui/mobile/guides/patterns/passkeys).
+
+
+Registering a passkey is supported on Android 9 (API level 28) and above.
+
+
+Using passkeys with Amplify requires following these steps:
+
+1. Deploy a Digital Asset Links file to your website granting the `get_login_creds` permission to your application. See the [Credential Manager documentation](https://developer.android.com/identity/sign-in/credential-manager#add-support-dal) for more details about this file.
+1. [Configure your Amazon Cognito user pool](/[platform]/build-a-backend/auth/modify-resources-with-cdk/#override-cognito-userpool-to-enable-passwordless-sign-in-methods) with `WEB_AUTHN` as an allowed first factor, and specify your website domain as the `WebAuthnRelyingPartyID`.
+1. Use the Amplify Android APIs to first [register a passkey](/[platform]/build-a-backend/auth/manage-users/manage-webauthn-credentials/#associate-webauthn-credentials) and then to [sign in with WebAuthn](/[platform]/build-a-backend/auth/connect-your-frontend/sign-in/#webauthn-passkeys).
@@ -144,6 +154,6 @@ WebAuthn uses biometrics or security keys for authentication, leveraging device-
### Managing credentials
-{/* quick blurb then segue over to "manage WebAuthn credentials" page */}
+Passwordless authentication with WebAuthn requires associating one or more credentials with the user's Amazon Cognito account. Amplify provides APIs that integrate with each platform's local authenticator to easily create, view, and delete these credential associations.
[Learn more about managing WebAuthn credentials](/[platform]/build-a-backend/auth/manage-users/manage-webauthn-credentials).
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 d8efe532254..5f97f5e3e9c 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
@@ -1140,7 +1140,7 @@ func socialSignInWithWebUI() -> AnyCancellable {
## Sign in with passwordless methods
-Your application's users can also sign in using passwordless methods. To learn more, visit the [concepts page for passwordless](/[platform]/build-a-backend/auth/concepts/passwordless/).
+Your application's users can also sign in using passwordless methods. To learn more, including how to setup the various passwordless authentication flows, visit the [concepts page for passwordless](/[platform]/build-a-backend/auth/concepts/passwordless/).
### SMS OTP
@@ -1175,17 +1175,24 @@ if (signInNextStep.signInStep === 'CONFIRM_SIGN_IN_WITH_SMS_CODE') {
-To request an OTP code via SMS for authentication, you pass the `challengeResponse` for `AuthFactorType.SMS_OTP` to the `confirmSignIn` API.
+Pass `SMS_OTP` as the `preferredFirstFactor` when calling the `signIn` API in order to initiate a passwordless authentication flow with SMS OTP.
-Amplify will respond appropriately to Cognito and return the challenge as the sign in next step: `CONFIRM_SIGN_IN_WITH_OTP_CODE`. You will call `confirmSignIn` again, this time with the OTP that your user provides.
```java
-// First confirm the challenge type
-Amplify.Auth.confirmSignIn(
- AuthFactorType.SMS_OTP.getChallengeResponse(),
+// Use options to specify the preferred first factor
+AWSCognitoAuthSignInOptions options = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .preferredFirstFactor(AuthFactorType.SMS_OTP) // Sign in using SMS OTP
+ .build();
+
+// Sign in the user
+Amplify.Auth.signIn(
+ username,
+ null, // no password
+ options,
result -> {
if (result.getNextStep().getSignInStep() == AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP) {
// Show UI to collect OTP
@@ -1208,28 +1215,30 @@ Amplify.Auth.confirmSignIn(
```kotlin
-// First confirm the challenge type
-Amplify.Auth.confirmSignIn(
- AuthFactorType.SMS_OTP.challengeResponse,
- { result ->
+// Use options to specify the preferred first factor
+val options = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .preferredFirstFactor(AuthFactorType.SMS_OTP) // Sign in using SMS OTP
+ .build()
+
+// Sign in the user
+Amplify.Auth.signIn(
+ username,
+ null, // no password
+ options,
+ { result: AuthSignInResult ->
if (result.nextStep.signInStep == AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP) {
// Show UI to collect OTP
}
},
- { error ->
- Log.e("AuthQuickstart", "Failed to sign in", error)
- }
+ { error: AuthException -> Log.e("AuthQuickstart", error.toString()) }
)
// Then pass that OTP into the confirmSignIn API
Amplify.Auth.confirmSignIn(
"123456",
- { result ->
- // result.nextStep.signInStep should be "DONE" now
- },
- { error ->
- Log.e("AuthQuickstart", "Failed to sign in", error)
- }
+ { result: AuthSignInResult? -> },
+ { error: AuthException -> Log.e("AuthQuickstart", error.toString()) }
)
```
@@ -1237,32 +1246,52 @@ Amplify.Auth.confirmSignIn(
```kotlin
-// First confirm the challenge type
-var result = Amplify.Auth.confirmSignIn(AuthFactorType.SMS_OTP.challengeResponse)
+// Use options to specify the preferred first factor
+val options = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .preferredFirstFactor(AuthFactorType.SMS_OTP) // Sign in using SMS OTP
+ .build()
+
+// Sign in the user
+val result = Amplify.Auth.signIn(
+ username = username,
+ password = null,
+ options = options
+)
if (result.nextStep.signInStep == AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP) {
// Show UI to collect OTP
}
// Then pass that OTP into the confirmSignIn API
-result = Amplify.Auth.confirmSignIn("123456")
-
-// result.nextStep.signInStep should be "DONE" now
+val confirmResult = Amplify.Auth.confirmSignIn(
+ challengeResponse = "123456"
+)
+// confirmResult.nextStep.signInStep should be "DONE"
```
```java
-// First confirm the challenge type
-RxAmplify.Auth.confirmSignIn(AuthFactorType.SMS_OTP.getChallengeResponse())
- .subscribe(
- result -> {
- if (result.getNextStep().getSignInStep() == AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP) {
- // Show UI to collect OTP
- }
- },
- error -> Log.e("AuthQuickstart", error.toString())
- );
+// Use options to specify the preferred first factor
+AWSCognitoAuthSignInOptions options = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .preferredFirstFactor(AuthFactorType.SMS_OTP) // Sign in using SMS OTP
+ .build();
+
+// Sign in the user
+RxAmplify.Auth.signIn(
+ username,
+ null, // no password
+ options
+).subscribe(
+ result -> {
+ if (result.getNextStep().getSignInStep() == AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP) {
+ // Show UI to collect OTP
+ }
+ },
+ error -> Log.e("AuthQuickstart", error.toString())
+)
// Then pass that OTP into the confirmSignIn API
RxAmplify.Auth.confirmSignIn("123456")
@@ -1281,6 +1310,8 @@ RxAmplify.Auth.confirmSignIn("123456")
+Pass `smsOTP` as the `preferredFirstFactor` when calling the `signIn` API in order to initiate a passwordless authentication flow with SMS OTP.
+
@@ -1389,17 +1420,23 @@ if (signInNextStep.signInStep === 'CONFIRM_SIGN_IN_WITH_EMAIL_CODE') {
-To request an OTP code via email for authentication, you pass the `challengeResponse` for `AuthFactorType.EMAIL_OTP` to the `confirmSignIn` API.
-
-Amplify will respond appropriately to Cognito and return the challenge as the sign in next step: `CONFIRM_SIGN_IN_WITH_OTP_CODE`. You will call `confirmSignIn` again, this time with the OTP that your user provides.
+Pass `EMAIL_OTP` as the `preferredFirstFactor` when calling the `signIn` API in order to initiate a passwordless authentication flow with Email OTP.
```java
-// First confirm the challenge type
-Amplify.Auth.confirmSignIn(
- AuthFactorType.EMAIL_OTP.getChallengeResponse(),
+// Use options to specify the preferred first factor
+AWSCognitoAuthSignInOptions options = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .preferredFirstFactor(AuthFactorType.EMAIL_OTP) // Sign in using Email OTP
+ .build();
+
+// Sign in the user
+Amplify.Auth.signIn(
+ username,
+ null, // no password
+ options,
result -> {
if (result.getNextStep().getSignInStep() == AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP) {
// Show UI to collect OTP
@@ -1422,28 +1459,30 @@ Amplify.Auth.confirmSignIn(
```kotlin
-// First confirm the challenge type
-Amplify.Auth.confirmSignIn(
- AuthFactorType.EMAIL_OTP.challengeResponse,
- { result ->
+// Use options to specify the preferred first factor
+val options = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .preferredFirstFactor(AuthFactorType.EMAIL_OTP) // Sign in using Email OTP
+ .build()
+
+// Sign in the user
+Amplify.Auth.signIn(
+ username,
+ null, // no password
+ options,
+ { result: AuthSignInResult ->
if (result.nextStep.signInStep == AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP) {
// Show UI to collect OTP
}
},
- { error ->
- Log.e("AuthQuickstart", "Failed to sign in", error)
- }
+ { error: AuthException -> Log.e("AuthQuickstart", error.toString()) }
)
// Then pass that OTP into the confirmSignIn API
Amplify.Auth.confirmSignIn(
"123456",
- { result ->
- // result.nextStep.signInStep should be "DONE" now
- },
- { error ->
- Log.e("AuthQuickstart", "Failed to sign in", error)
- }
+ { result: AuthSignInResult? -> },
+ { error: AuthException -> Log.e("AuthQuickstart", error.toString()) }
)
```
@@ -1451,32 +1490,52 @@ Amplify.Auth.confirmSignIn(
```kotlin
-// First confirm the challenge type
-var result = Amplify.Auth.confirmSignIn(AuthFactorType.EMAIL_OTP.challengeResponse)
+// Use options to specify the preferred first factor
+val options = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .preferredFirstFactor(AuthFactorType.EMAIL_OTP) // Sign in using Email OTP
+ .build()
+
+// Sign in the user
+val result = Amplify.Auth.signIn(
+ username = username,
+ password = null,
+ options = options
+)
if (result.nextStep.signInStep == AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP) {
// Show UI to collect OTP
}
// Then pass that OTP into the confirmSignIn API
-result = Amplify.Auth.confirmSignIn("123456")
-
-// result.nextStep.signInStep should be "DONE" now
+val confirmResult = Amplify.Auth.confirmSignIn(
+ challengeResponse = "123456"
+)
+// confirmResult.nextStep.signInStep should be "DONE"
```
```java
-// First confirm the challenge type
-RxAmplify.Auth.confirmSignIn(AuthFactorType.EMAIL_OTP.getChallengeResponse())
- .subscribe(
- result -> {
- if (result.getNextStep().getSignInStep() == AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP) {
- // Show UI to collect OTP
- }
- },
- error -> Log.e("AuthQuickstart", error.toString())
- );
+// Use options to specify the preferred first factor
+AWSCognitoAuthSignInOptions options = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .preferredFirstFactor(AuthFactorType.EMAIL_OTP) // Sign in using Email OTP
+ .build();
+
+// Sign in the user
+RxAmplify.Auth.signIn(
+ username,
+ null, // no password
+ options
+).subscribe(
+ result -> {
+ if (result.getNextStep().getSignInStep() == AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP) {
+ // Show UI to collect OTP
+ }
+ },
+ error -> Log.e("AuthQuickstart", error.toString())
+)
// Then pass that OTP into the confirmSignIn API
RxAmplify.Auth.confirmSignIn("123456")
@@ -1491,11 +1550,12 @@ RxAmplify.Auth.confirmSignIn("123456")
-
-
+Pass `emailOTP` as the `preferredFirstFactor` when calling the `signIn` API in order to initiate a passwordless authentication flow with Email OTP.
+
+
@@ -1597,28 +1657,35 @@ if (signInNextStep.signInStep === 'DONE') {
-To sign in with WebAuthn, you pass the `challengeResponse` for `AuthFactorType.WEB_AUTHN` to the `confirmSignIn` API. Amplify will invoke Android's Credential Manager to retrieve a PassKey, and the user will be shown a system UI to authorize the PassKey access. This flow
-completes without any additional interaction from your application, so there is only one `confirmSignIn` call needed for WebAuthn.
+Pass `WEB_AUTHN` as the `preferredFirstFactor` in order to initiate the passwordless authentication flow using a WebAuthn credential. This flow
+completes without any additional interaction from your application, so there is only one `Amplify.Auth` call needed for WebAuthn.
+
+
+The user must have previously associated a credential to use this auth factor. To learn more, visit the [manage WebAuthn credentials page](/[platform]/build-a-backend/auth/manage-users/manage-webauthn-credentials/).
+
-Amplify requires an `Activity` reference to attach the PassKey UI to your Application's [Task](https://developer.android.com/guide/components/activities/tasks-and-back-stack) when using WebAuthn - if an `Activity` is not supplied then the UI will appear in a separate Task. For this reason, we strongly recommend passing the `callingActivity` option to both the `signIn` and `confirmSignIn` APIs if your application uses the `USER_AUTH` flow.
+Amplify requires an `Activity` reference to attach the PassKey UI to your Application's [Task](https://developer.android.com/guide/components/activities/tasks-and-back-stack) when using WebAuthn - if an `Activity` is not supplied then the UI will appear in a separate Task. For this reason, we strongly recommend always passing the `callingActivity` option to both the `signIn` and `confirmSignIn` APIs if your application allows users to sign in with passkeys.
```java
-// Pass the calling activity
-AuthSignInOptions options = AWSCognitoAuthConfirmSignInOptions.builder()
- .callingActivity(activity)
+// Use options to specify the preferred first factor
+AWSCognitoAuthSignInOptions options = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .callingActivity(callingActivity)
+ .preferredFirstFactor(AuthFactorType.WEB_AUTHN) // Sign in using WebAuthn
.build();
-// Confirm WebAuthn as the challenge type
-Amplify.Auth.confirmSignIn(
- AuthFactorType.WEB_AUTHN.getChallengeResponse(),
+// Sign in the user
+Amplify.Auth.signIn(
+ username,
+ null, // no password
options,
result -> Log.i("AuthQuickStart", "Next sign in step: " + result.getNextStep()),
- error -> Log.e("AuthQuickstart", "Failed to sign in", error)
+ error -> Log.e("AuthQuickstart", error.toString())
);
```
@@ -1626,17 +1693,20 @@ Amplify.Auth.confirmSignIn(
```kotlin
-// Pass the calling activity
-val options = AWSCognitoAuthConfirmSignInOptions.builder()
- .callingActivity(activity)
+// Use options to specify the preferred first factor
+val options = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .callingActivity(callingActivity)
+ .preferredFirstFactor(AuthFactorType.WEB_AUTHN) // Sign in using WebAuthn
.build()
-// Confirm WebAuthn as the challenge type
-Amplify.Auth.confirmSignIn(
- AuthFactorType.WEB_AUTHN.name,
+// Sign in the user
+Amplify.Auth.signIn(
+ username,
+ null, // no password
options,
- { result -> Log.i("AuthQuickStart", "Next sign in step: ${result.nextStep}") },
- { error -> Log.e("AuthQuickstart", "Failed to sign in", error) }
+ { result: AuthSignInResult -> Log.i("AuthQuickStart", "Next sign in step: ${result.nextStep}") },
+ { error: AuthException -> Log.e("AuthQuickStart", error.toString()) }
)
```
@@ -1644,38 +1714,44 @@ Amplify.Auth.confirmSignIn(
```kotlin
-// Pass the calling activity
-val options = AWSCognitoAuthConfirmSignInOptions.builder()
- .callingActivity(activity)
+// Use options to specify the preferred first factor
+val options = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .callingActivity(callingActivity)
+ .preferredFirstFactor(AuthFactorType.WEB_AUTHN) // Sign in using WebAuthn
.build()
-try {
- // Confirm WebAuthn as the challenge type
- var result = Amplify.Auth.confirmSignIn(
- challengeResponse = AuthFactorType.WEB_AUTHN.challengeResponse,
- options = options
- )
- Log.i("AuthQuickStart", "Next sign in step: ${result.nextStep}")
-} catch (error: AuthException) {
- Log.e("AuthQuickstart", "Failed to sign in", error)
-}
+// Sign in the user
+val result = Amplify.Auth.signIn(
+ username = username,
+ password = null,
+ options = options
+)
+
+// result.nextStep.signInStep should be "DONE" if use granted access to the passkey
+// NOTE: `signIn` will throw a UserCancelledException if user dismissed the passkey UI
```
```java
-// Pass the calling activity
-AuthSignInOptions options = AWSCognitoAuthConfirmSignInOptions.builder()
- .callingActivity(activity)
+// Use options to specify the preferred first factor
+AWSCognitoAuthSignInOptions options = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .callingActivity(callingActivity)
+ .preferredFirstFactor(AuthFactorType.WEB_AUTHN) // Sign in using WebAuthn
.build();
-// Confirm WebAuthn as the challenge type
-RxAmplify.Auth.confirmSignIn(AuthFactorType.WEB_AUTHN.getChallengeResponse(), options)
- .subscribe(
- result -> Log.i("AuthQuickStart", "Next sign in step: " + result.getNextStep()),
- error -> Log.e("AuthQuickstart", "Failed to sign in", error)
- );
+// Sign in the user
+RxAmplify.Auth.signIn(
+ username,
+ null, // no password
+ options
+).subscribe(
+ result -> Log.i("AuthQuickStart", "Next sign in step: " + result.getNextStep()),
+ error -> Log.e("AuthQuickstart", error.toString())
+)
```
@@ -1683,33 +1759,94 @@ RxAmplify.Auth.confirmSignIn(AuthFactorType.WEB_AUTHN.getChallengeResponse(), op
Using WebAuthn sign in may result in a number of possible exception types.
-- `UserCancelledException` - If the user declines to authorize access to the PassKey in the system UI. You can retry the WebAuthn flow by invoking `confirmSignIn` again, or restart the `signIn` process to select a different `AuthFactorType`.
+- `UserCancelledException` - If the user declines to authorize access to the passkey in the system UI. You can retry the WebAuthn flow by invoking `confirmSignIn` again, or restart the `signIn` process to select a different `AuthFactorType`.
- `WebAuthnNotEnabledException` - This indicates WebAuthn is not enabled in your user pool.
- `WebAuthnNotSupportedException` - This indicates WebAuthn is not supported on the user's device.
- `WebAuthnRpMismatchException` - This indicates there is a problem with the `assetlinks.json` file deployed to your relying party.
- `WebAuthnFailedException` - This exception is used for other errors that may occur with WebAuthn. Inspect the `cause` to determine the best course of action.
-
{/* */}
-{/* */}
+Pass `webAuthn` as the `preferredFirstFactor` in order to initiate the passwordless authentication flow using a WebAuthn credential.
-
+
+
-
+```swift
+// sign in with `webAuthn` as preferred factor
+func signIn(username: String) async {
+ do {
+ let authFactorType : AuthFactorType
+ if #available(iOS 17.4, *) {
+ authFactorType = .webAuthn
+ } else {
+ // Fallback on earlier versions
+ authFactorType = .passwordSRP
+ }
-### Password
+ let signInResult = try await Amplify.Auth.signIn(
+ username: username,
+ options: .init(pluginOptions: AWSAuthSignInOptions(
+ authFlowType: .userAuth(
+ preferredFirstFactor: authFactorType))))
+ print("Sign in succeeded. Next step: \(signInResult.nextStep)")
+ } catch let error as AuthError {
+ print("Sign in failed \(error)")
+ } catch {
+ print("Unexpected error: \(error)")
+ }
+}
-Pass either `PASSWORD` or `PASSWORD_SRP` as the `preferredChallenge` in order to initiate a traditional password based authentication flow.
+```
+
+
+
+
+```swift
+// sign in with `webAuthn` as preferred factor
+func signIn(username: String) async {
+ Amplify.Publisher.create {
+ let authFactorType : AuthFactorType
+ if #available(iOS 17.4, *) {
+ authFactorType = .webAuthn
+ } else {
+ // Fallback on earlier versions
+ authFactorType = .passwordSRP
+ }
+
+ try await Amplify.Auth.signIn(
+ username: username,
+ options: .init(pluginOptions: AWSAuthSignInOptions(
+ authFlowType: .userAuth(
+ preferredFirstFactor: authFactorType))))
+ }.sink {
+ if case let .failure(authError) = $0 {
+ print("Sign in failed \(authError)")
+ }
+ }
+ receiveValue: { signInResult in
+ print("Sign in succeeded. Next step: \(signInResult.nextStep)")
+ }
+}
+```
+
+
+
+
+### Password
+
+
+Pass either `PASSWORD` or `PASSWORD_SRP` as the `preferredChallenge` in order to initiate a traditional password based authentication flow.
+
```ts
const { nextStep: signInNextStep } = await signIn({
username: 'hello@example.com',
@@ -1728,27 +1865,25 @@ if (confirmSignInNextStep.signInStep === 'DONE') {
+Pass either `PASSWORD` or `PASSWORD_SRP` as the `preferredFirstFactor` in order to initiate a traditional password based authentication flow.
+
+
```java
-// First confirm the challenge type
-Amplify.Auth.confirmSignIn(
- AuthFactorType.PASSWORD.getChallengeResponse(), // or PASSWORD_SRP
- result -> {
- if (result.getNextStep().getSignInStep() == AuthSignInStep.CONFIRM_SIGN_IN_WITH_PASSWORD) {
- // Show UI to collect password
- }
- },
- error -> Log.e("AuthQuickstart", error.toString())
-);
+// Use options to specify the preferred first factor
+AWSCognitoAuthSignInOptions options = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .preferredFirstFactor(AuthFactorType.PASSWORD) // Sign in using Password
+ .build();
-// Then pass that password into the confirmSignIn API
-Amplify.Auth.confirmSignIn(
- "password",
- result -> {
- // result.getNextStep().getSignInStep() should be "DONE" now
- },
+// Sign in the user
+Amplify.Auth.signIn(
+ username,
+ password, // supply the password if preferredFirstFactor is PASSWORD or PASSWORD_SRP
+ options,
+ result -> Log.i("AuthQuickStart", "Next sign in step: " + result.getNextStep()),
error -> Log.e("AuthQuickstart", error.toString())
);
```
@@ -1757,28 +1892,19 @@ Amplify.Auth.confirmSignIn(
```kotlin
-// First confirm the challenge type
-Amplify.Auth.confirmSignIn(
- AuthFactorType.PASSWORD.challengeResponse, // or PASSWORD_SRP
- { result ->
- if (result.nextStep.signInStep == AuthSignInStep.CONFIRM_SIGN_IN_WITH_PASSWORD) {
- // Show UI to collect password
- }
- },
- { error ->
- Log.e("AuthQuickstart", "Failed to sign in", error)
- }
-)
+// Use options to specify the preferred first factor
+val options = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .preferredFirstFactor(AuthFactorType.PASSWORD) // Sign in using Password
+ .build()
-// Then pass that password into the confirmSignIn API
-Amplify.Auth.confirmSignIn(
- "password",
- { result ->
- // result.nextStep.signInStep should be "DONE" now
- },
- { error ->
- Log.e("AuthQuickstart", "Failed to sign in", error)
- }
+// Sign in the user
+Amplify.Auth.signIn(
+ username,
+ password, // supply the password if preferredFirstFactor is PASSWORD or PASSWORD_SRP
+ options,
+ { result: AuthSignInResult -> Log.i("AuthQuickStart", "Next sign in step: ${result.nextStep}") },
+ { error: AuthException -> Log.e("AuthQuickstart", error.toString()) }
)
```
@@ -1786,57 +1912,109 @@ Amplify.Auth.confirmSignIn(
```kotlin
-// First confirm the challenge type
-var result = Amplify.Auth.confirmSignIn(AuthFactorType.PASSWORD.challengeResponse) // or PASSWORD_SRP
-if (result.nextStep.signInStep == AuthSignInStep.CONFIRM_SIGN_IN_WITH_PASSWORD) {
- // Show UI to collect password
-}
+// Use options to specify the preferred first factor
+val options = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .preferredFirstFactor(AuthFactorType.PASSWORD) // Sign in using Password
+ .build()
-// Then pass that password into the confirmSignIn API
-result = Amplify.Auth.confirmSignIn("password")
+// Sign in the user
+val result = Amplify.Auth.signIn(
+ username = username,
+ password = password, // supply the password if preferredFirstFactor is PASSWORD or PASSWORD_SRP
+ options = options
+)
-// result.nextStep.signInStep should be "DONE" now
+// result.nextStep.signInStep should be "DONE"
```
```java
-// First confirm the challenge type
-RxAmplify.Auth.confirmSignIn(AuthFactorType.PASSWORD.getChallengeResponse()) // or PASSWORD_SRP
- .subscribe(
- result -> {
- if (result.getNextStep().getSignInStep() == AuthSignInStep.CONFIRM_SIGN_IN_WITH_PASSWORD) {
- // Show UI to collect password
- }
- },
- error -> Log.e("AuthQuickstart", error.toString())
- );
+// Use options to specify the preferred first factor
+AWSCognitoAuthSignInOptions options = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .preferredFirstFactor(AuthFactorType.Password) // Sign in using Password
+ .build();
-// Then pass that password into the confirmSignIn API
-RxAmplify.Auth.confirmSignIn("password")
- .subscribe(
- result -> {
- // result.getNextStep().getSignInStep() should be "DONE" now
- },
- error -> Log.e("AuthQuickstart", error.toString())
- );
+// Sign in the user
+RxAmplify.Auth.signIn(
+ username,
+ password, // supply the password if preferredFirstFactor is PASSWORD or PASSWORD_SRP
+ options
+).subscribe(
+ result -> Log.i("AuthQuickStart", "Next sign in step: " + result.getNextStep()),
+ error -> Log.e("AuthQuickstart", error.toString())
+)
```
+
-
-### First Factor Selection
+Pass either `password` or `passwordSRP` as the `preferredFirstFactor` in order to initiate a traditional password based authentication flow.
-Omit the `preferredChallenge` parameter to discover what first factors are available for a given user.
+
+
+
+```swift
+// sign in with `password` as preferred factor
+func signIn(username: String) async {
+ do {
+ let pluginOptions = AWSAuthSignInOptions(
+ authFlowType: .userAuth(preferredFirstFactor: .password))
+ let signInResult = try await Amplify.Auth.signIn(
+ username: username,
+ options: .init(pluginOptions: pluginOptions))
+ print("Sign in succeeded. Next step: \(signInResult.nextStep)")
+ } catch let error as AuthError {
+ print("Sign in failed \(error)")
+ } catch {
+ print("Unexpected error: \(error)")
+ }
+}
+
+```
+
+
+
+
+```swift
+// sign in with `password` as preferred factor
+func signIn(username: String) async {
+ Amplify.Publisher.create {
+ let pluginOptions = AWSAuthSignInOptions(
+ authFlowType: .userAuth(preferredFirstFactor: .password))
+ try await Amplify.Auth.signIn(
+ username: username,
+ options: .init(pluginOptions: pluginOptions))
+ }.sink {
+ if case let .failure(authError) = $0 {
+ print("Sign in failed \(authError)")
+ }
+ }
+ receiveValue: { signInResult in
+ print("Sign in succeeded. Next step: \(signInResult.nextStep)")
+ }
+}
+```
+
+
-The `confirmSignIn` API can then be used to select a challenge and initiate the associated authentication flow.
+### First Factor Selection
+
+
+Omit the `preferredChallenge` parameter to discover which first factors are available for a given user. This is useful to allow
+users to choose how they would like to sign in.
+
+The `confirmSignIn` API can then be used to select a challenge and initiate the associated authentication flow.
+
```ts
const { nextStep: signInNextStep } = await signIn({
username: '+15551234567',
@@ -1861,17 +2039,23 @@ if (
+
+Omit the `preferredFirstFactor` option to discover which first factors are available for a given user. This is useful to allow
+users to choose how they would like to sign in.
+
+The `confirmSignIn` API can then be used to select a challenge and initiate the associated authentication flow.
+
```java
-// Retrieve the authentication factors by calling .availableFactors
-AWSCognitoAuthSignInOptions options =
- AWSCognitoAuthSignInOptions
- .builder()
- .authFlowType(AuthFlowType.USER_AUTH)
- .callingActivity(callingActivity)
- .build();
+// Omit preferredFirstFactor. If the user has more than one factor available then
+// the next step will be CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION.
+AuthSignInOptions options = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .build();
+
+// Step 1: Sign in the user
Amplify.Auth.signIn(
"hello@example.com",
null,
@@ -1886,32 +2070,80 @@ Amplify.Auth.signIn(
},
error -> Log.e("AuthQuickstart", error.toString())
);
+
+// Step 2: Select SMS OTP for sign in
+Amplify.Auth.confirmSignIn(
+ AuthFactorType.SMS_OTP.getChallengeResponse(),
+ result -> {
+ if (result.getNextStep().getSignInStep() == AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP) {
+ Log.i(
+ "AuthQuickStart",
+ "OTP code sent to " + result.getNextStep().getCodeDeliveryDetails()
+ )
+ // Show UI to collect OTP
+ }
+ },
+ error -> Log.e("AuthQuickstart", error.toString())
+);
+
+// Step 3: Then pass that OTP into the confirmSignIn API
+Amplify.Auth.confirmSignIn(
+ "123456",
+ result -> {
+ // result.getNextStep().getSignInStep() should be "DONE" now
+ },
+ error -> Log.e("AuthQuickstart", error.toString())
+);
```
```kotlin
-// Retrieve the authentication factors by calling .availableFactors
-val options = AWSCognitoAuthSignInOptions.builder()
+// Omit preferredFirstFactor. If the user has more than one factor available then
+// the next step will be CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION.
+val options: AuthSignInOptions = AWSCognitoAuthSignInOptions.builder()
.authFlowType(AuthFlowType.USER_AUTH)
- .callingActivity(callingActivity)
.build()
+
+// Step 1: Sign in the user
Amplify.Auth.signIn(
"hello@example.com",
null,
options,
- { result ->
+ { result: AuthSignInResult ->
if (result.nextStep.signInStep == AuthSignInStep.CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION) {
Log.i(
"AuthQuickstart",
- "Available factors for this user: ${result.nextStep.availableFactors}"
+ "Available authentication factors for this user: ${result.nextStep.availableFactors}"
)
}
},
- { error ->
- Log.e("AuthQuickstart", "Failed to sign in", error)
- }
+ { error: AuthException -> Log.e("AuthQuickstart", error.toString()) }
+)
+
+// Step 2: Select SMS OTP for sign in
+Amplify.Auth.confirmSignIn(
+ AuthFactorType.SMS_OTP.getChallengeResponse(),
+ { result: AuthSignInResult ->
+ if (result.nextStep.signInStep == AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP) {
+ Log.i(
+ "AuthQuickStart",
+ "OTP code sent to ${result.nextStep.codeDeliveryDetails}"
+ )
+ // Show UI to collect OTP
+ }
+ },
+ { error: AuthException -> Log.e("AuthQuickstart", error.toString()) }
+)
+
+// Step 3: Then pass that OTP into the confirmSignIn API
+Amplify.Auth.confirmSignIn(
+ "123456",
+ { result: AuthSignInResult? ->
+ // result.nextStep.signInStep should be "DONE" now
+ },
+ { error: AuthException -> Log.e("AuthQuickstart", error.toString()) }
)
```
@@ -1919,58 +2151,210 @@ Amplify.Auth.signIn(
```kotlin
-try {
- // Retrieve the authentication factors by calling .availableFactors
- val options = AWSCognitoAuthSignInOptions.builder()
- .authFlowType(AuthFlowType.USER_AUTH)
- .callingActivity(callingActivity)
- .build()
- val result = Amplify.Auth.signIn(
- username = "hello@example.com",
- password = null,
- options = options
+// Omit preferredFirstFactor. If the user has more than one factor available then
+// the next step will be CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION.
+val options: AuthSignInOptions = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .build()
+
+// Step 1: Sign in the user
+val result = Amplify.Auth.signIn(
+ username = "hello@example.com",
+ password = null,
+ options = options
+)
+
+if (result.nextStep.signInStep == AuthSignInStep.CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION) {
+ Log.i(
+ "AuthQuickStart",
+ "Available authentication factors for this user: ${result.nextStep.availableFactors}"
)
- if (result.nextStep.signInStep == AuthSignInStep.CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION) {
- Log.i(
- "AuthQuickstart",
- "Available factors for this user: ${result.nextStep.availableFactors}"
- )
- }
-} catch (error: AuthException) {
- Log.e("AuthQuickstart", "Sign in failed", error)
}
+
+// Step 2: Select SMS OTP for sign in
+val selectFactorResult = Amplify.Auth.confirmSignIn(challengeResponse = AuthFactorType.SMS_OTP.challengeResponse)
+
+if (result.nextStep.signInStep == AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP) {
+ Log.i(
+ "AuthQuickStart",
+ "OTP code sent to ${result.nextStep.codeDeliveryDetails}"
+ )
+ // Show UI to collect OTP
+}
+
+// Step 3: Then pass that OTP into the confirmSignIn API
+val confirmResult = Amplify.Auth.confirmSignIn(challengeResponse = "123456")
+
+// confirmResult.nextStep.signInStep should be "DONE" now
```
```java
-// Retrieve the authentication factors by calling .availableFactors
-AWSCognitoAuthSignInOptions options =
- AWSCognitoAuthSignInOptions
- .builder()
- .authFlowType(AuthFlowType.USER_AUTH)
- .callingActivity(callingActivity)
- .build();
-RxAmplify.Auth.signIn("hello@example.com", null, options)
+// Omit preferredFirstFactor. If the user has more than one factor available then
+// the next step will be CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION.
+AWSCognitoAuthSignInOptions options = AWSCognitoAuthSignInOptions.builder()
+ .authFlowType(AuthFlowType.USER_AUTH)
+ .build();
+
+// Step 1: Sign in the user
+RxAmplify.Auth.signIn(
+ username,
+ null, // no password
+ options
+).subscribe(
+ result -> {
+ if (result.getNextStep().getSignInStep() == AuthSignInStep.CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION) {
+ Log.i(
+ "AuthQuickstart",
+ "Available authentication factors for this user: " + result.getNextStep().getAvailableFactors()
+ );
+ }
+ },
+ error -> Log.e("AuthQuickstart", error.toString())
+)
+
+// Step 2: Select SMS OTP for sign in
+RxAmplify.Auth.confirmSignIn(AuthFactorType.SMS_OTP.getChallengeResponse())
.subscribe(
result -> {
- if (result.getNextStep().getSignInStep() == AuthSignInStep.CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION) {
+ if (result.getNextStep().getSignInStep() == AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP) {
Log.i(
- "AuthQuickstart",
- "Available authentication factors for this user: " + result.getNextStep().getAvailableFactors()
- );
+ "AuthQuickStart",
+ "OTP code sent to " + result.getNextStep().getCodeDeliveryDetails()
+ )
+ // Show UI to collect OTP
}
},
error -> Log.e("AuthQuickstart", error.toString())
);
+
+// Step 3: Then pass that OTP into the confirmSignIn API
+RxAmplify.Auth.confirmSignIn("123456")
+ .subscribe(
+ result -> {
+ // result.getNextStep().getSignInStep() should be "DONE" now
+ },
+ error -> Log.e("AuthQuickstart", error.toString())
+ );
```
+
+
+Omit the `preferredFirstFactor` parameter to discover which first factors are available for a given user. This is useful to allow
+users to choose how they would like to sign in.
+
+The `confirmSignIn` API can then be used to select a challenge and initiate the associated authentication flow.
+
+
+
+
+```swift
+// Step 1: Initiate UserAuth Sign-In
+let pluginOptions = AWSAuthSignInOptions(authFlowType: .userAuth)
+let signInResult = try await Amplify.Auth.signIn(
+ username: "user@example.com",
+ options: .init(pluginOptions: pluginOptions)
+)
+
+switch signInResult.nextStep {
+case .continueSignInWithFirstFactorSelection(let availableFactors):
+ print("Available factors to select: \(availableFactors)")
+ // Prompt the user to select a first factor
+default:
+ break
+}
+
+// Step 2: Select Authentication Factor
+let confirmSignInResult = try await Amplify.Auth.confirmSignIn(
+ challengeResponse: AuthFactorType.emailOTP.challengeResponse
+)
+
+switch confirmSignInResult.nextStep {
+case .confirmSignInWithOTP(let deliveryDetails):
+ print("Delivery details: \(deliveryDetails)")
+ // Prompt the user to enter email OTP code received
+default:
+ break
+}
+
+// Step 3: Complete Sign-In with OTP Code
+let finalSignInResult = try await Amplify.Auth.confirmSignIn(
+ challengeResponse: ""
+)
+if case .done = finalSignInResult.nextStep {
+ print("Login successful")
+}
+```
+
+
+
+
+```swift
+func signInWithUserAuth(username: String, getOTP: @escaping () async -> String) -> AnyCancellable {
+ Amplify.Publisher.create {
+ // Step 1: Initiate UserAuth Sign-In
+ let pluginOptions = AWSAuthSignInOptions(authFlowType: .userAuth)
+ let signInResult = try await Amplify.Auth.signIn(
+ username: username,
+ options: .init(pluginOptions: pluginOptions)
+ )
+
+ // Step 2: Handle factor selection
+ if case .continueSignInWithFirstFactorSelection(let availableFactors) = signInResult.nextStep {
+ print("Available factors to select: \(availableFactors)")
+
+ // For this example, we select emailOTP. You could prompt the user here.
+ let confirmSignInResult = try await Amplify.Auth.confirmSignIn(
+ challengeResponse: AuthFactorType.emailOTP.challengeResponse
+ )
+
+ // Step 3: Handle OTP delivery
+ if case .confirmSignInWithOTP(let deliveryDetails) = confirmSignInResult.nextStep {
+ print("Delivery details: \(deliveryDetails)")
+
+ // Prompt user for OTP code (async closure)
+ let code = await getOTP()
+
+ // Step 4: Complete sign-in with OTP code
+ let finalSignInResult = try await Amplify.Auth.confirmSignIn(
+ challengeResponse: code
+ )
+
+ return finalSignInResult
+ } else {
+ // Handle other next steps if needed
+ return confirmSignInResult
+ }
+ } else {
+ // Handle other next steps or immediate sign-in
+ return signInResult
+ }
+ }
+ .sink(
+ receiveCompletion: { completion in
+ if case let .failure(authError) = completion {
+ print("Sign in failed: \(authError)")
+ }
+ },
+ receiveValue: { result in
+ if result.isSignedIn || (result.nextStep == .done) {
+ print("Sign in succeeded")
+ } else {
+ print("Next step: \(result.nextStep)")
+ }
+ }
+ )
+}
+```
+
+
diff --git a/src/pages/[platform]/build-a-backend/auth/manage-users/manage-webauthn-credentials/index.mdx b/src/pages/[platform]/build-a-backend/auth/manage-users/manage-webauthn-credentials/index.mdx
index af358d68c50..644b40d2bb5 100644
--- a/src/pages/[platform]/build-a-backend/auth/manage-users/manage-webauthn-credentials/index.mdx
+++ b/src/pages/[platform]/build-a-backend/auth/manage-users/manage-webauthn-credentials/index.mdx
@@ -35,9 +35,17 @@ WebAuthn registration and authentication are not currently supported on React Na
-Amplify Auth enables your users to associate, keep track of, and delete passkeys.
+Amplify Auth uses passkeys as the credential mechanism for WebAuthn. The following APIs allow users to register, keep track of, and delete the passkeys associated with their Cognito account.
-## Associate WebAuthN credentials
+[Learn more about using passkeys with Amplify](/[platform]/build-a-backend/auth/concepts/passwordless/#webauthn-passkey).
+
+## Associate WebAuthn credentials
+
+
+
+Registering a passkey is supported on Android 9 (API level 28) and above.
+
+
Note that users must be authenticated to register a passkey. That also means users cannot create a passkey during sign up; consequently, they must have at least one other first factor authentication mechanism associated with their account to use WebAuthn.
@@ -62,7 +70,7 @@ await associateWebAuthnCredential();
Amplify.Auth.associateWebAuthnCredential(
activity,
() -> Log.i("AuthQuickstart", "Associated credential"),
- error -> Log.e("AuthQuickstart", "Failed to register credential", error)
+ error -> Log.e("AuthQuickstart", "Failed to associate credential", error)
);
```
@@ -73,7 +81,7 @@ Amplify.Auth.associateWebAuthnCredential(
Amplify.Auth.associateWebAuthnCredential(
activity,
{ Log.i("AuthQuickstart", "Associated credential") },
- { Log.e("AuthQuickstart", "Failed to register credential", error) }
+ { Log.e("AuthQuickstart", "Failed to associate credential", error) }
)
```
@@ -103,7 +111,7 @@ RxAmplify.Auth.associateWebAuthnCredential(activity)
-You must supply an `Activity` instance so that Amplify can display the PassKey UI in your Application's [Task](https://developer.android.com/guide/components/activities/tasks-and-back-stack).
+You must supply an `Activity` instance so that Amplify can display the PassKey UI in your application's [Task](https://developer.android.com/guide/components/activities/tasks-and-back-stack).
@@ -145,7 +153,7 @@ func associateWebAuthNCredentials() -> AnyCancellable {
The user will be prompted to register a passkey using their local authenticator. Amplify will then associate that passkey with Cognito.
-## List WebAuthN credentials
+## List WebAuthn credentials
You can list registered passkeys using the following API:
@@ -301,7 +309,7 @@ RxAmplify.Auth.listWebAuthnCredentials()
-## Delete WebAuthN credentials
+## Delete WebAuthn credentials
You can delete a passkey with the following API:
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 df7143cec42..6534247ad93 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
@@ -121,7 +121,9 @@ cfnUserPool.enabledMfas = [...(cfnUserPool.enabledMfas || []), "EMAIL_OTP"]
### Override Cognito UserPool to enable passwordless sign-in methods
-You can modify the underlying Cognito user pool resource to enable sign in with passwordless methods. [Learn more about passwordless sign-in methods](/[platform]/build-a-backend/auth/concepts/passwordless/).
+You can modify the underlying Cognito user pool resource to enable sign in with passwordless methods. [Learn more about passwordless sign-in methods](/[platform]/build-a-backend/auth/concepts/passwordless/).
+
+You can also read more about how passwordless authentication flows are implemented in the [Cognito documentation](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow-methods.html).
```ts title="amplify/backend.ts"
import { defineBackend } from "@aws-amplify/backend"
@@ -134,17 +136,20 @@ const backend = defineBackend({
const { cfnResources } = backend.auth.resources;
const { cfnUserPool, cfnUserPoolClient } = cfnResources;
+// Specify which authentication factors you want to allow with USER_AUTH
cfnUserPool.addPropertyOverride(
'Policies.SignInPolicy.AllowedFirstAuthFactors',
['PASSWORD', 'WEB_AUTHN', 'EMAIL_OTP', 'SMS_OTP']
);
+// The USER_AUTH flow is used for passwordless sign in
cfnUserPoolClient.explicitAuthFlows = [
'ALLOW_REFRESH_TOKEN_AUTH',
'ALLOW_USER_AUTH'
];
/* Needed for WebAuthn */
+// The WebAuthnRelyingPartyID is the domain of your relying party, e.g. "example.domain.com"
cfnUserPool.addPropertyOverride('WebAuthnRelyingPartyID', '');
cfnUserPool.addPropertyOverride('WebAuthnUserVerification', 'preferred');
```