1414
1515package com.firebase.ui.auth.compose.mfa
1616
17- import android.app.Activity
17+ import com.firebase.ui.auth.compose.configuration.auth_provider.AuthProvider
1818import com.google.firebase.auth.FirebaseAuth
1919import com.google.firebase.auth.FirebaseUser
2020import com.google.firebase.auth.MultiFactorAssertion
2121import com.google.firebase.auth.PhoneAuthCredential
22- import com.google.firebase.auth.PhoneAuthOptions
2322import com.google.firebase.auth.PhoneAuthProvider
2423import com.google.firebase.auth.PhoneMultiFactorGenerator
2524import 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 */
6463class 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
0 commit comments