@@ -24,13 +24,17 @@ import {
2424 EmailAuthProvider ,
2525 linkWithCredential ,
2626 PhoneAuthProvider ,
27+ TotpMultiFactorGenerator ,
28+ multiFactor ,
2729 type ActionCodeSettings ,
2830 type ApplicationVerifier ,
2931 type AuthProvider ,
3032 type UserCredential ,
3133 type AuthCredential ,
3234 type TotpSecret ,
33- type PhoneInfoOptions ,
35+ type MultiFactorAssertion ,
36+ type MultiFactorUser ,
37+ type MultiFactorInfo ,
3438} from "firebase/auth" ;
3539import QRCode from "qrcode-generator" ;
3640import { type FirebaseUI } from "./config" ;
@@ -54,13 +58,18 @@ async function handlePendingCredential(_ui: FirebaseUI, user: UserCredential): P
5458 }
5559}
5660
61+ function setPendingState ( ui : FirebaseUI ) {
62+ ui . setRedirectError ( undefined ) ;
63+ ui . setState ( "pending" ) ;
64+ }
65+
5766export async function signInWithEmailAndPassword (
5867 ui : FirebaseUI ,
5968 email : string ,
6069 password : string
6170) : Promise < UserCredential > {
6271 try {
63- ui . setState ( "pending" ) ;
72+ setPendingState ( ui ) ;
6473 const credential = EmailAuthProvider . credential ( email , password ) ;
6574
6675 if ( hasBehavior ( ui , "autoUpgradeAnonymousCredential" ) ) {
@@ -87,7 +96,7 @@ export async function createUserWithEmailAndPassword(
8796 displayName ?: string
8897) : Promise < UserCredential > {
8998 try {
90- ui . setState ( "pending" ) ;
99+ setPendingState ( ui ) ;
91100 const credential = EmailAuthProvider . credential ( email , password ) ;
92101
93102 if ( hasBehavior ( ui , "requireDisplayName" ) && ! displayName ) {
@@ -122,13 +131,38 @@ export async function createUserWithEmailAndPassword(
122131
123132export async function verifyPhoneNumber (
124133 ui : FirebaseUI ,
125- phoneNumber : PhoneInfoOptions | string ,
126- appVerifier : ApplicationVerifier
134+ phoneNumber : string ,
135+ appVerifier : ApplicationVerifier ,
136+ mfaUser ?: MultiFactorUser ,
137+ mfaHint ?: MultiFactorInfo
127138) : Promise < string > {
128139 try {
129- ui . setState ( "pending" ) ;
140+ setPendingState ( ui ) ;
130141 const provider = new PhoneAuthProvider ( ui . auth ) ;
131- return await provider . verifyPhoneNumber ( phoneNumber , appVerifier ) ;
142+
143+ if ( mfaHint && ui . multiFactorResolver ) {
144+ // MFA assertion flow
145+ return await provider . verifyPhoneNumber (
146+ {
147+ multiFactorHint : mfaHint ,
148+ session : ui . multiFactorResolver . session ,
149+ } ,
150+ appVerifier
151+ ) ;
152+ } else if ( mfaUser ) {
153+ // MFA enrollment flow
154+ const session = await mfaUser . getSession ( ) ;
155+ return await provider . verifyPhoneNumber (
156+ {
157+ phoneNumber,
158+ session,
159+ } ,
160+ appVerifier
161+ ) ;
162+ } else {
163+ // Regular phone auth flow
164+ return await provider . verifyPhoneNumber ( phoneNumber , appVerifier ) ;
165+ }
132166 } catch ( error ) {
133167 handleFirebaseError ( ui , error ) ;
134168 } finally {
@@ -142,7 +176,7 @@ export async function confirmPhoneNumber(
142176 verificationCode : string
143177) : Promise < UserCredential > {
144178 try {
145- ui . setState ( "pending" ) ;
179+ setPendingState ( ui ) ;
146180 const currentUser = ui . auth . currentUser ;
147181 const credential = PhoneAuthProvider . credential ( verificationId , verificationCode ) ;
148182
@@ -165,7 +199,7 @@ export async function confirmPhoneNumber(
165199
166200export async function sendPasswordResetEmail ( ui : FirebaseUI , email : string ) : Promise < void > {
167201 try {
168- ui . setState ( "pending" ) ;
202+ setPendingState ( ui ) ;
169203 await _sendPasswordResetEmail ( ui . auth , email ) ;
170204 } catch ( error ) {
171205 handleFirebaseError ( ui , error ) ;
@@ -176,7 +210,7 @@ export async function sendPasswordResetEmail(ui: FirebaseUI, email: string): Pro
176210
177211export async function sendSignInLinkToEmail ( ui : FirebaseUI , email : string ) : Promise < void > {
178212 try {
179- ui . setState ( "pending" ) ;
213+ setPendingState ( ui ) ;
180214 const actionCodeSettings = {
181215 url : window . location . href ,
182216 // TODO(ehesp): Check this...
@@ -200,7 +234,7 @@ export async function signInWithEmailLink(ui: FirebaseUI, email: string, link: s
200234
201235export async function signInWithCredential ( ui : FirebaseUI , credential : AuthCredential ) : Promise < UserCredential > {
202236 try {
203- ui . setState ( "pending" ) ;
237+ setPendingState ( ui ) ;
204238 if ( hasBehavior ( ui , "autoUpgradeAnonymousCredential" ) ) {
205239 const userCredential = await getBehavior ( ui , "autoUpgradeAnonymousCredential" ) ( ui , credential ) ;
206240
@@ -222,7 +256,7 @@ export async function signInWithCredential(ui: FirebaseUI, credential: AuthCrede
222256
223257export async function signInAnonymously ( ui : FirebaseUI ) : Promise < UserCredential > {
224258 try {
225- ui . setState ( "pending" ) ;
259+ setPendingState ( ui ) ;
226260 const result = await _signInAnonymously ( ui . auth ) ;
227261 return handlePendingCredential ( ui , result ) ;
228262 } catch ( error ) {
@@ -234,7 +268,7 @@ export async function signInAnonymously(ui: FirebaseUI): Promise<UserCredential>
234268
235269export async function signInWithProvider ( ui : FirebaseUI , provider : AuthProvider ) : Promise < UserCredential | never > {
236270 try {
237- ui . setState ( "pending" ) ;
271+ setPendingState ( ui ) ;
238272 if ( hasBehavior ( ui , "autoUpgradeAnonymousProvider" ) ) {
239273 const credential = await getBehavior ( ui , "autoUpgradeAnonymousProvider" ) ( ui , provider ) ;
240274
@@ -267,7 +301,7 @@ export async function completeEmailLinkSignIn(ui: FirebaseUI, currentUrl: string
267301 const email = window . localStorage . getItem ( "emailForSignIn" ) ;
268302 if ( ! email ) return null ;
269303
270- ui . setState ( "pending" ) ;
304+ setPendingState ( ui ) ;
271305 const result = await signInWithEmailLink ( ui , email , currentUrl ) ;
272306 return handlePendingCredential ( ui , result ) ;
273307 } catch ( error ) {
@@ -292,3 +326,44 @@ export function generateTotpQrCode(ui: FirebaseUI, secret: TotpSecret, accountNa
292326 qr . make ( ) ;
293327 return qr . createDataURL ( ) ;
294328}
329+
330+ export async function signInWithMultiFactorAssertion ( ui : FirebaseUI , assertion : MultiFactorAssertion ) {
331+ try {
332+ setPendingState ( ui ) ;
333+ const result = await ui . multiFactorResolver ?. resolveSignIn ( assertion ) ;
334+ ui . setMultiFactorResolver ( undefined ) ;
335+ return result ;
336+ } catch ( error ) {
337+ handleFirebaseError ( ui , error ) ;
338+ } finally {
339+ ui . setState ( "idle" ) ;
340+ }
341+ }
342+
343+ export async function enrollWithMultiFactorAssertion (
344+ ui : FirebaseUI ,
345+ assertion : MultiFactorAssertion ,
346+ displayName ?: string
347+ ) : Promise < void > {
348+ try {
349+ setPendingState ( ui ) ;
350+ await multiFactor ( ui . auth . currentUser ! ) . enroll ( assertion , displayName ) ;
351+ } catch ( error ) {
352+ handleFirebaseError ( ui , error ) ;
353+ } finally {
354+ ui . setState ( "idle" ) ;
355+ }
356+ }
357+
358+ export async function generateTotpSecret ( ui : FirebaseUI ) : Promise < TotpSecret > {
359+ try {
360+ setPendingState ( ui ) ;
361+ const mfaUser = multiFactor ( ui . auth . currentUser ! ) ;
362+ const session = await mfaUser . getSession ( ) ;
363+ return await TotpMultiFactorGenerator . generateSecret ( session ) ;
364+ } catch ( error ) {
365+ handleFirebaseError ( ui , error ) ;
366+ } finally {
367+ ui . setState ( "idle" ) ;
368+ }
369+ }
0 commit comments