@@ -25,13 +25,17 @@ import {
2525 EmailAuthProvider ,
2626 linkWithCredential ,
2727 PhoneAuthProvider ,
28+ TotpMultiFactorGenerator ,
29+ multiFactor ,
2830 type ActionCodeSettings ,
2931 type ApplicationVerifier ,
3032 type AuthProvider ,
3133 type UserCredential ,
3234 type AuthCredential ,
3335 type TotpSecret ,
34- type PhoneInfoOptions ,
36+ type MultiFactorAssertion ,
37+ type MultiFactorUser ,
38+ type MultiFactorInfo ,
3539} from "firebase/auth" ;
3640import QRCode from "qrcode-generator" ;
3741import { type FirebaseUI } from "./config" ;
@@ -55,13 +59,18 @@ async function handlePendingCredential(_ui: FirebaseUI, user: UserCredential): P
5559 }
5660}
5761
62+ function setPendingState ( ui : FirebaseUI ) {
63+ ui . setRedirectError ( undefined ) ;
64+ ui . setState ( "pending" ) ;
65+ }
66+
5867export async function signInWithEmailAndPassword (
5968 ui : FirebaseUI ,
6069 email : string ,
6170 password : string
6271) : Promise < UserCredential > {
6372 try {
64- ui . setState ( "pending" ) ;
73+ setPendingState ( ui ) ;
6574 const credential = EmailAuthProvider . credential ( email , password ) ;
6675
6776 if ( hasBehavior ( ui , "autoUpgradeAnonymousCredential" ) ) {
@@ -88,7 +97,7 @@ export async function createUserWithEmailAndPassword(
8897 displayName ?: string
8998) : Promise < UserCredential > {
9099 try {
91- ui . setState ( "pending" ) ;
100+ setPendingState ( ui ) ;
92101 const credential = EmailAuthProvider . credential ( email , password ) ;
93102
94103 if ( hasBehavior ( ui , "requireDisplayName" ) && ! displayName ) {
@@ -123,13 +132,38 @@ export async function createUserWithEmailAndPassword(
123132
124133export async function verifyPhoneNumber (
125134 ui : FirebaseUI ,
126- phoneNumber : PhoneInfoOptions | string ,
127- appVerifier : ApplicationVerifier
135+ phoneNumber : string ,
136+ appVerifier : ApplicationVerifier ,
137+ mfaUser ?: MultiFactorUser ,
138+ mfaHint ?: MultiFactorInfo
128139) : Promise < string > {
129140 try {
130- ui . setState ( "pending" ) ;
141+ setPendingState ( ui ) ;
131142 const provider = new PhoneAuthProvider ( ui . auth ) ;
132- return await provider . verifyPhoneNumber ( phoneNumber , appVerifier ) ;
143+
144+ if ( mfaHint && ui . multiFactorResolver ) {
145+ // MFA assertion flow
146+ return await provider . verifyPhoneNumber (
147+ {
148+ multiFactorHint : mfaHint ,
149+ session : ui . multiFactorResolver . session ,
150+ } ,
151+ appVerifier
152+ ) ;
153+ } else if ( mfaUser ) {
154+ // MFA enrollment flow
155+ const session = await mfaUser . getSession ( ) ;
156+ return await provider . verifyPhoneNumber (
157+ {
158+ phoneNumber,
159+ session,
160+ } ,
161+ appVerifier
162+ ) ;
163+ } else {
164+ // Regular phone auth flow
165+ return await provider . verifyPhoneNumber ( phoneNumber , appVerifier ) ;
166+ }
133167 } catch ( error ) {
134168 handleFirebaseError ( ui , error ) ;
135169 } finally {
@@ -143,7 +177,7 @@ export async function confirmPhoneNumber(
143177 verificationCode : string
144178) : Promise < UserCredential > {
145179 try {
146- ui . setState ( "pending" ) ;
180+ setPendingState ( ui ) ;
147181 const currentUser = ui . auth . currentUser ;
148182 const credential = PhoneAuthProvider . credential ( verificationId , verificationCode ) ;
149183
@@ -166,7 +200,7 @@ export async function confirmPhoneNumber(
166200
167201export async function sendPasswordResetEmail ( ui : FirebaseUI , email : string ) : Promise < void > {
168202 try {
169- ui . setState ( "pending" ) ;
203+ setPendingState ( ui ) ;
170204 await _sendPasswordResetEmail ( ui . auth , email ) ;
171205 } catch ( error ) {
172206 handleFirebaseError ( ui , error ) ;
@@ -177,7 +211,7 @@ export async function sendPasswordResetEmail(ui: FirebaseUI, email: string): Pro
177211
178212export async function sendSignInLinkToEmail ( ui : FirebaseUI , email : string ) : Promise < void > {
179213 try {
180- ui . setState ( "pending" ) ;
214+ setPendingState ( ui ) ;
181215 const actionCodeSettings = {
182216 url : window . location . href ,
183217 // TODO(ehesp): Check this...
@@ -201,7 +235,7 @@ export async function signInWithEmailLink(ui: FirebaseUI, email: string, link: s
201235
202236export async function signInWithCredential ( ui : FirebaseUI , credential : AuthCredential ) : Promise < UserCredential > {
203237 try {
204- ui . setState ( "pending" ) ;
238+ setPendingState ( ui ) ;
205239 if ( hasBehavior ( ui , "autoUpgradeAnonymousCredential" ) ) {
206240 const userCredential = await getBehavior ( ui , "autoUpgradeAnonymousCredential" ) ( ui , credential ) ;
207241
@@ -235,7 +269,7 @@ export async function signInWithCustomToken(ui: FirebaseUI, customToken: string)
235269
236270export async function signInAnonymously ( ui : FirebaseUI ) : Promise < UserCredential > {
237271 try {
238- ui . setState ( "pending" ) ;
272+ setPendingState ( ui ) ;
239273 const result = await _signInAnonymously ( ui . auth ) ;
240274 return handlePendingCredential ( ui , result ) ;
241275 } catch ( error ) {
@@ -247,7 +281,7 @@ export async function signInAnonymously(ui: FirebaseUI): Promise<UserCredential>
247281
248282export async function signInWithProvider ( ui : FirebaseUI , provider : AuthProvider ) : Promise < UserCredential | never > {
249283 try {
250- ui . setState ( "pending" ) ;
284+ setPendingState ( ui ) ;
251285 if ( hasBehavior ( ui , "autoUpgradeAnonymousProvider" ) ) {
252286 const credential = await getBehavior ( ui , "autoUpgradeAnonymousProvider" ) ( ui , provider ) ;
253287
@@ -280,7 +314,7 @@ export async function completeEmailLinkSignIn(ui: FirebaseUI, currentUrl: string
280314 const email = window . localStorage . getItem ( "emailForSignIn" ) ;
281315 if ( ! email ) return null ;
282316
283- ui . setState ( "pending" ) ;
317+ setPendingState ( ui ) ;
284318 const result = await signInWithEmailLink ( ui , email , currentUrl ) ;
285319 return handlePendingCredential ( ui , result ) ;
286320 } catch ( error ) {
@@ -305,3 +339,44 @@ export function generateTotpQrCode(ui: FirebaseUI, secret: TotpSecret, accountNa
305339 qr . make ( ) ;
306340 return qr . createDataURL ( ) ;
307341}
342+
343+ export async function signInWithMultiFactorAssertion ( ui : FirebaseUI , assertion : MultiFactorAssertion ) {
344+ try {
345+ setPendingState ( ui ) ;
346+ const result = await ui . multiFactorResolver ?. resolveSignIn ( assertion ) ;
347+ ui . setMultiFactorResolver ( undefined ) ;
348+ return result ;
349+ } catch ( error ) {
350+ handleFirebaseError ( ui , error ) ;
351+ } finally {
352+ ui . setState ( "idle" ) ;
353+ }
354+ }
355+
356+ export async function enrollWithMultiFactorAssertion (
357+ ui : FirebaseUI ,
358+ assertion : MultiFactorAssertion ,
359+ displayName ?: string
360+ ) : Promise < void > {
361+ try {
362+ setPendingState ( ui ) ;
363+ await multiFactor ( ui . auth . currentUser ! ) . enroll ( assertion , displayName ) ;
364+ } catch ( error ) {
365+ handleFirebaseError ( ui , error ) ;
366+ } finally {
367+ ui . setState ( "idle" ) ;
368+ }
369+ }
370+
371+ export async function generateTotpSecret ( ui : FirebaseUI ) : Promise < TotpSecret > {
372+ try {
373+ setPendingState ( ui ) ;
374+ const mfaUser = multiFactor ( ui . auth . currentUser ! ) ;
375+ const session = await mfaUser . getSession ( ) ;
376+ return await TotpMultiFactorGenerator . generateSecret ( session ) ;
377+ } catch ( error ) {
378+ handleFirebaseError ( ui , error ) ;
379+ } finally {
380+ ui . setState ( "idle" ) ;
381+ }
382+ }
0 commit comments