Skip to content

Commit ba697de

Browse files
authored
feat(auth)!: decouple SMS multi-factor configuration from the phone sign-in provider (#2483)
1 parent 9244eb8 commit ba697de

26 files changed

Lines changed: 1202 additions & 40 deletions

app/src/main/java/com/firebaseui/android/demo/auth/AuthFlowControllerDemoActivity.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ class AuthFlowControllerDemoActivity : ComponentActivity() {
103103
defaultNumber = null,
104104
defaultCountryCode = null,
105105
allowedCountries = emptyList(),
106-
smsCodeLength = 6,
107106
timeout = 120L,
108107
isInstantVerificationEnabled = true
109108
),

app/src/main/java/com/firebaseui/android/demo/auth/HighLevelApiDemoActivity.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,6 @@ class HighLevelApiDemoActivity : ComponentActivity() {
153153
defaultNumber = null,
154154
defaultCountryCode = null,
155155
allowedCountries = emptyList(),
156-
smsCodeLength = 6,
157156
timeout = 120L,
158157
isInstantVerificationEnabled = true
159158
)

app/src/main/java/com/firebaseui/android/demo/auth/PhoneAuthSlotDemoActivity.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ class PhoneAuthSlotDemoActivity : ComponentActivity() {
9595
defaultNumber = null,
9696
defaultCountryCode = "US",
9797
allowedCountries = emptyList(),
98-
smsCodeLength = 6,
9998
timeout = 60L,
10099
isInstantVerificationEnabled = true
101100
)

auth/README.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -454,9 +454,6 @@ val phoneProvider = AuthProvider.Phone(
454454
// Optional: Allowed countries
455455
allowedCountries = listOf("US", "CA", "GB"),
456456

457-
// Optional: SMS code length (default: 6)
458-
smsCodeLength = 6,
459-
460457
// Optional: Timeout for SMS delivery in seconds (default: 60)
461458
timeout = 60L,
462459

@@ -1082,7 +1079,12 @@ val mfaConfig = MfaConfiguration(
10821079
allowedFactors = listOf(MfaFactor.Sms, MfaFactor.Totp),
10831080

10841081
// Optional: Require MFA enrollment (default: false)
1085-
requireEnrollment = false
1082+
requireEnrollment = false,
1083+
1084+
// Optional: restrict the SMS enrollment step's country selector, as ISO 3166-1 alpha-2
1085+
// codes (default: null, no restriction). Independent of the phone sign-in provider's own
1086+
// allowedCountries — an SMS second factor is configured separately from phone sign-in.
1087+
allowedCountries = listOf("US", "CA", "GB")
10861088
)
10871089

10881090
val configuration = authUIConfiguration {
@@ -1112,10 +1114,14 @@ fun MfaEnrollmentFlow() {
11121114

11131115
if (currentUser != null) {
11141116
val mfaConfig = MfaConfiguration(
1115-
allowedFactors = listOf(MfaFactor.Sms, MfaFactor.Totp)
1117+
allowedFactors = listOf(MfaFactor.Sms, MfaFactor.Totp),
1118+
allowedCountries = listOf("US", "CA", "GB")
11161119
)
11171120
val backStack = rememberNavBackStack(MfaStepKey(MfaEnrollmentStep.SelectFactor))
1118-
val flowState = rememberMfaEnrollmentFlowState()
1121+
// Pass the restriction so the SMS step opens on a country the selector will offer. The
1122+
// screen also reconciles this itself, so a host that forgets cannot end up sending to an
1123+
// unpermitted dial code.
1124+
val flowState = rememberMfaEnrollmentFlowState(mfaConfig.allowedCountries)
11191125

11201126
NavDisplay(
11211127
backStack = backStack,

auth/src/main/java/com/firebase/ui/auth/configuration/MfaConfiguration.kt

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
package com.firebase.ui.auth.configuration
1616

17+
import com.firebase.ui.auth.util.CountryUtils
18+
1719
/**
1820
* Configuration class for Multi-Factor Authentication (MFA) enrollment and verification behavior.
1921
*
@@ -25,14 +27,29 @@ package com.firebase.ui.auth.configuration
2527
* @property requireEnrollment Whether MFA enrollment is mandatory for all users.
2628
* When true, users must enroll in at least one MFA factor.
2729
* Defaults to false.
30+
* @property allowedCountries ISO 3166-1 alpha-2 country codes the [MfaFactor.Sms] enrollment step
31+
* restricts its country selector to, or `null` for no restriction. Dial
32+
* codes are rejected: the filter behind this matches alpha-2 only, so a
33+
* dial code would silently restrict to nothing. Lives here rather
34+
* than on the phone sign-in provider because a second factor is
35+
* configured independently of the first: Firebase enables SMS second
36+
* factors separately from phone sign-in, and phone sign-in cannot carry
37+
* a second factor at all. Defaults to null.
2838
*/
2939
class MfaConfiguration(
3040
val allowedFactors: List<MfaFactor> = listOf(MfaFactor.Sms, MfaFactor.Totp),
31-
val requireEnrollment: Boolean = false
41+
val requireEnrollment: Boolean = false,
42+
val allowedCountries: List<String>? = null
3243
) {
3344
init {
3445
require(allowedFactors.isNotEmpty()) {
3546
"At least one MFA factor must be allowed"
3647
}
48+
allowedCountries?.forEach { code ->
49+
require(CountryUtils.findByCountryCode(code) != null) {
50+
"Invalid country code: $code. allowedCountries takes ISO 3166-1 alpha-2 codes " +
51+
"(e.g. 'us', 'GB'). Dial codes are not accepted."
52+
}
53+
}
3754
}
3855
}

auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/AuthProvider.kt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -273,11 +273,6 @@ abstract class AuthProvider(open val providerId: String, open val providerName:
273273
*/
274274
val allowedCountries: List<String>?,
275275

276-
/**
277-
* The expected length of the SMS verification code. Defaults to 6.
278-
*/
279-
val smsCodeLength: Int = 6,
280-
281276
/**
282277
* The timeout in seconds for receiving the SMS. Defaults to 60L.
283278
*/

auth/src/main/java/com/firebase/ui/auth/mfa/MfaEnrollmentContentState.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import com.google.firebase.auth.MultiFactorInfo
6969
* @property phoneNumber (Step: [MfaEnrollmentStep.ConfigureSms]) The current value of the phone number input field. Does not include country code prefix.
7070
* @property onPhoneNumberChange (Step: [MfaEnrollmentStep.ConfigureSms]) Callback invoked when the phone number input changes. Receives the new phone number string.
7171
* @property selectedCountry (Step: [MfaEnrollmentStep.ConfigureSms]) The currently selected country for phone number formatting. Contains dial code, country code, and flag.
72+
* @property allowedCountries (Step: [MfaEnrollmentStep.ConfigureSms]) Country codes the selector is restricted to, or `null` for no restriction. Determined by [com.firebase.ui.auth.configuration.MfaConfiguration.allowedCountries].
7273
* @property onCountrySelected (Step: [MfaEnrollmentStep.ConfigureSms]) Callback invoked when the user selects a different country. Receives the new [CountryData].
7374
* @property onSendSmsCodeClick (Step: [MfaEnrollmentStep.ConfigureSms]) Callback to send the SMS verification code to the entered phone number.
7475
*
@@ -119,6 +120,8 @@ data class MfaEnrollmentContentState(
119120

120121
val selectedCountry: CountryData? = null,
121122

123+
val allowedCountries: List<String>? = null,
124+
122125
val onCountrySelected: (CountryData) -> Unit = {},
123126

124127
val onSendSmsCodeClick: () -> Unit = {},

auth/src/main/java/com/firebase/ui/auth/mfa/SmsEnrollmentHandler.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ class SmsEnrollmentHandler(
7272
defaultNumber = null,
7373
defaultCountryCode = null,
7474
allowedCountries = null,
75-
smsCodeLength = SMS_CODE_LENGTH,
7675
timeout = VERIFICATION_TIMEOUT_SECONDS,
7776
isInstantVerificationEnabled = true
7877
)

auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ fun FirebaseAuthScreen(
197197
val lastSuccessfulUserId = remember { mutableStateOf<String?>(null) }
198198
val pendingLinkingCredential = remember { mutableStateOf<AuthCredential?>(null) }
199199
val pendingResolver = remember { mutableStateOf<MultiFactorResolver?>(null) }
200-
val mfaEnrollmentFlowState = rememberMfaEnrollmentFlowState()
200+
val mfaEnrollmentFlowState = rememberMfaEnrollmentFlowState(mfaConfiguration.allowedCountries)
201201
val phoneAuthFlowState = rememberPhoneAuthFlowState(configuration)
202202
val reauthRequest = reauthState?.request
203203
val reauthConfig = reauthRequest?.let { configuration.toReauthConfiguration(it.user) }

auth/src/main/java/com/firebase/ui/auth/ui/screens/mfa/MfaEnrollmentDefaults.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ internal fun DefaultMfaEnrollmentContent(
160160
onPhoneNumberChange = state.onPhoneNumberChange,
161161
onCountrySelected = state.onCountrySelected,
162162
onSendCodeClick = state.onSendSmsCodeClick,
163+
allowedCountries = remember(state.allowedCountries) {
164+
state.allowedCountries?.toSet()
165+
},
163166
title = stringProvider.mfaEnrollmentEnterPhoneNumber
164167
)
165168
}

0 commit comments

Comments
 (0)