Skip to content

Commit 4d8ca6a

Browse files
committed
fixes
1 parent b1d7bf1 commit 4d8ca6a

File tree

2 files changed

+67
-112
lines changed

2 files changed

+67
-112
lines changed

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

Lines changed: 65 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,14 @@
1414

1515
package com.firebase.ui.auth.compose.mfa
1616

17-
import android.app.Activity
17+
import com.firebase.ui.auth.compose.configuration.auth_provider.AuthProvider
1818
import com.google.firebase.auth.FirebaseAuth
1919
import com.google.firebase.auth.FirebaseUser
2020
import com.google.firebase.auth.MultiFactorAssertion
2121
import com.google.firebase.auth.PhoneAuthCredential
22-
import com.google.firebase.auth.PhoneAuthOptions
2322
import com.google.firebase.auth.PhoneAuthProvider
2423
import com.google.firebase.auth.PhoneMultiFactorGenerator
2524
import kotlinx.coroutines.tasks.await
26-
import java.util.concurrent.TimeUnit
27-
import kotlin.coroutines.resume
28-
import kotlin.coroutines.resumeWithException
29-
import kotlin.coroutines.suspendCoroutine
3025

3126
/**
3227
* Handler for SMS multi-factor authentication enrollment.
@@ -37,9 +32,12 @@ import kotlin.coroutines.suspendCoroutine
3732
* - Verifying SMS codes entered by users
3833
* - Finalizing enrollment with Firebase Authentication
3934
*
35+
* This handler uses the existing [AuthProvider.Phone.verifyPhoneNumberAwait] infrastructure
36+
* for sending and verifying SMS codes, ensuring consistency with the primary phone auth flow.
37+
*
4038
* **Usage:**
4139
* ```kotlin
42-
* val handler = SmsEnrollmentHandler(auth, user, activity)
40+
* val handler = SmsEnrollmentHandler(auth, user)
4341
*
4442
* // Step 1: Send verification code
4543
* val session = handler.sendVerificationCode("+1234567890")
@@ -57,15 +55,23 @@ import kotlin.coroutines.suspendCoroutine
5755
*
5856
* @property auth The [FirebaseAuth] instance
5957
* @property user The [FirebaseUser] to enroll in SMS MFA
60-
* @property activity The [Activity] context required for phone authentication
6158
*
6259
* @since 10.0.0
60+
* @see TotpEnrollmentHandler
61+
* @see AuthProvider.Phone.verifyPhoneNumberAwait
6362
*/
6463
class SmsEnrollmentHandler(
6564
private val auth: FirebaseAuth,
66-
private val user: FirebaseUser,
67-
private val activity: Activity
65+
private val user: FirebaseUser
6866
) {
67+
private val phoneProvider = AuthProvider.Phone(
68+
defaultNumber = null,
69+
defaultCountryCode = null,
70+
allowedCountries = null,
71+
smsCodeLength = SMS_CODE_LENGTH,
72+
timeout = VERIFICATION_TIMEOUT_SECONDS,
73+
isInstantVerificationEnabled = true
74+
)
6975
/**
7076
* Sends an SMS verification code to the specified phone number.
7177
*
@@ -89,53 +95,32 @@ class SmsEnrollmentHandler(
8995
"Phone number must be in E.164 format (e.g., +1234567890)"
9096
}
9197

92-
// Get the multi-factor session
9398
val multiFactorSession = user.multiFactor.session.await()
94-
95-
return suspendCoroutine { continuation ->
96-
val callbacks = object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
97-
override fun onVerificationCompleted(credential: PhoneAuthCredential) {
98-
// Auto-verification succeeded - create session with credential
99-
// Note: Auto-verification rarely happens for MFA enrollment
100-
continuation.resume(
101-
SmsEnrollmentSession(
102-
verificationId = credential.smsCode ?: "",
103-
phoneNumber = phoneNumber,
104-
forceResendingToken = null,
105-
sentAt = System.currentTimeMillis(),
106-
autoVerifiedCredential = credential
107-
)
108-
)
109-
}
110-
111-
override fun onVerificationFailed(e: com.google.firebase.FirebaseException) {
112-
continuation.resumeWithException(e)
113-
}
114-
115-
override fun onCodeSent(
116-
verificationId: String,
117-
token: PhoneAuthProvider.ForceResendingToken
118-
) {
119-
continuation.resume(
120-
SmsEnrollmentSession(
121-
verificationId = verificationId,
122-
phoneNumber = phoneNumber,
123-
forceResendingToken = token,
124-
sentAt = System.currentTimeMillis()
125-
)
126-
)
127-
}
99+
val result = phoneProvider.verifyPhoneNumberAwait(
100+
auth = auth,
101+
phoneNumber = phoneNumber,
102+
multiFactorSession = multiFactorSession,
103+
forceResendingToken = null
104+
)
105+
106+
return when (result) {
107+
is AuthProvider.Phone.VerifyPhoneNumberResult.AutoVerified -> {
108+
SmsEnrollmentSession(
109+
verificationId = "", // Not needed when auto-verified
110+
phoneNumber = phoneNumber,
111+
forceResendingToken = null,
112+
sentAt = System.currentTimeMillis(),
113+
autoVerifiedCredential = result.credential
114+
)
115+
}
116+
is AuthProvider.Phone.VerifyPhoneNumberResult.NeedsManualVerification -> {
117+
SmsEnrollmentSession(
118+
verificationId = result.verificationId,
119+
phoneNumber = phoneNumber,
120+
forceResendingToken = result.token,
121+
sentAt = System.currentTimeMillis()
122+
)
128123
}
129-
130-
val options = PhoneAuthOptions.newBuilder(auth)
131-
.setPhoneNumber(phoneNumber)
132-
.setTimeout(VERIFICATION_TIMEOUT_SECONDS, TimeUnit.SECONDS)
133-
.setActivity(activity)
134-
.setCallbacks(callbacks)
135-
.setMultiFactorSession(multiFactorSession)
136-
.build()
137-
138-
PhoneAuthProvider.verifyPhoneNumber(options)
139124
}
140125
}
141126

@@ -157,52 +142,32 @@ class SmsEnrollmentHandler(
157142
"Cannot resend code without a force resending token"
158143
}
159144

160-
// Get a fresh multi-factor session
161145
val multiFactorSession = user.multiFactor.session.await()
162-
163-
return suspendCoroutine { continuation ->
164-
val callbacks = object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
165-
override fun onVerificationCompleted(credential: PhoneAuthCredential) {
166-
continuation.resume(
167-
SmsEnrollmentSession(
168-
verificationId = credential.smsCode ?: session.verificationId,
169-
phoneNumber = session.phoneNumber,
170-
forceResendingToken = session.forceResendingToken,
171-
sentAt = System.currentTimeMillis(),
172-
autoVerifiedCredential = credential
173-
)
174-
)
175-
}
176-
177-
override fun onVerificationFailed(e: com.google.firebase.FirebaseException) {
178-
continuation.resumeWithException(e)
179-
}
180-
181-
override fun onCodeSent(
182-
verificationId: String,
183-
token: PhoneAuthProvider.ForceResendingToken
184-
) {
185-
continuation.resume(
186-
SmsEnrollmentSession(
187-
verificationId = verificationId,
188-
phoneNumber = session.phoneNumber,
189-
forceResendingToken = token,
190-
sentAt = System.currentTimeMillis()
191-
)
192-
)
193-
}
146+
val result = phoneProvider.verifyPhoneNumberAwait(
147+
auth = auth,
148+
phoneNumber = session.phoneNumber,
149+
multiFactorSession = multiFactorSession,
150+
forceResendingToken = session.forceResendingToken
151+
)
152+
153+
return when (result) {
154+
is AuthProvider.Phone.VerifyPhoneNumberResult.AutoVerified -> {
155+
SmsEnrollmentSession(
156+
verificationId = "", // Not needed when auto-verified
157+
phoneNumber = session.phoneNumber,
158+
forceResendingToken = session.forceResendingToken,
159+
sentAt = System.currentTimeMillis(),
160+
autoVerifiedCredential = result.credential
161+
)
162+
}
163+
is AuthProvider.Phone.VerifyPhoneNumberResult.NeedsManualVerification -> {
164+
SmsEnrollmentSession(
165+
verificationId = result.verificationId,
166+
phoneNumber = session.phoneNumber,
167+
forceResendingToken = result.token,
168+
sentAt = System.currentTimeMillis()
169+
)
194170
}
195-
196-
val options = PhoneAuthOptions.newBuilder(auth)
197-
.setPhoneNumber(session.phoneNumber)
198-
.setTimeout(VERIFICATION_TIMEOUT_SECONDS, TimeUnit.SECONDS)
199-
.setActivity(activity)
200-
.setCallbacks(callbacks)
201-
.setMultiFactorSession(multiFactorSession)
202-
.setForceResendingToken(session.forceResendingToken)
203-
.build()
204-
205-
PhoneAuthProvider.verifyPhoneNumber(options)
206171
}
207172
}
208173

@@ -229,15 +194,10 @@ class SmsEnrollmentHandler(
229194
"Verification code must be 6 digits"
230195
}
231196

232-
// Use auto-verified credential if available, otherwise create from code
233197
val credential = session.autoVerifiedCredential
234198
?: PhoneAuthProvider.getCredential(session.verificationId, verificationCode)
235199

236-
// Create the multi-factor assertion for enrollment
237-
val multiFactorAssertion: MultiFactorAssertion =
238-
PhoneMultiFactorGenerator.getAssertion(credential)
239-
240-
// Enroll the user with the SMS factor
200+
val multiFactorAssertion = PhoneMultiFactorGenerator.getAssertion(credential)
241201
user.multiFactor.enroll(multiFactorAssertion, displayName).await()
242202
}
243203

auth/src/test/java/com/firebase/ui/auth/compose/mfa/SmsEnrollmentHandlerTest.kt

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
package com.firebase.ui.auth.compose.mfa
1616

17-
import android.app.Activity
1817
import com.google.firebase.auth.FirebaseAuth
1918
import com.google.firebase.auth.FirebaseUser
2019
import com.google.firebase.auth.MultiFactor
@@ -29,7 +28,6 @@ import org.junit.runner.RunWith
2928
import org.mockito.Mock
3029
import org.mockito.Mockito.`when`
3130
import org.mockito.MockitoAnnotations
32-
import org.robolectric.Robolectric
3331
import org.robolectric.RobolectricTestRunner
3432
import org.robolectric.annotation.Config
3533

@@ -50,16 +48,13 @@ class SmsEnrollmentHandlerTest {
5048
private lateinit var mockUser: FirebaseUser
5149
@Mock
5250
private lateinit var mockMultiFactor: MultiFactor
53-
@Mock
54-
private lateinit var mockActivity: Activity
5551
private lateinit var handler: SmsEnrollmentHandler
5652

5753
@Before
5854
fun setUp() {
5955
MockitoAnnotations.openMocks(this)
60-
mockActivity = Robolectric.buildActivity(Activity::class.java).create().get()
6156
`when`(mockUser.multiFactor).thenReturn(mockMultiFactor)
62-
handler = SmsEnrollmentHandler(mockAuth, mockUser, mockActivity)
57+
handler = SmsEnrollmentHandler(mockAuth, mockUser)
6358
}
6459

6560
// isValidCodeFormat tests
@@ -158,7 +153,7 @@ class SmsEnrollmentHandlerTest {
158153
@Test
159154
fun `handler is created with correct auth and user references`() {
160155
// Verify handler can be instantiated
161-
val newHandler = SmsEnrollmentHandler(mockAuth, mockUser, mockActivity)
156+
val newHandler = SmsEnrollmentHandler(mockAuth, mockUser)
162157
// Basic smoke test - if we get here, construction succeeded
163158
assertTrue(newHandler.isValidCodeFormat("123456"))
164159
}

0 commit comments

Comments
 (0)