diff --git a/docs/src/pages/[platform]/connected-components/authenticator/configuration/index.page.mdx b/docs/src/pages/[platform]/connected-components/authenticator/configuration/index.page.mdx
index 58dc372255..0ec7ee2f71 100644
--- a/docs/src/pages/[platform]/connected-components/authenticator/configuration/index.page.mdx
+++ b/docs/src/pages/[platform]/connected-components/authenticator/configuration/index.page.mdx
@@ -44,6 +44,8 @@ import HideSignupSwift from './hidesignup.swift.mdx';
import HideSignupWeb from './hidesignup.web.mdx';
import TotpSwift from './totpIssuer.swift.mdx';
import TotpAndroid from './totpIssuer.android.mdx';
+import PasswordlessSwift from './passwordless.swift.mdx';
+import PasswordlessAndroid from './passwordless.android.mdx';
export async function getStaticPaths() {
return getCustomStaticPath(frontmatter.supportedFrameworks);
@@ -72,6 +74,8 @@ By default, unauthenticated users are redirected to the Sign In flow. You can ex
+
+
@@ -81,6 +85,8 @@ By default, unauthenticated users are redirected to the Sign In flow. You can ex
+
+
## Sign Up Attributes
diff --git a/docs/src/pages/[platform]/connected-components/authenticator/configuration/passwordless.android.mdx b/docs/src/pages/[platform]/connected-components/authenticator/configuration/passwordless.android.mdx
new file mode 100644
index 0000000000..530acb6d2c
--- /dev/null
+++ b/docs/src/pages/[platform]/connected-components/authenticator/configuration/passwordless.android.mdx
@@ -0,0 +1,113 @@
+import { Alert } from '@aws-amplify/ui-react';
+
+### Passwordless Authentication
+
+
+ Passkey support requires Android API level 28+ with Google Play Services.
+
+
+#### Backend Configuration
+
+For backend setup including Email OTP, SMS OTP, and WebAuthn (Passkey) configuration, see the [Passwordless documentation](https://docs.amplify.aws/android/build-a-backend/auth/concepts/passwordless/).
+
+#### Authentication Flows
+
+The Authenticator supports two authentication flows:
+
+**Password Flow (Default)**
+
+Traditional password-based authentication:
+
+```kotlin
+Authenticator(
+ authenticationFlow = AuthenticationFlow.Password
+) { state ->
+ Text("Welcome!")
+}
+```
+
+**User Choice Flow**
+
+Allows users to choose from available authentication methods:
+
+```kotlin
+Authenticator(
+ authenticationFlow = AuthenticationFlow.UserChoice()
+) { state ->
+ Text("Welcome!")
+}
+```
+
+#### Available Authentication Factors
+
+| Factor | Description | Code |
+|--------|-------------|------|
+| Password | Traditional password with SRP | `AuthFactor.Password(srp = true)` |
+| Password (no SRP) | Password without SRP | `AuthFactor.Password(srp = false)` |
+| Email OTP | One-time code via email | `AuthFactor.EmailOtp` |
+| SMS OTP | One-time code via SMS | `AuthFactor.SmsOtp` |
+| WebAuthn (Passkey) | Biometric/security key authentication | `AuthFactor.WebAuthn` |
+
+#### Preferred Auth Factor
+
+Skip the factor selection step by specifying a preferred factor:
+
+```kotlin
+Authenticator(
+ authenticationFlow = AuthenticationFlow.UserChoice(
+ preferredAuthFactor = AuthFactor.WebAuthn
+ )
+) { state ->
+ Text("Welcome!")
+}
+```
+
+When a preferred factor is set:
+- If the user has that factor available, they'll use it directly
+- If not available, they'll see the factor selection screen
+
+#### Passkey Prompts
+
+Control when users are prompted to create a passkey:
+
+```kotlin
+Authenticator(
+ authenticationFlow = AuthenticationFlow.UserChoice(
+ passkeyPrompts = PasskeyPrompts(
+ afterSignUp = PasskeyPrompt.Always, // Prompt after sign-up
+ afterSignIn = PasskeyPrompt.Never // Don't prompt after sign-in
+ )
+ )
+) { state ->
+ Text("Welcome!")
+}
+```
+
+**PasskeyPrompt Options:**
+- `PasskeyPrompt.Always` - Always prompt to create a passkey (if user doesn't have one)
+- `PasskeyPrompt.Never` - Never prompt to create a passkey
+
+#### Authentication Flow Steps
+
+When using `UserChoice` flow, users may encounter these additional steps:
+
+1. **SignInSelectAuthFactor** - User selects their preferred authentication method
+2. **SignInConfirmPassword** - User enters password (if password factor selected)
+3. **PromptToCreatePasskey** - User is prompted to create a passkey
+4. **PasskeyCreated** - Confirmation after successful passkey creation
+
+#### Complete Example
+
+```kotlin
+Authenticator(
+ authenticationFlow = AuthenticationFlow.UserChoice(
+ preferredAuthFactor = AuthFactor.WebAuthn,
+ passkeyPrompts = PasskeyPrompts(
+ afterSignUp = PasskeyPrompt.Always,
+ afterSignIn = PasskeyPrompt.Always
+ )
+ )
+) { state ->
+ Text("Welcome ${state.user.username}!")
+}
+```
diff --git a/docs/src/pages/[platform]/connected-components/authenticator/configuration/passwordless.swift.mdx b/docs/src/pages/[platform]/connected-components/authenticator/configuration/passwordless.swift.mdx
new file mode 100644
index 0000000000..a93ed95393
--- /dev/null
+++ b/docs/src/pages/[platform]/connected-components/authenticator/configuration/passwordless.swift.mdx
@@ -0,0 +1,109 @@
+import { Alert } from '@aws-amplify/ui-react';
+
+### Passwordless Authentication
+
+
+ Passkey support requires iOS 17.4+, macOS 13.5+, or visionOS 1.0+.
+
+
+#### Backend Configuration
+
+For backend setup including Email OTP, SMS OTP, and WebAuthn (Passkey) configuration, see the [Passwordless documentation](https://docs.amplify.aws/swift/build-a-backend/auth/concepts/passwordless/).
+
+#### Authentication Flows
+
+The Authenticator supports two authentication flows:
+
+**Password Flow (Default)**
+
+Traditional password-based authentication:
+
+```swift
+Authenticator(authenticationFlow: .password) { signedInState in
+ Text("Welcome!")
+}
+```
+
+**User Choice Flow**
+
+Allows users to choose from available authentication methods:
+
+```swift
+Authenticator(authenticationFlow: .userChoice()) { signedInState in
+ Text("Welcome!")
+}
+```
+
+#### Available Authentication Factors
+
+| Factor | Description | Code |
+|--------|-------------|------|
+| Password | Traditional password with SRP | `.password(srp: true)` |
+| Password (no SRP) | Password without SRP | `.password(srp: false)` |
+| Email OTP | One-time code via email | `.emailOtp` |
+| SMS OTP | One-time code via SMS | `.smsOtp` |
+| WebAuthn (Passkey) | Biometric/security key authentication | `.webAuthn` |
+
+#### Preferred Auth Factor
+
+Skip the factor selection step by specifying a preferred factor:
+
+```swift
+Authenticator(
+ authenticationFlow: .userChoice(
+ preferredAuthFactor: .webAuthn
+ )
+) { signedInState in
+ Text("Welcome!")
+}
+```
+
+When a preferred factor is set:
+- If the user has that factor available, they'll use it directly
+- If not available, they'll see the factor selection screen
+
+#### Passkey Prompts
+
+Control when users are prompted to create a passkey:
+
+```swift
+Authenticator(
+ authenticationFlow: .userChoice(
+ passkeyPrompts: PasskeyPrompts(
+ afterSignUp: .always, // Prompt after sign-up
+ afterSignIn: .never // Don't prompt after sign-in
+ )
+ )
+) { signedInState in
+ Text("Welcome!")
+}
+```
+
+**PasskeyPrompt Options:**
+- `.always` - Always prompt to create a passkey (if user doesn't have one)
+- `.never` - Never prompt to create a passkey
+
+#### Authentication Flow Steps
+
+When using `.userChoice` flow, users may encounter these additional steps:
+
+1. **signInSelectAuthFactor** - User selects their preferred authentication method
+2. **signInConfirmPassword** - User enters password (if password factor selected)
+3. **promptToCreatePasskey** - User is prompted to create a passkey
+4. **passkeyCreated** - Confirmation after successful passkey creation
+
+#### Complete Example
+
+```swift
+Authenticator(
+ authenticationFlow: .userChoice(
+ preferredAuthFactor: .webAuthn,
+ passkeyPrompts: PasskeyPrompts(
+ afterSignUp: .always,
+ afterSignIn: .always
+ )
+ )
+) { signedInState in
+ Text("Welcome \(signedInState.user.username)!")
+}
+```
diff --git a/docs/src/pages/[platform]/connected-components/authenticator/customization/customization.passwordless-views.android.mdx b/docs/src/pages/[platform]/connected-components/authenticator/customization/customization.passwordless-views.android.mdx
new file mode 100644
index 0000000000..d49868977e
--- /dev/null
+++ b/docs/src/pages/[platform]/connected-components/authenticator/customization/customization.passwordless-views.android.mdx
@@ -0,0 +1,75 @@
+import { Alert } from '@aws-amplify/ui-react';
+
+### Customizing Passwordless Views
+
+When using `AuthenticationFlow.UserChoice`, you can customize the additional views that appear during the authentication flow. Below is an example of customizing the Passkey views.
+
+#### Prompt To Create Passkey View
+
+Customize the passkey creation prompt that appears after sign-in or sign-up:
+
+```kotlin
+Authenticator(
+ authenticationFlow = AuthenticationFlow.UserChoice(),
+ promptToCreatePasskeyContent = { state ->
+ Column {
+ Text("Create a Passkey")
+ Text("Sign in faster with biometrics")
+
+ Button(onClick = {
+ scope.launch { state.createPasskey() }
+ }) {
+ Text("Create Passkey")
+ }
+
+ Button(onClick = {
+ scope.launch { state.skip() }
+ }) {
+ Text("Skip for now")
+ }
+ }
+ }
+) { state ->
+ Text("Welcome!")
+}
+```
+
+**PromptToCreatePasskeyState Actions:**
+- `createPasskey()` - Initiates passkey creation using the device's biometric/security key authentication
+- `skip()` - Skips passkey creation and continues to the signed-in state
+
+#### Passkey Created View
+
+Customize the confirmation screen shown after successful passkey creation:
+
+```kotlin
+Authenticator(
+ authenticationFlow = AuthenticationFlow.UserChoice(),
+ passkeyCreatedContent = { state ->
+ Column {
+ Text("Passkey Created Successfully!")
+ Text("You have ${state.passkeys.size} passkey(s)")
+
+ state.passkeys.forEach { passkey ->
+ Card {
+ Text(passkey.friendlyName ?: "Unknown Passkey")
+ }
+ }
+
+ Button(onClick = {
+ scope.launch { state.continueSignIn() }
+ }) {
+ Text("Continue")
+ }
+ }
+ }
+) { state ->
+ Text("Welcome!")
+}
+```
+
+**PasskeyCreatedState Properties:**
+- `passkeys: List` - List of user's passkey credentials
+
+**PasskeyCreatedState Actions:**
+- `continueSignIn()` - Continues to the signed-in state
diff --git a/docs/src/pages/[platform]/connected-components/authenticator/customization/customization.passwordless-views.swift.mdx b/docs/src/pages/[platform]/connected-components/authenticator/customization/customization.passwordless-views.swift.mdx
new file mode 100644
index 0000000000..c8a19317da
--- /dev/null
+++ b/docs/src/pages/[platform]/connected-components/authenticator/customization/customization.passwordless-views.swift.mdx
@@ -0,0 +1,101 @@
+import { Alert } from '@aws-amplify/ui-react';
+
+### Customizing Passwordless Views
+
+When using `.userChoice` authentication flow, you can customize the additional views that appear during the authentication flow. Below is an example of customizing the Passkey views.
+
+#### Prompt To Create Passkey View
+
+Customize the passkey creation prompt that appears after sign-in or sign-up:
+
+```swift
+Authenticator(
+ authenticationFlow: .userChoice(),
+ promptToCreatePasskeyContent: { state in
+ VStack(spacing: 24) {
+ Image(systemName: "person.badge.key.fill")
+ .font(.system(size: 60))
+ .foregroundColor(.blue)
+
+ Text("Create a Passkey")
+ .font(.title)
+
+ Text("Sign in faster with Face ID or Touch ID")
+ .foregroundColor(.secondary)
+ .multilineTextAlignment(.center)
+
+ Button("Create Passkey") {
+ Task { try? await state.createPasskey() }
+ }
+ .buttonStyle(.borderedProminent)
+
+ Button("Skip for now") {
+ Task { try? await state.skip() }
+ }
+ .buttonStyle(.borderless)
+ }
+ .padding()
+ }
+) { signedInState in
+ Text("Welcome!")
+}
+```
+
+**PromptToCreatePasskeyState Methods:**
+- `createPasskey()` - Initiates passkey creation using the device's biometric/security key authentication
+- `skip()` - Skips passkey creation and continues to the signed-in state
+
+#### Passkey Created View
+
+Customize the confirmation screen shown after successful passkey creation:
+
+```swift
+Authenticator(
+ authenticationFlow: .userChoice(),
+ passkeyCreatedContent: { state in
+ VStack(spacing: 24) {
+ Image(systemName: "checkmark.circle.fill")
+ .font(.system(size: 60))
+ .foregroundColor(.green)
+
+ Text("Passkey Created!")
+ .font(.title)
+
+ if !state.passkeyCredentials.isEmpty {
+ VStack(alignment: .leading, spacing: 8) {
+ Text("Your Passkeys")
+ .font(.headline)
+
+ ForEach(state.passkeyCredentials, id: \.credentialId) { credential in
+ HStack {
+ Image(systemName: "key.fill")
+ Text(credential.friendlyName ?? "Unknown Passkey")
+ }
+ .padding()
+ .background(Color.secondary.opacity(0.1))
+ .cornerRadius(8)
+ }
+ }
+ }
+
+ Button("Continue") {
+ Task { try? await state.continue() }
+ }
+ .buttonStyle(.borderedProminent)
+ }
+ .padding()
+ .task {
+ await state.fetchPasskeyCredentials()
+ }
+ }
+) { signedInState in
+ Text("Welcome!")
+}
+```
+
+**PasskeyCreatedState Properties:**
+- `passkeyCredentials: [AuthWebAuthnCredential]` - List of user's passkey credentials
+
+**PasskeyCreatedState Methods:**
+- `continue()` - Continues to the signed-in state
+- `fetchPasskeyCredentials()` - Fetches the list of passkey credentials
diff --git a/docs/src/pages/[platform]/connected-components/authenticator/customization/index.page.mdx b/docs/src/pages/[platform]/connected-components/authenticator/customization/index.page.mdx
index 731b072c1e..da11d68a1c 100644
--- a/docs/src/pages/[platform]/connected-components/authenticator/customization/index.page.mdx
+++ b/docs/src/pages/[platform]/connected-components/authenticator/customization/index.page.mdx
@@ -41,6 +41,8 @@ import StylingWeb from './customization.styling.web.mdx';
import FullAndroid from './customization.full-ui-customization.android.mdx';
import FullFlutter from './customization.full-ui-customization.flutter.mdx';
import FullSwift from './customization.full-ui-customization.swift.mdx';
+import PasswordlessViewsAndroid from './customization.passwordless-views.android.mdx';
+import PasswordlessViewsSwift from './customization.passwordless-views.swift.mdx';
export async function getStaticPaths() {
return getCustomStaticPath(frontmatter.supportedFrameworks);
@@ -100,6 +102,8 @@ export async function getStaticProps() {
+
+
@@ -127,6 +131,8 @@ export async function getStaticProps() {
+
+
diff --git a/docs/src/pages/[platform]/connected-components/authenticator/customization/sign-up-fields.android.mdx b/docs/src/pages/[platform]/connected-components/authenticator/customization/sign-up-fields.android.mdx
index 30d8f36b34..38e21e39ba 100644
--- a/docs/src/pages/[platform]/connected-components/authenticator/customization/sign-up-fields.android.mdx
+++ b/docs/src/pages/[platform]/connected-components/authenticator/customization/sign-up-fields.android.mdx
@@ -1,3 +1,5 @@
+import { Alert } from '@aws-amplify/ui-react';
+
## Sign Up Field Order
The Authenticator allows a custom order of sign up fields on the Sign Up page.
@@ -93,4 +95,44 @@ val state = rememberAuthenticatorState(
}
}
)
-```
\ No newline at end of file
+```
+
+## Password Fields in Passwordless Flow
+
+When using `AuthenticationFlow.UserChoice`, password fields are **not visible by default** on the sign-up form. This allows users to sign up using passwordless methods like Email OTP, SMS OTP, or Passkeys.
+
+To explicitly show password fields, add them to your `signUpForm` configuration. You can then set `required = true` or `required = false` based on your requirements:
+
+```kotlin
+Authenticator(
+ authenticationFlow = AuthenticationFlow.UserChoice(),
+ signUpForm = {
+ username()
+ email()
+ password(required = true) // Show password field, make it required
+ confirmPassword(required = true)
+ }
+) { state ->
+ Text("Welcome!")
+}
+```
+
+Or make password optional while still showing the fields:
+
+```kotlin
+Authenticator(
+ authenticationFlow = AuthenticationFlow.UserChoice(),
+ signUpForm = {
+ username()
+ email()
+ password(required = false) // Show password field, make it optional
+ confirmPassword(required = false)
+ }
+) { state ->
+ Text("Welcome!")
+}
+```
+
+
+ When using `AuthenticationFlow.Password`, password fields are **always required** regardless of the `required` parameter. This means the `Password` flow will override any `password()` configuration in `signUpForm`, even if `required` is set to `false`.
+
diff --git a/docs/src/pages/[platform]/connected-components/authenticator/customization/sign-up-fields.swift.mdx b/docs/src/pages/[platform]/connected-components/authenticator/customization/sign-up-fields.swift.mdx
index e3852fe563..299b883bea 100644
--- a/docs/src/pages/[platform]/connected-components/authenticator/customization/sign-up-fields.swift.mdx
+++ b/docs/src/pages/[platform]/connected-components/authenticator/customization/sign-up-fields.swift.mdx
@@ -1,3 +1,5 @@
+import { Alert } from '@aws-amplify/ui-react';
+
## Sign Up Field Order
The Authenticator respects the order of the Sign Up fields that are provided in the `signUpFields(_:)` call.
@@ -56,3 +58,44 @@ extension Binding where Value == String {
}
}
```
+
+
+## Password Fields in Passwordless Flow
+
+When using `.userChoice` authentication flow, password fields are **not visible by default** on the sign-up form. This allows users to sign up using passwordless methods like Email OTP, SMS OTP, or Passkeys.
+
+To explicitly show password fields, add them to your `signUpFields` configuration. You can then set `isRequired: true` or `isRequired: false` based on your requirements:
+
+```swift
+Authenticator(
+ authenticationFlow: .userChoice()
+) { signedInState in
+ Text("Welcome!")
+}
+.signUpFields([
+ .username(),
+ .email(),
+ .password(isRequired: true), // Show password field, make it required
+ .confirmPassword(isRequired: true)
+])
+```
+
+Or make password optional while still showing the fields:
+
+```swift
+Authenticator(
+ authenticationFlow: .userChoice()
+) { signedInState in
+ Text("Welcome!")
+}
+.signUpFields([
+ .username(),
+ .email(),
+ .password(isRequired: false), // Show password field, make it optional
+ .confirmPassword(isRequired: false)
+])
+```
+
+
+ When using `.password` authentication flow, password fields are **always required** regardless of the `isRequired` parameter. This means the `.password` flow will override any `.signUpFields(.password())` configuration, even if `isRequired` is set to `false`.
+