Skip to content

Commit f7a6c4c

Browse files
authored
feat(authenticator): Add configuration values for passwordless sign in (#269)
1 parent d496b4b commit f7a6c4c

File tree

7 files changed

+138
-8
lines changed

7 files changed

+138
-8
lines changed

authenticator/src/main/java/com/amplifyframework/ui/authenticator/AuthenticatorConfiguration.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515

1616
package com.amplifyframework.ui.authenticator
1717

18+
import com.amplifyframework.ui.authenticator.data.AuthenticationFlow
1819
import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
1920
import com.amplifyframework.ui.authenticator.forms.SignUpFormBuilder
2021
import com.amplifyframework.ui.authenticator.options.TotpOptions
2122

2223
internal data class AuthenticatorConfiguration(
2324
val initialStep: AuthenticatorInitialStep,
2425
val signUpForm: SignUpFormBuilder.() -> Unit,
25-
val totpOptions: TotpOptions?
26+
val totpOptions: TotpOptions?,
27+
val authenticationFlow: AuthenticationFlow
2628
)

authenticator/src/main/java/com/amplifyframework/ui/authenticator/AuthenticatorState.kt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import androidx.compose.runtime.remember
2323
import androidx.compose.runtime.rememberCoroutineScope
2424
import androidx.compose.runtime.setValue
2525
import androidx.lifecycle.viewmodel.compose.viewModel
26+
import com.amplifyframework.ui.authenticator.data.AuthenticationFlow
2627
import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
2728
import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
2829
import com.amplifyframework.ui.authenticator.forms.SignUpFormBuilder
@@ -46,15 +47,17 @@ import kotlinx.coroutines.flow.onEach
4647
fun rememberAuthenticatorState(
4748
initialStep: AuthenticatorInitialStep = AuthenticatorStep.SignIn,
4849
signUpForm: SignUpFormBuilder.() -> Unit = {},
49-
totpOptions: TotpOptions? = null
50+
totpOptions: TotpOptions? = null,
51+
authenticationFlow: AuthenticationFlow = AuthenticationFlow.Password
5052
): AuthenticatorState {
5153
val viewModel = viewModel<AuthenticatorViewModel>()
5254
val scope = rememberCoroutineScope()
5355
return remember {
5456
val configuration = AuthenticatorConfiguration(
5557
initialStep = initialStep,
5658
signUpForm = signUpForm,
57-
totpOptions = totpOptions
59+
totpOptions = totpOptions,
60+
authenticationFlow = authenticationFlow
5861
)
5962

6063
viewModel.start(configuration)
@@ -102,9 +105,7 @@ interface AuthenticatorState {
102105
val messages: Flow<AuthenticatorMessage>
103106
}
104107

105-
internal class AuthenticatorStateImpl constructor(
106-
private val viewModel: AuthenticatorViewModel
107-
) : AuthenticatorState {
108+
internal class AuthenticatorStateImpl constructor(private val viewModel: AuthenticatorViewModel) : AuthenticatorState {
108109
override var stepState by mutableStateOf<AuthenticatorStepState>(LoadingState)
109110

110111
override val messages: Flow<AuthenticatorMessage>

authenticator/src/main/java/com/amplifyframework/ui/authenticator/AuthenticatorStepState.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import com.amplifyframework.auth.AuthUserAttribute
2424
import com.amplifyframework.auth.MFAType
2525
import com.amplifyframework.auth.result.AuthSignOutResult
2626
import com.amplifyframework.auth.result.AuthWebAuthnCredential
27-
import com.amplifyframework.ui.authenticator.enums.AuthFactor
27+
import com.amplifyframework.ui.authenticator.data.AuthFactor
2828
import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
2929
import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
3030
import com.amplifyframework.ui.authenticator.forms.MutableFormState

authenticator/src/main/java/com/amplifyframework/ui/authenticator/AuthenticatorViewModel.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,14 @@ internal class AuthenticatorViewModel(application: Application, private val auth
121121
// Is there a current Amplify call in progress that could result in a signed in event?
122122
private var expectingSignInEvent: Boolean = false
123123

124+
// The current activity is used for WebAuthn sign-in when using passwordless functionality
125+
private var activityReference: WeakReference<Activity> = WeakReference(null)
126+
var activity: Activity?
127+
get() = activityReference.get()
128+
set(value) {
129+
activityReference = WeakReference(value)
130+
}
131+
124132
fun start(configuration: AuthenticatorConfiguration) {
125133
if (::configuration.isInitialized) {
126134
return

authenticator/src/main/java/com/amplifyframework/ui/authenticator/data/AuthFactor.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* permissions and limitations under the License.
1414
*/
1515

16-
package com.amplifyframework.ui.authenticator.enums
16+
package com.amplifyframework.ui.authenticator.data
1717

1818
import com.amplifyframework.auth.AuthFactorType
1919

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package com.amplifyframework.ui.authenticator.data
17+
18+
import com.amplifyframework.auth.cognito.options.AuthFlowType
19+
20+
/**
21+
* AuthenticationFlow represents the different styles of authentication supported by the Authenticator component.
22+
*/
23+
sealed interface AuthenticationFlow {
24+
/**
25+
* The standard password-based auth flow. The user will be prompted to enter a username and password on the SignIn
26+
* screen. You can use this with either Password or PasswordSrp sign ins.
27+
*/
28+
data object Password : AuthenticationFlow
29+
30+
/**
31+
* A choice-based auth flow, where the user may log in via a password, a passkey, or a one-time-password (OTP) sent
32+
* to their email or SMS. The user is first prompted to enter only their sign in attribute (username/email/phone)
33+
* and then may be presented with options for how to log in. You must have ALLOW_USER_AUTH enabled as an
34+
* authentication flow in your Cognito User Pool.
35+
*/
36+
data class UserChoice(
37+
/**
38+
* Specify an [AuthFactor] to use by default, if available to the user.
39+
*
40+
* For example, if you want any user with a registered passkey to sign in with that passkey without being
41+
* prompted, then set this value to `AuthFactor.WebAuthn`.
42+
*
43+
* If this is null or the [AuthFactor] is not available to the user, they may go directly into a different
44+
* [AuthFactor] (if they only have one available) or may be prompted to choose a factor (if they have multiple
45+
* available).
46+
*
47+
* If this is set to [AuthFactor.Password] or [AuthFactor.PasswordSrp] then the user will be prompted for a
48+
* password directly when signing in. Use these values only if you're certain that no users exist who don't
49+
* have passwords.
50+
*/
51+
val preferredAuthFactor: AuthFactor? = null,
52+
53+
/**
54+
* Control when/if the user is prompted to create a passkey after logging in.
55+
*/
56+
val passkeyPrompts: PasskeyPrompts = PasskeyPrompts()
57+
) : AuthenticationFlow
58+
}
59+
60+
internal val AuthenticationFlow.signUpRequiresPassword: Boolean get() = when (this) {
61+
is AuthenticationFlow.Password -> true
62+
is AuthenticationFlow.UserChoice -> false
63+
}
64+
65+
internal val AuthenticationFlow.signInRequiresPassword: Boolean get() = when (this) {
66+
is AuthenticationFlow.Password -> true
67+
is AuthenticationFlow.UserChoice -> this.preferredAuthFactor is AuthFactor.Password
68+
}
69+
70+
internal fun AuthenticationFlow.toAuthFlowType() = when (this) {
71+
is AuthenticationFlow.Password -> AuthFlowType.USER_SRP_AUTH
72+
is AuthenticationFlow.UserChoice -> AuthFlowType.USER_AUTH
73+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package com.amplifyframework.ui.authenticator.data
17+
18+
/**
19+
* Class that contains configuration values for when/if to show prompts to create passkeys to the user.
20+
*/
21+
data class PasskeyPrompts(
22+
/**
23+
* Show a prompt after a user who does not have a passkey registered signs in to the application.
24+
*/
25+
val afterSignIn: PasskeyPrompt = PasskeyPrompt.Always,
26+
/**
27+
* Show a prompt to create a passkey after the automatic sign in following a new user signing up.
28+
*/
29+
val afterSignUp: PasskeyPrompt = PasskeyPrompt.Always
30+
)
31+
32+
/**
33+
* Possible selections for controlling passkey prompts.
34+
*/
35+
sealed interface PasskeyPrompt {
36+
/**
37+
* Never prompt users to create a passkey after signing in.
38+
*/
39+
data object Never : PasskeyPrompt
40+
41+
/**
42+
* Always prompt users to create a passkey after signing in if they don't already have an existing registered
43+
* passkey.
44+
*/
45+
data object Always : PasskeyPrompt
46+
}

0 commit comments

Comments
 (0)