Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
313 changes: 281 additions & 32 deletions authenticator/api/authenticator.api

Large diffs are not rendered by default.

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 @@ -22,12 +22,15 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
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
import com.amplifyframework.ui.authenticator.options.TotpOptions
import com.amplifyframework.ui.authenticator.util.AuthenticatorMessage
import com.amplifyframework.ui.authenticator.util.findActivity
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
Expand All @@ -46,18 +49,22 @@ 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()
val context = LocalContext.current

return remember {
val configuration = AuthenticatorConfiguration(
initialStep = initialStep,
signUpForm = signUpForm,
totpOptions = totpOptions
totpOptions = totpOptions,
authenticationFlow = authenticationFlow
)

viewModel.start(configuration)
viewModel.start(configuration, context.findActivity())
AuthenticatorStateImpl(viewModel).also { state ->
viewModel.stepState.onEach { state.stepState = it }.launchIn(scope)
}
Expand Down Expand Up @@ -102,9 +109,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 @@ -23,6 +23,8 @@ import com.amplifyframework.auth.AuthUser
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.data.AuthFactor
import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
import com.amplifyframework.ui.authenticator.forms.MutableFormState
Expand All @@ -38,6 +40,17 @@ interface AuthenticatorStepState {
val step: AuthenticatorStep
}

/**
* A state holder for the UI that has multiple possible actions that may be in progress.
*/
@Stable
interface AuthenticatorActionState<T> {
/**
* The action in progress, or null if state is idle
*/
val action: T?
}

/**
* The Authenticator is loading the current state of the user's Auth session.
*/
Expand Down Expand Up @@ -93,6 +106,73 @@ interface SignInState : AuthenticatorStepState {
suspend fun signIn()
}

/**
* The user has entered their username and must select the authentication factor they'd like to use to sign in
*/
@Stable
interface SignInSelectAuthFactorState :
AuthenticatorStepState,
AuthenticatorActionState<SignInSelectAuthFactorState.Action> {

sealed interface Action {
/**
* User has selected an auth factor
*/
data class SelectAuthFactor(val factor: AuthFactor) : Action
}

/**
* The input form state holder for this step.
*/
val form: MutableFormState

/**
* The username entered in the SignIn step
*/
val username: String

/**
* The available types to select how to sign in.
*/
val availableAuthFactors: Set<AuthFactor>

/**
* Move the user to a different [AuthenticatorInitialStep].
*/
fun moveTo(step: AuthenticatorInitialStep)

/**
* Initiate a sign in with one of the available sign in types
*/
suspend fun select(authFactor: AuthFactor)
}

/**
* A user has entered their username and must enter their password to continue signing in
*/
@Stable
interface SignInConfirmPasswordState : AuthenticatorStepState {
/**
* The input form state holder for this step.
*/
val form: MutableFormState

/**
* The username entered in the SignIn step
*/
val username: String

/**
* Move the user to a different [AuthenticatorInitialStep].
*/
fun moveTo(step: AuthenticatorInitialStep)

/**
* Initiate a sign in with the information entered into the [form].
*/
suspend fun signIn()
}

/**
* The user has completed the initial Sign In step, and needs to enter the confirmation code from an MFA
* message to complete the sign in process.
Expand Down Expand Up @@ -460,3 +540,59 @@ interface VerifyUserConfirmState : AuthenticatorStepState {
*/
fun skip()
}

/**
* The user is being shown a prompt to create a passkey, encouraging them to use this as a way to sign in quickly
* via biometrics
*/
@Stable
interface PromptToCreatePasskeyState :
AuthenticatorStepState,
AuthenticatorActionState<PromptToCreatePasskeyState.Action> {
sealed interface Action {
/**
* User is creating a passkey
*/
class CreatePasskey : Action

/**
* User has selected the Skip button
*/
class Skip : Action
}

/**
* Create a passkey
*/
suspend fun createPasskey()

/**
* Skip passkey creation and continue to the next step
*/
suspend fun skip()
}

/**
* The user is being shown a confirmation screen after creating a passkey
*/
@Stable
interface PasskeyCreatedState :
AuthenticatorStepState,
AuthenticatorActionState<PasskeyCreatedState.Action> {
sealed interface Action {
/**
* User has selected the Done button
*/
class ContinueSignIn : Action
}

/**
* A list of existing passkeys for this user, including the one they've just created
*/
val passkeys: List<AuthWebAuthnCredential>

/**
* Continue to the next step
*/
suspend fun continueSignIn()
}
Loading
Loading