Skip to content

Commit 94c445e

Browse files
authored
feat(authenticator): Support passwordless signUp (#274)
1 parent f7a6c4c commit 94c445e

File tree

19 files changed

+223
-189
lines changed

19 files changed

+223
-189
lines changed

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

Lines changed: 90 additions & 97 deletions
Large diffs are not rendered by default.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.amplifyframework.ui.authenticator.data
2+
3+
import com.amplifyframework.ui.authenticator.enums.SignInSource
4+
5+
internal data class UserInfo(val username: String, val password: String?, val signInSource: SignInSource)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.amplifyframework.ui.authenticator.enums
2+
3+
internal enum class SignInSource {
4+
// Standard sign in
5+
SignIn,
6+
7+
// Automatic sign in after completing sign up
8+
SignUp,
9+
10+
// Signed in outside of Authenticator
11+
External
12+
}

authenticator/src/main/java/com/amplifyframework/ui/authenticator/forms/FormBuilder.kt

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@ import com.amplifyframework.ui.authenticator.auth.toFieldKey
2323

2424
internal data class FormData(val fields: List<FieldConfig>)
2525

26-
internal fun buildForm(func: FormBuilderImpl.() -> Unit): FormData {
27-
return FormBuilderImpl().apply(func).build()
28-
}
26+
internal fun buildForm(func: FormBuilderImpl.() -> Unit): FormData = FormBuilderImpl().apply(func).build()
2927

3028
/**
3129
* Builder API for supplying custom form metadata for the signup form.
@@ -39,12 +37,12 @@ interface SignUpFormBuilder {
3937
/**
4038
* Adds the standard password field.
4139
*/
42-
fun password()
40+
fun password(required: Boolean = true)
4341

4442
/**
4543
* Adds the standard password confirmation field.
4644
*/
47-
fun confirmPassword()
45+
fun confirmPassword(required: Boolean = true)
4846

4947
/**
5048
* Adds the standard email field.
@@ -187,18 +185,23 @@ internal class FormBuilderImpl : SignUpFormBuilder {
187185
)
188186
}
189187

190-
override fun password() = password(validator = FieldValidators.None)
188+
override fun password(required: Boolean) = password(
189+
required = required,
190+
validator = FieldValidators.None
191+
)
191192

192-
fun password(validator: FieldValidator) {
193+
fun password(validator: FieldValidator, required: Boolean = true) {
193194
this += FieldConfig.Password(
194195
key = FieldKey.Password,
196+
required = required,
195197
validator = validator
196198
)
197199
}
198200

199-
override fun confirmPassword() {
201+
override fun confirmPassword(required: Boolean) {
200202
this += FieldConfig.Password(
201203
key = FieldKey.ConfirmPassword,
204+
required = required,
202205
validator = FieldValidators.confirmPassword()
203206
)
204207
}
@@ -317,13 +320,7 @@ internal class FormBuilderImpl : SignUpFormBuilder {
317320
)
318321
}
319322

320-
override fun date(
321-
key: FieldKey,
322-
label: String,
323-
hint: String?,
324-
required: Boolean,
325-
validator: FieldValidator
326-
) {
323+
override fun date(key: FieldKey, label: String, hint: String?, required: Boolean, validator: FieldValidator) {
327324
this += FieldConfig.Date(
328325
key = key,
329326
label = label,
@@ -333,13 +330,7 @@ internal class FormBuilderImpl : SignUpFormBuilder {
333330
)
334331
}
335332

336-
override fun phone(
337-
key: FieldKey,
338-
label: String,
339-
hint: String?,
340-
required: Boolean,
341-
validator: FieldValidator
342-
) {
333+
override fun phone(key: FieldKey, label: String, hint: String?, required: Boolean, validator: FieldValidator) {
343334
this += FieldConfig.PhoneNumber(
344335
key = key,
345336
label = label,
@@ -392,10 +383,7 @@ internal class FormBuilderImpl : SignUpFormBuilder {
392383
fields.putAll(map)
393384
}
394385

395-
fun markRequiredFields(
396-
signInMethod: SignInMethod,
397-
requiredKeys: List<AuthUserAttributeKey>
398-
) {
386+
fun markRequiredFields(signInMethod: SignInMethod, requiredKeys: List<AuthUserAttributeKey>) {
399387
fields.replaceAll { fieldKey, config ->
400388
if (fieldKey is FieldKey.UserAttributeKey && requiredKeys.contains(fieldKey.attributeKey)) {
401389
config.required()

authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignInSelectAuthFactorStateImpl.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import androidx.compose.runtime.mutableStateOf
55
import androidx.compose.runtime.setValue
66
import com.amplifyframework.ui.authenticator.SignInSelectAuthFactorState
77
import com.amplifyframework.ui.authenticator.auth.SignInMethod
8-
import com.amplifyframework.ui.authenticator.enums.AuthFactor
8+
import com.amplifyframework.ui.authenticator.data.AuthFactor
9+
import com.amplifyframework.ui.authenticator.data.containsPassword
910
import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
1011
import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
11-
import com.amplifyframework.ui.authenticator.enums.containsPassword
1212

1313
internal class SignInSelectAuthFactorStateImpl(
1414
override val username: String,
@@ -46,4 +46,4 @@ internal fun SignInSelectAuthFactorState.getPasswordFactor(): AuthFactor =
4646
availableAuthFactors.first { it is AuthFactor.Password }
4747

4848
internal val SignInSelectAuthFactorState.signInMethod: SignInMethod
49-
get() = (this as SignInSelectAuthFactorStateImpl).signInMethod
49+
get() = (this as SignInSelectAuthFactorStateImpl).signInMethod

authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/SignUpStateImpl.kt

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,28 @@ import com.amplifyframework.ui.authenticator.forms.buildForm
3131
internal class SignUpStateImpl(
3232
private val signInMethod: SignInMethod,
3333
private val signUpAttributes: List<AuthUserAttributeKey>,
34+
requirePasswordField: Boolean,
3435
private val passwordCriteria: PasswordCriteria,
3536
private val signUpForm: FormData,
36-
private val onSubmit: suspend (username: String, password: String, attributes: List<AuthUserAttribute>) -> Unit,
37+
private val onSubmit: suspend (username: String, password: String?, attributes: List<AuthUserAttribute>) -> Unit,
3738
private val onMoveTo: (step: AuthenticatorInitialStep) -> Unit
38-
) : BaseStateImpl(), SignUpState {
39+
) : BaseStateImpl(),
40+
SignUpState {
3941

4042
init {
4143
val formData = buildForm {
4244
// First add all fields required by configuration in the standard order
4345
fieldForSignInMethod(signInMethod)
44-
password(validator = FieldValidators.password(passwordCriteria))
45-
confirmPassword()
46+
if (requirePasswordField) {
47+
password(validator = FieldValidators.password(passwordCriteria))
48+
49+
// We don't add confirm password if the customer supplied a form with password and without confirmPassword
50+
if (signUpForm.containsField(FieldKey.ConfirmPassword) ||
51+
!signUpForm.containsField(FieldKey.Password)
52+
) {
53+
confirmPassword()
54+
}
55+
}
4656
signUpAttributes.forEach { attribute ->
4757
when (attribute) {
4858
AuthUserAttributeKey.birthdate() -> birthdate(required = true)
@@ -77,8 +87,10 @@ internal class SignUpStateImpl(
7787

7888
override suspend fun signUp() = doSubmit {
7989
val username = form.getTrimmed(signInMethod.toFieldKey())!!
80-
val password = form.getTrimmed(FieldKey.Password)!!
90+
val password = form.getTrimmed(FieldKey.Password)
8191
val attributes = form.getUserAttributes()
8292
onSubmit(username, password, attributes)
8393
}
94+
95+
private fun FormData.containsField(key: FieldKey) = fields.any { it.key == key }
8496
}

authenticator/src/main/java/com/amplifyframework/ui/authenticator/states/StepStateFactory.kt

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,25 @@ import com.amplifyframework.auth.AuthUserAttribute
2121
import com.amplifyframework.auth.AuthUserAttributeKey
2222
import com.amplifyframework.auth.MFAType
2323
import com.amplifyframework.auth.result.AuthSignOutResult
24+
import com.amplifyframework.ui.authenticator.AuthenticatorConfiguration
2425
import com.amplifyframework.ui.authenticator.auth.AmplifyAuthConfiguration
26+
import com.amplifyframework.ui.authenticator.data.signUpRequiresPassword
2527
import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
2628
import com.amplifyframework.ui.authenticator.forms.FormData
2729

2830
internal class StepStateFactory(
31+
private val configuration: AuthenticatorConfiguration,
2932
private val authConfiguration: AmplifyAuthConfiguration,
3033
private val signUpForm: FormData,
3134
private val onMoveTo: (step: AuthenticatorInitialStep) -> Unit
3235
) {
3336

34-
fun newSignedInState(
35-
user: AuthUser,
36-
onSignOut: suspend () -> AuthSignOutResult
37-
) = SignedInStateImpl(
37+
fun newSignedInState(user: AuthUser, onSignOut: suspend () -> AuthSignOutResult) = SignedInStateImpl(
3838
user = user,
3939
onSignOut = onSignOut
4040
)
4141

42-
fun newSignInState(
43-
onSubmit: suspend (username: String, password: String) -> Unit
44-
) = SignInStateImpl(
42+
fun newSignInState(onSubmit: suspend (username: String, password: String) -> Unit) = SignInStateImpl(
4543
signInMethod = authConfiguration.signInMethod,
4644
onSubmit = onSubmit,
4745
onMoveTo = onMoveTo
@@ -67,20 +65,18 @@ internal class StepStateFactory(
6765
onMoveTo = onMoveTo
6866
)
6967

70-
fun newSignInConfirmNewPasswordState(
71-
onSubmit: suspend (password: String) -> Unit
72-
) = SignInConfirmNewPasswordStateImpl(
73-
passwordCriteria = authConfiguration.passwordCriteria,
74-
onSubmit = onSubmit,
75-
onMoveTo = onMoveTo
76-
)
68+
fun newSignInConfirmNewPasswordState(onSubmit: suspend (password: String) -> Unit) =
69+
SignInConfirmNewPasswordStateImpl(
70+
passwordCriteria = authConfiguration.passwordCriteria,
71+
onSubmit = onSubmit,
72+
onMoveTo = onMoveTo
73+
)
7774

78-
fun newSignInConfirmTotpCodeState(
79-
onSubmit: suspend (confirmationCode: String) -> Unit
80-
) = SignInConfirmTotpCodeStateImpl(
81-
onSubmit = onSubmit,
82-
onMoveTo = onMoveTo
83-
)
75+
fun newSignInConfirmTotpCodeState(onSubmit: suspend (confirmationCode: String) -> Unit) =
76+
SignInConfirmTotpCodeStateImpl(
77+
onSubmit = onSubmit,
78+
onMoveTo = onMoveTo
79+
)
8480

8581
fun newSignInContinueWithMfaSetupSelectionState(
8682
allowedMfaTypes: Set<MFAType>,
@@ -91,12 +87,11 @@ internal class StepStateFactory(
9187
onMoveTo = onMoveTo
9288
)
9389

94-
fun newSignInContinueWithEmailSetupState(
95-
onSubmit: suspend (email: String) -> Unit
96-
) = SignInContinueWithEmailSetupStateImpl(
97-
onSubmit = onSubmit,
98-
onMoveTo = onMoveTo
99-
)
90+
fun newSignInContinueWithEmailSetupState(onSubmit: suspend (email: String) -> Unit) =
91+
SignInContinueWithEmailSetupStateImpl(
92+
onSubmit = onSubmit,
93+
onMoveTo = onMoveTo
94+
)
10095

10196
fun newSignInContinueWithMfaSelectionState(
10297
allowedMfaTypes: Set<MFAType>,
@@ -119,10 +114,11 @@ internal class StepStateFactory(
119114
)
120115

121116
fun newSignUpState(
122-
onSubmit: suspend (username: String, password: String, attributes: List<AuthUserAttribute>) -> Unit
117+
onSubmit: suspend (username: String, password: String?, attributes: List<AuthUserAttribute>) -> Unit
123118
) = SignUpStateImpl(
124119
signInMethod = authConfiguration.signInMethod,
125120
signUpAttributes = authConfiguration.signUpAttributes,
121+
requirePasswordField = configuration.authenticationFlow.signUpRequiresPassword,
126122
passwordCriteria = authConfiguration.passwordCriteria,
127123
signUpForm = signUpForm,
128124
onSubmit = onSubmit,
@@ -140,9 +136,7 @@ internal class StepStateFactory(
140136
onMoveTo = onMoveTo
141137
)
142138

143-
fun newResetPasswordState(
144-
onSubmit: suspend (username: String) -> Unit
145-
) = PasswordResetStateImpl(
139+
fun newResetPasswordState(onSubmit: suspend (username: String) -> Unit) = PasswordResetStateImpl(
146140
signInMethod = authConfiguration.signInMethod,
147141
onSubmit = onSubmit,
148142
onMoveTo = onMoveTo

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ import androidx.compose.ui.unit.dp
1616
import com.amplifyframework.ui.authenticator.R
1717
import com.amplifyframework.ui.authenticator.SignInSelectAuthFactorState
1818
import com.amplifyframework.ui.authenticator.auth.toFieldKey
19-
import com.amplifyframework.ui.authenticator.enums.AuthFactor
19+
import com.amplifyframework.ui.authenticator.data.AuthFactor
20+
import com.amplifyframework.ui.authenticator.data.containsPassword
2021
import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
21-
import com.amplifyframework.ui.authenticator.enums.containsPassword
2222
import com.amplifyframework.ui.authenticator.forms.FieldKey
2323
import com.amplifyframework.ui.authenticator.states.getPasswordFactor
2424
import com.amplifyframework.ui.authenticator.states.signInMethod
@@ -33,7 +33,7 @@ fun SignInSelectAuthFactor(
3333
headerContent: @Composable (SignInSelectAuthFactorState) -> Unit = {
3434
AuthenticatorTitle(stringResource(R.string.amplify_ui_authenticator_title_select_factor))
3535
},
36-
footerContent: @Composable (SignInSelectAuthFactorState) -> Unit = { SignInSelectFactorFooter(it) }
36+
footerContent: @Composable (SignInSelectAuthFactorState) -> Unit = { SignInSelectAuthFactorFooter(it) }
3737
) {
3838
Column(
3939
modifier = modifier.fillMaxWidth().padding(horizontal = 16.dp)
@@ -79,10 +79,11 @@ fun SignInSelectAuthFactor(
7979
}
8080

8181
@Composable
82-
fun SignInSelectFactorFooter(state: SignInSelectAuthFactorState, modifier: Modifier = Modifier) = BackToSignInFooter(
83-
modifier = modifier,
84-
onClickBackToSignIn = { state.moveTo(AuthenticatorStep.SignIn) }
85-
)
82+
fun SignInSelectAuthFactorFooter(state: SignInSelectAuthFactorState, modifier: Modifier = Modifier) =
83+
BackToSignInFooter(
84+
modifier = modifier,
85+
onClickBackToSignIn = { state.moveTo(AuthenticatorStep.SignIn) }
86+
)
8687

8788
@Composable
8889
private fun AuthFactorButton(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
package com.amplifyframework.ui.authenticator.ui
1717

18-
import com.amplifyframework.ui.authenticator.enums.AuthFactor
18+
import com.amplifyframework.ui.authenticator.data.AuthFactor
1919
import com.amplifyframework.ui.authenticator.forms.FieldKey
2020

2121
@Suppress("ConstPropertyName")

authenticator/src/main/java/com/amplifyframework/ui/authenticator/util/AuthProvider.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ import kotlinx.coroutines.flow.callbackFlow
5252
* An abstraction of the Amplify.Auth API that allows us to use coroutines with no exceptions
5353
*/
5454
internal interface AuthProvider {
55-
suspend fun signIn(username: String, password: String): AmplifyResult<AuthSignInResult>
55+
suspend fun signIn(username: String, password: String?): AmplifyResult<AuthSignInResult>
5656

5757
suspend fun confirmSignIn(challengeResponse: String): AmplifyResult<AuthSignInResult>
5858

59-
suspend fun signUp(username: String, password: String, options: AuthSignUpOptions): AmplifyResult<AuthSignUpResult>
59+
suspend fun signUp(username: String, password: String?, options: AuthSignUpOptions): AmplifyResult<AuthSignUpResult>
6060

6161
suspend fun confirmSignUp(username: String, code: String): AmplifyResult<AuthSignUpResult>
6262

@@ -106,7 +106,7 @@ internal class RealAuthProvider : AuthProvider {
106106
cognitoPlugin?.addToUserAgent(AWSCognitoAuthMetadataType.Authenticator, BuildConfig.VERSION_NAME)
107107
}
108108

109-
override suspend fun signIn(username: String, password: String) = suspendCoroutine { continuation ->
109+
override suspend fun signIn(username: String, password: String?) = suspendCoroutine { continuation ->
110110
Amplify.Auth.signIn(
111111
username,
112112
password,
@@ -123,7 +123,7 @@ internal class RealAuthProvider : AuthProvider {
123123
)
124124
}
125125

126-
override suspend fun signUp(username: String, password: String, options: AuthSignUpOptions) =
126+
override suspend fun signUp(username: String, password: String?, options: AuthSignUpOptions) =
127127
suspendCoroutine { continuation ->
128128
Amplify.Auth.signUp(
129129
username,

0 commit comments

Comments
 (0)