Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@

package com.amplifyframework.ui.authenticator

import com.amplifyframework.ui.authenticator.data.AuthenticationFlow
import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
import com.amplifyframework.ui.authenticator.forms.SignUpFormBuilder
import com.amplifyframework.ui.authenticator.options.TotpOptions

internal data class AuthenticatorConfiguration(
val initialStep: AuthenticatorInitialStep,
val signUpForm: SignUpFormBuilder.() -> Unit,
val totpOptions: TotpOptions?
val totpOptions: TotpOptions?,
val authenticationFlow: AuthenticationFlow
)
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.lifecycle.viewmodel.compose.viewModel
import com.amplifyframework.ui.authenticator.data.AuthenticationFlow
import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
import com.amplifyframework.ui.authenticator.forms.SignUpFormBuilder
Expand All @@ -46,15 +47,17 @@ import kotlinx.coroutines.flow.onEach
fun rememberAuthenticatorState(
initialStep: AuthenticatorInitialStep = AuthenticatorStep.SignIn,
signUpForm: SignUpFormBuilder.() -> Unit = {},
totpOptions: TotpOptions? = null
totpOptions: TotpOptions? = null,
authenticationFlow: AuthenticationFlow = AuthenticationFlow.Password
): AuthenticatorState {
val viewModel = viewModel<AuthenticatorViewModel>()
val scope = rememberCoroutineScope()
return remember {
val configuration = AuthenticatorConfiguration(
initialStep = initialStep,
signUpForm = signUpForm,
totpOptions = totpOptions
totpOptions = totpOptions,
authenticationFlow = authenticationFlow
)

viewModel.start(configuration)
Expand Down Expand Up @@ -102,9 +105,7 @@ interface AuthenticatorState {
val messages: Flow<AuthenticatorMessage>
}

internal class AuthenticatorStateImpl constructor(
private val viewModel: AuthenticatorViewModel
) : AuthenticatorState {
internal class AuthenticatorStateImpl constructor(private val viewModel: AuthenticatorViewModel) : AuthenticatorState {
override var stepState by mutableStateOf<AuthenticatorStepState>(LoadingState)

override val messages: Flow<AuthenticatorMessage>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import com.amplifyframework.auth.AuthUserAttribute
import com.amplifyframework.auth.MFAType
import com.amplifyframework.auth.result.AuthSignOutResult
import com.amplifyframework.auth.result.AuthWebAuthnCredential
import com.amplifyframework.ui.authenticator.enums.AuthFactor
import com.amplifyframework.ui.authenticator.data.AuthFactor
import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
import com.amplifyframework.ui.authenticator.forms.MutableFormState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ internal class AuthenticatorViewModel(application: Application, private val auth
// Is there a current Amplify call in progress that could result in a signed in event?
private var expectingSignInEvent: Boolean = false

// The current activity is used for WebAuthn sign-in when using passwordless functionality
private var activityReference: WeakReference<Activity> = WeakReference(null)
var activity: Activity?
get() = activityReference.get()
set(value) {
activityReference = WeakReference(value)
}

fun start(configuration: AuthenticatorConfiguration) {
if (::configuration.isInitialized) {
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* permissions and limitations under the License.
*/

package com.amplifyframework.ui.authenticator.enums
package com.amplifyframework.ui.authenticator.data

import com.amplifyframework.auth.AuthFactorType

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package com.amplifyframework.ui.authenticator.data

import com.amplifyframework.auth.cognito.options.AuthFlowType

/**
* AuthenticationFlow represents the different styles of authentication supported by the Authenticator component.
*/
sealed interface AuthenticationFlow {
/**
* The standard password-based auth flow. The user will be prompted to enter a username and password on the SignIn
* screen. You can use this with either Password or PasswordSrp sign ins.
*/
data object Password : AuthenticationFlow

/**
* A choice-based auth flow, where the user may log in via a password, a passkey, or a one-time-password (OTP) sent
* to their email or SMS. The user is first prompted to enter only their sign in attribute (username/email/phone)
* and then may be presented with options for how to log in. You must have ALLOW_USER_AUTH enabled as an
* authentication flow in your Cognito User Pool.
*/
data class UserChoice(
/**
* Specify an [AuthFactor] to use by default, if available to the user.
*
* For example, if you want any user with a registered passkey to sign in with that passkey without being
* prompted, then set this value to `AuthFactor.WebAuthn`.
*
* If this is null or the [AuthFactor] is not available to the user, they may go directly into a different
* [AuthFactor] (if they only have one available) or may be prompted to choose a factor (if they have multiple
* available).
*
* If this is set to [AuthFactor.Password] or [AuthFactor.PasswordSrp] then the user will be prompted for a
* password directly when signing in. Use these values only if you're certain that no users exist who don't
* have passwords.
*/
val preferredAuthFactor: AuthFactor? = null,

/**
* Control when/if the user is prompted to create a passkey after logging in.
*/
val passkeyPrompts: PasskeyPrompts = PasskeyPrompts()
) : AuthenticationFlow
}

internal val AuthenticationFlow.signUpRequiresPassword: Boolean get() = when (this) {
is AuthenticationFlow.Password -> true
is AuthenticationFlow.UserChoice -> false
}

internal val AuthenticationFlow.signInRequiresPassword: Boolean get() = when (this) {
is AuthenticationFlow.Password -> true
is AuthenticationFlow.UserChoice -> this.preferredAuthFactor is AuthFactor.Password
}

internal fun AuthenticationFlow.toAuthFlowType() = when (this) {
is AuthenticationFlow.Password -> AuthFlowType.USER_SRP_AUTH
is AuthenticationFlow.UserChoice -> AuthFlowType.USER_AUTH
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package com.amplifyframework.ui.authenticator.data

/**
* Class that contains configuration values for when/if to show prompts to create passkeys to the user.
*/
data class PasskeyPrompts(
/**
* Show a prompt after a user who does not have a passkey registered signs in to the application.
*/
val afterSignIn: PasskeyPrompt = PasskeyPrompt.Always,
/**
* Show a prompt to create a passkey after the automatic sign in following a new user signing up.
*/
val afterSignUp: PasskeyPrompt = PasskeyPrompt.Always
)

/**
* Possible selections for controlling passkey prompts.
*/
sealed interface PasskeyPrompt {
/**
* Never prompt users to create a passkey after signing in.
*/
data object Never : PasskeyPrompt

/**
* Always prompt users to create a passkey after signing in if they don't already have an existing registered
* passkey.
*/
data object Always : PasskeyPrompt
}