Working with multi-factor users

This document shows you how to perform common tasks with Identity Platform users enrolled in multi-factor authentication.

Updating a user's email

Multi-factor users must always have a verified email address. This prevents malicious actors from registering for your app with an email they don't own, and then locking out the real owner by adding a second factor.

To update a user's email, use the verifyBeforeUpdateEmail() method. Unlike updateEmail() , this method requires the user to follow a verification link before Identity Platform updates their email address. For example:

Web version 8

  var 
  
 user 
  
 = 
  
 firebase 
 . 
 auth 
 () 
 . 
 currentUser 
 ; 
 user 
 . 
 verifyBeforeUpdateEmail 
 ( 
 newEmail 
 ) 
 . 
 then 
 ( 
 function 
 () 
  
 { 
  
 // 
  
 Email 
  
 sent 
 . 
  
 // 
  
 User 
  
 must 
  
 click 
  
 the 
  
 email 
  
 link 
  
 before 
  
 the 
  
 email 
  
 is 
  
 updated 
 . 
 }) 
 . 
 catch 
 ( 
 function 
 ( 
 error 
 ) 
  
 { 
  
 // 
  
 An 
  
 error 
  
 happened 
 . 
 }); 
 

Web version 9

  import 
  
 { 
 getAuth 
 , 
 verifyBeforeUpdateEmail 
 } 
 from 
  
 "firebase/auth" 
 ; 
 const 
 auth 
 = 
 getAuth 
 ( 
 firebaseApp 
 ); 
 verifyBeforeUpdateEmail 
 ( 
 auth 
 . 
 currentUser 
 , 
 newEmail 
 ) 
 . 
 then 
 (() 
 = 
> { 
 // 
 Email 
 sent 
 . 
 // 
 User 
 must 
 click 
 the 
 email 
 link 
 before 
 the 
 email 
 is 
 updated 
 . 
 }) 
 . 
 catch 
 (( 
 error 
 ) 
 = 
> { 
 // 
 An 
 error 
 happened 
 . 
 }); 
 

iOS

 let user = Auth.auth().currentUser
user.verifyBeforeUpdateEmail(newEmail, completion: { (error) in
  if error != nil {
    // An error happened.
  }
  // Email sent.
  // User must click the email link before the email is updated.
}) 

Android

  FirebaseUser 
  
 user 
  
 = 
  
 FirebaseAuth 
 . 
 getInstance 
 (). 
 getCurrentUser 
 (); 
 user 
 . 
 verifyBeforeUpdateEmail 
 ( 
 newEmail 
 ) 
  
 . 
 addOnCompleteListener 
 ( 
  
 new 
  
 OnCompleteListener<Void> 
 () 
  
 { 
  
 @Override 
  
 public 
  
 void 
  
 onComplete 
 ( 
 @NonNull 
  
 Task<Void> 
  
 task 
 ) 
  
 { 
  
 if 
  
 ( 
 task 
 . 
 isSuccessful 
 ()) 
  
 { 
  
 // 
  
 Email 
  
 sent 
 . 
  
 // 
  
 User 
  
 must 
  
 click 
  
 the 
  
 email 
  
 link 
  
 before 
  
 the 
  
 email 
  
 is 
  
 updated 
 . 
  
 } 
  
 else 
  
 { 
  
 // 
  
 An 
  
 error 
  
 occurred 
 . 
  
 } 
  
 } 
  
 } 
 ); 
 

By default, Identity Platform sends the user an email, and provides a simple, web-based handler to process the verification. There are a few ways you can customize this flow.

Localizing the verification emails

To localize the emails sent by Identity Platform, set the language code before calling verifyBeforeUpdateEmail() :

Web version 8

 firebase.auth().languageCode = 'fr'; 

Web version 9

  import 
  
 { 
 getAuth 
 } 
 from 
  
 "firebase/auth" 
 ; 
 const 
 auth 
 = 
 getAuth 
 (); 
 auth 
 . 
 languageCode 
 = 
 'fr' 
 ; 
 

iOS

 Auth.auth().languageCode = 'fr'; 

Android

 FirebaseAuth.getInstance().setLanguageCode("fr"); 

Passing additional state

You can use action code settings to include additional state in the verification email, or handle verification from a mobile app. For example:

Web version 8

  var 
  
 user 
  
 = 
  
 firebase 
 . 
 auth 
 () 
 . 
 currentUser 
 ; 
 var 
  
 actionCodeSettings 
  
 = 
  
 { 
  
 url 
 : 
  
 'https://www.example.com/completeVerification?state=*****' 
 , 
  
 iOS 
 : 
  
 { 
  
 bundleId 
 : 
  
 'com.example.ios' 
  
 }, 
  
 android 
 : 
  
 { 
  
 packageName 
 : 
  
 'com.example.android' 
 , 
  
 installApp 
 : 
  
 true 
 , 
  
 minimumVersion 
 : 
  
 '12' 
  
 }, 
  
 handleCodeInApp 
 : 
  
 true 
 , 
  
 // 
  
 When 
  
 multiple 
  
 custom 
  
 dynamic 
  
 link 
  
 domains 
  
 are 
  
 defined 
 , 
  
 specify 
  
 which 
  
 // 
  
 one 
  
 to 
  
 use 
 . 
  
 dynamicLinkDomain 
 : 
  
 "example.page.link" 
 }; 
 user 
 . 
 verifyBeforeUpdateEmail 
 ( 
 newEmail 
 , 
  
 actionCodeSettings 
 ) 
 . 
 then 
 ( 
 function 
 () 
  
 { 
  
 // 
  
 Email 
  
 sent 
 . 
  
 // 
  
 User 
  
 must 
  
 click 
  
 the 
  
 email 
  
 link 
  
 before 
  
 the 
  
 email 
  
 is 
  
 updated 
 . 
 }) 
 . 
 catch 
 ( 
 function 
 ( 
 error 
 ) 
  
 { 
  
 // 
  
 An 
  
 error 
  
 happened 
 . 
 }); 
 

Web version 9

  import 
  
 { 
 getAuth 
 , 
 verifyBeforeUpdateEmail 
 } 
 from 
  
 "firebase/auth" 
 ; 
 const 
 auth 
 = 
 getAuth 
 ( 
 firebaseApp 
 ); 
 const 
 user 
 = 
 auth 
 . 
 currentUser 
 const 
 actionCodeSettings 
 = 
 { 
 url 
 : 
 'https://www.example.com/completeVerification?state=*****' 
 , 
 iOS 
 : 
 { 
 bundleId 
 : 
 'com.example.ios' 
 }, 
 android 
 : 
 { 
 packageName 
 : 
 'com.example.android' 
 , 
 installApp 
 : 
 true 
 , 
 minimumVersion 
 : 
 '12' 
 }, 
 handleCodeInApp 
 : 
 true 
 , 
 // 
 When 
 multiple 
 custom 
 dynamic 
 link 
 domains 
 are 
 defined 
 , 
 specify 
 which 
 // 
 one 
 to 
 use 
 . 
 dynamicLinkDomain 
 : 
 "example.page.link" 
 }; 
 verifyBeforeUpdateEmail 
 ( 
 auth 
 . 
 currentUser 
 , 
 newEmail 
 , 
 actionCodeSettings 
 ) 
 . 
 then 
 (() 
 = 
> { 
 // 
 Email 
 sent 
 . 
 // 
 User 
 must 
 click 
 the 
 email 
 link 
 before 
 the 
 email 
 is 
 updated 
 . 
 }) 
 . 
 catch 
 (( 
 error 
 ) 
 = 
> { 
 // 
 An 
 error 
 happened 
 . 
 }) 
 

iOS

  var 
  
 actionCodeSettings 
  
 = 
  
 ActionCodeSettings 
 . 
 init 
 () 
 actionCodeSettings 
 . 
 canHandleInApp 
  
 = 
  
 true 
 let 
  
 user 
  
 = 
  
 Auth 
 . 
 auth 
 (). 
 currentUser 
 () 
 actionCodeSettings 
 . 
 URL 
  
 = 
  
 String 
 ( 
 format 
 : 
  
 "https://www.example.com/?email=%@" 
 , 
  
 user 
 . 
 email 
 ) 
 actionCodeSettings 
 . 
 iOSbundleID 
  
 = 
  
 Bundle 
 . 
 main 
 . 
 bundleIdentifier 
 ! 
 actionCodeSettings 
 . 
 setAndroidPakageName 
 ( 
 "com.example.android" 
 , 
  
 installIfNotAvailable 
 : 
 true 
 , 
  
 minimumVersion 
 : 
 "12" 
 ) 
 // When multiple custom dynamic link domains are defined, specify which one to use. 
 actionCodeSettings 
 . 
 dynamicLinkDomain 
  
 = 
  
 "example.page.link" 
 user 
 . 
 sendEmailVerification 
 ( 
 withActionCodeSettings 
 : 
 actionCodeSettings 
  
 { 
  
 error 
  
 in 
  
 if 
  
 error 
  
 != 
  
 nil 
  
 { 
  
 // Error occurred. Inspect error.code and handle error. 
  
 return 
  
 } 
  
 // Email verification sent. 
 }) 
 user 
 . 
 verifyBeforeUpdateEmail 
 ( 
 newEmail 
 , 
  
 actionCodeSettings 
 , 
  
 completion 
 : 
  
 { 
  
 ( 
 error 
 ) 
  
 in 
  
 if 
  
 error 
  
 != 
  
 nil 
  
 { 
  
 // An error happened. 
  
 } 
  
 // Email sent. 
  
 // User must click the email link before the email is updated. 
 }) 
 

Android

  ActionCodeSettings 
  
 actionCodeSettings 
  
 = 
  
 ActionCodeSettings 
 . 
 newBuilder 
 () 
  
 . 
 setUrl 
 ( 
 "https://www.example.com/completeVerification?state=*****" 
 ) 
  
 . 
 setHandleCodeInApp 
 ( 
 true 
 ) 
  
 . 
 setAndroidPackageName 
 ( 
  
 "com.example.android" 
 , 
  
 /* installIfNotAvailable= */ 
  
 true 
 , 
  
 /* minimumVersion= */ 
  
 null 
 ) 
  
 . 
 setIOSBundleId 
 ( 
 "com.example.ios" 
 ) 
  
 // 
  
 When 
  
 multiple 
  
 custom 
  
 dynamic 
  
 link 
  
 domains 
  
 are 
  
 defined 
 , 
  
 specify 
  
 // 
  
 which 
  
 one 
  
 to 
  
 use 
 . 
  
 . 
 setDynamicLinkDomain 
 ( 
 "example.page.link" 
 ) 
  
 . 
 build 
 (); 
 FirebaseUser 
  
 multiFactorUser 
  
 = 
  
 FirebaseAuth 
 . 
 getInstance 
 (). 
 getCurrentUser 
 (); 
 multiFactorUser 
  
 . 
 verifyBeforeUpdateEmail 
 ( 
 newEmail 
 , 
  
 actionCodeSettings 
 ) 
  
 . 
 addOnCompleteListener 
 ( 
  
 new 
  
 OnCompleteListener<Void> 
 () 
  
 { 
  
 @Override 
  
 public 
  
 void 
  
 onComplete 
 ( 
 @NonNull 
  
 Task<Void> 
  
 task 
 ) 
  
 { 
  
 if 
  
 ( 
 task 
 . 
 isSuccessful 
 ()) 
  
 { 
  
 // 
  
 Email 
  
 sent 
 . 
  
 // 
  
 User 
  
 must 
  
 click 
  
 the 
  
 email 
  
 link 
  
 before 
  
 the 
  
 email 
  
 is 
  
 updated 
 . 
  
 } 
  
 else 
  
 { 
  
 // 
  
 An 
  
 error 
  
 occurred 
 . 
  
 } 
  
 } 
  
 } 
 ); 
 

Customizing the verification handler

You can create your own handler to process email verification. The following example shows how to check an action code and inspect its metadata before applying it:

Web version 8

  var 
  
 email 
 ; 
 firebase 
 . 
 auth 
 (). 
 checkActionCode 
 ( 
 actionCode 
 ) 
  
 . 
 then 
 ( 
 function 
 ( 
 info 
 ) 
  
 { 
  
 // 
  
 Operation 
  
 is 
  
 equal 
  
 to 
  
 // 
  
 firebase 
 . 
 auth 
 . 
 ActionCodeInfo 
 . 
 Operation 
 . 
 VERIFY_AND_CHANGE_EMAIL 
  
 var 
  
 operation 
  
 = 
  
 info 
 [ 
 'operation' 
 ] 
 ; 
  
 // 
  
 This 
  
 is 
  
 the 
  
 old 
  
 email 
 . 
  
 var 
  
 previousEmail 
  
 = 
  
 info 
 [ 
 'data' 
 ][ 
 'previousEmail' 
 ] 
 ; 
  
 // 
  
 This 
  
 is 
  
 the 
  
 new 
  
 email 
  
 the 
  
 user 
  
 is 
  
 changing 
  
 to 
 . 
  
 email 
  
 = 
  
 info 
 [ 
 'data' 
 ][ 
 'email' 
 ] 
 ; 
  
 // 
  
 TODO 
 : 
  
 Display 
  
 a 
  
 message 
  
 to 
  
 the 
  
 end 
  
 user 
  
 that 
  
 the 
  
 email 
  
 address 
  
 of 
  
 the 
  
 account 
  
 is 
  
 // 
  
 going 
  
 to 
  
 be 
  
 changed 
  
 from 
  
 `fromEmail` 
  
 to 
  
 `email` 
  
 // 
  
  
  
 // 
  
 On 
  
 confirmation 
 . 
  
 return 
  
 firebase 
 . 
 auth 
 (). 
 applyActionCode 
 ( 
 actionCode 
 ) 
  
 } 
 ). 
 then 
 ( 
 function 
 () 
  
 { 
  
 // 
  
 Confirm 
  
 to 
  
 the 
  
 end 
  
 user 
  
 the 
  
 email 
  
 was 
  
 updated 
 . 
  
 showUI 
 ( 
 'You can now sign in with your new email: ' 
  
 + 
  
 email 
 ); 
  
 } 
 ) 
  
 . 
 catch 
 ( 
 function 
 ( 
 error 
 ) 
  
 { 
  
 // 
  
 Error 
  
 occurred 
  
 during 
  
 confirmation 
 . 
  
 The 
  
 code 
  
 might 
  
 have 
  
 expired 
  
 or 
  
 the 
  
 // 
  
 link 
  
 has 
  
 been 
  
 used 
  
 before 
 . 
  
 } 
 ); 
 

Web version 9

  import 
  
 { 
 getAuth 
 , 
 checkActionCode 
 , 
 applyActionCode 
 } 
 from 
  
 "firebase/auth" 
 ; 
 const 
 auth 
 = 
 getAuth 
 ( 
 firebaseApp 
 ); 
 var 
 email 
 ; 
 checkActionCode 
 ( 
 auth 
 , 
 actionCode 
 ) 
 . 
 then 
 (( 
 info 
 ) 
 = 
> { 
 // 
 Operation 
 is 
 equal 
 to 
 // 
 ActionCodeOperation 
 . 
 VERIFY_AND_CHANGE_EMAIL 
 const 
 operation 
 = 
 info 
 [ 
 'operation' 
 ]; 
 // 
 This 
 is 
 the 
 old 
 email 
 . 
 const 
 previousEmail 
 = 
 info 
 [ 
 'data' 
 ][ 
 'previousEmail' 
 ]; 
 // 
 This 
 is 
 the 
 new 
 email 
 the 
 user 
 is 
 changing 
 to 
 . 
 email 
 = 
 info 
 [ 
 'data' 
 ][ 
 'email' 
 ]; 
 // 
 TODO 
 : 
 Display 
 a 
 message 
 to 
 the 
 end 
 user 
 that 
 the 
 email 
 address 
 of 
 the 
 account 
 is 
 // 
 going 
 to 
 be 
 changed 
 from 
  
 ` 
 fromEmail 
 ` 
 to 
 ` 
 email 
 ` 
 // 
  
 // 
 On 
 confirmation 
 . 
 return 
 applyActionCode 
 ( 
 auth 
 , 
 actionCode 
 ) 
 }) 
 . 
 then 
 (() 
 = 
> { 
 // 
 Confirm 
 to 
 the 
 end 
 user 
 the 
 email 
 was 
 updated 
 . 
 showUI 
 ( 
 'You can now sign in with your new email: ' 
 + 
 email 
 ); 
 }) 
 . 
 catch 
 (( 
 error 
 ) 
 = 
> { 
 // 
 Error 
 occurred 
 during 
 confirmation 
 . 
 The 
 code 
 might 
 have 
 expired 
 or 
 the 
 // 
 link 
 has 
 been 
 used 
 before 
 . 
 }); 
 

iOS

  Auth 
 . 
 auth 
 (). 
 checkActionCode 
 ( 
 actionCode 
 ) 
  
 { 
  
 info 
 , 
  
 error 
  
 in 
  
 if 
  
 error 
  
 != 
  
 nil 
  
 { 
  
 // 
  
 Error 
  
 occurred 
  
 during 
  
 confirmation 
 . 
  
 The 
  
 code 
  
 might 
  
 have 
  
 expired 
  
 or 
  
 the 
  
 // 
  
 link 
  
 has 
  
 been 
  
 used 
  
 before 
 . 
  
 return 
  
 } 
  
 // 
  
 This 
  
 is 
  
 the 
  
 new 
  
 email 
  
 the 
  
 user 
  
 is 
  
 changing 
  
 to 
 . 
  
 let 
  
 email 
  
 = 
  
 info 
 ? 
 . 
 email 
  
 // 
  
 This 
  
 is 
  
 the 
  
 old 
  
 email 
 . 
  
 let 
  
 oldEmail 
  
 = 
  
 info 
 ? 
 . 
 previousEmail 
  
 // 
  
 operation 
  
 is 
  
 equal 
  
 to 
  
 // 
  
 firebase 
 . 
 auth 
 . 
 ActionCodeInfo 
 . 
 Operation 
 . 
 VERIFY_AND_CHANGE_EMAIL 
  
 let 
  
 operation 
  
 = 
  
 info 
 ? 
 . 
 operation 
  
 // 
  
 TODO 
 : 
  
 Display 
  
 a 
  
 message 
  
 to 
  
 the 
  
 end 
  
 user 
  
 that 
  
 the 
  
 email 
  
 address 
  
 of 
  
 the 
  
 account 
  
 is 
  
 // 
  
 going 
  
 to 
  
 be 
  
 changed 
  
 from 
  
 `fromEmail` 
  
 to 
  
 `email` 
  
 // 
  
  
  
 // 
  
 On 
  
 confirmation 
 . 
  
 return 
  
 Auth 
 . 
 auth 
 (). 
 applyActionCode 
 ( 
 actionCode 
 ) 
 } 
 

Android

  FirebaseAuth 
 . 
 getInstance 
 (). 
 checkActionCode 
 ( 
 actionCode 
 ). 
 addOnCompleteListener 
 ( 
  
 new 
  
 OnCompleteListener<ActionCodeResult> 
 () 
  
 { 
  
 @Override 
  
 public 
  
 void 
  
 onComplete 
 ( 
 @NonNull 
  
 Task<ActionCodeResult> 
  
 task 
 ) 
  
 { 
  
 if 
  
 ( 
 ! 
 task 
 . 
 isSuccessful 
 ()) 
  
 { 
  
 // 
  
 Error 
  
 occurred 
  
 during 
  
 confirmation 
 . 
  
 The 
  
 code 
  
 might 
  
 have 
  
 expired 
  
 or 
  
 the 
  
 // 
  
 link 
  
 has 
  
 been 
  
 used 
  
 before 
 . 
  
 return 
 ; 
  
 } 
  
 ActionCodeResult 
  
 result 
  
 = 
  
 task 
 . 
 getResult 
 (); 
  
 // 
  
 This 
  
 maps 
  
 to 
  
 VERIFY_AND_CHANGE_EMAIL 
 . 
  
 int 
  
 operation 
  
 = 
  
 result 
 . 
 getOperation 
 (); 
  
 if 
  
 ( 
 operation 
  
 == 
  
 ActionCodeResult 
 . 
 VERIFY_AND_CHANGE_EMAIL 
 ) 
  
 { 
  
 ActionCodeEmailInfo 
  
 actionCodeInfo 
  
 = 
  
 ( 
 ActionCodeEmailInfo 
 ) 
  
 result 
 . 
 getInfo 
 (); 
  
 String 
  
 fromEmail 
  
 = 
  
 actionCodeInfo 
 . 
 getFromEmail 
 (); 
  
 String 
  
 email 
  
 = 
  
 actionCodeInfo 
 . 
 getEmail 
 (); 
  
 // 
  
 TODO 
 : 
  
 Display 
  
 a 
  
 message 
  
 to 
  
 the 
  
 user 
  
 that 
  
 the 
  
 email 
  
 address 
  
 // 
  
 of 
  
 the 
  
 account 
  
 is 
  
 changing 
  
 from 
  
 `fromEmail` 
  
 to 
  
 `email` 
  
 once 
  
 // 
  
 they 
  
 confirm 
 . 
  
 } 
  
 } 
  
 } 
 ); 
 

To learn more, see the Firebase documentation on Creating custom email action handlers .

Re-authenticating a user

Even if a user is already signed in, you might want to re-authenticate them before performing sensitive operations, such as:

  • Changing a password.
  • Adding or removing a new second factor.
  • Updating personal information (like an address).
  • Executing financial transactions.
  • Deleting a user's account.

To re-authenticate a user with an email and password:

Web

  var 
  
 resolver 
 ; 
 var 
  
 credential 
  
 = 
  
 firebase 
 . 
 auth 
 . 
 EmailAuthProvider 
 . 
 credential 
 ( 
  
 firebase 
 . 
 auth 
 () 
 . 
 currentUser 
 . 
 email 
 , 
  
 password 
 ); 
 firebase 
 . 
 auth 
 () 
 . 
 currentUser 
 . 
 reauthenticateWithCredential 
 ( 
 credential 
 ) 
  
 . 
 then 
 ( 
 function 
 ( 
 userCredential 
 ) 
  
 { 
  
 // 
  
 User 
  
 successfully 
  
 re 
 - 
 authenticated 
  
 and 
  
 does 
  
 not 
  
 require 
  
 a 
  
 second 
  
 factor 
  
 challenge 
 . 
  
 // 
  
 ... 
  
 }) 
  
 . 
 catch 
 ( 
 function 
 ( 
 error 
 ) 
  
 { 
  
 if 
  
 ( 
 error 
 . 
 code 
  
 == 
  
 'auth/multi-factor-auth-required' 
 ) 
  
 { 
  
 // 
  
 Handle 
  
 multi 
 - 
 factor 
  
 authentication 
 . 
  
 } 
  
 else 
  
 { 
  
 // 
  
 Handle 
  
 other 
  
 errors 
 . 
  
 } 
  
 }); 
 

iOS

 let credential = EmailAuthProvider.credential(withEmail: email, password: password)
Auth.auth().currentUser.reauthenticate(with: credential, completion: { (result, error) in
  let authError = error as NSError?
  if (authError == nil || authError!.code != AuthErrorCode.secondFactorRequired.rawValue) {
    // User is not enrolled with a second factor or is successfully signed in.
  } else {
    // Handle multi-factor authentication.
  }
}) 

Android

  FirebaseUser 
  
 user 
  
 = 
  
 FirebaseAuth 
 . 
 getInstance 
 (). 
 getCurrentUser 
 (); 
 AuthCredential 
  
 credential 
  
 = 
  
 EmailAuthProvider 
 . 
 getCredential 
 ( 
 user 
 . 
 getEmail 
 (), 
  
 password 
 ); 
 user 
 . 
 reauthenticate 
 ( 
 credential 
 ) 
  
 . 
 addOnCompleteListener 
 ( 
  
 new 
  
 OnCompleteListener<AuthResult> 
 () 
  
 { 
  
 @Override 
  
 public 
  
 void 
  
 onComplete 
 ( 
 @NonNull 
  
 Task<AuthResult> 
  
 task 
 ) 
  
 { 
  
 if 
  
 ( 
 task 
 . 
 isSuccessful 
 ()) 
  
 { 
  
 // 
  
 User 
  
 successfully 
  
 re 
 - 
 authenticated 
  
 and 
  
 does 
  
 not 
  
 // 
  
 require 
  
 a 
  
 second 
  
 factor 
  
 challenge 
 . 
  
 // 
  
 ... 
  
 return 
 ; 
  
 } 
  
 if 
  
 ( 
 task 
 . 
 getException 
 () 
  
 instanceof 
  
 FirebaseAuthMultiFactorException 
 ) 
  
 { 
  
 // 
  
 Handle 
  
 multi 
 - 
 factor 
  
 authentication 
 . 
  
 } 
  
 else 
  
 { 
  
 // 
  
 Handle 
  
 other 
  
 errors 
 . 
  
 } 
  
 } 
  
 } 
 ); 
 

To re-authenticate using an OAuth provider, such as Microsoft:

Web

  var 
  
 resolver 
 ; 
 var 
  
 user 
  
 = 
  
 firebase 
 . 
 auth 
 () 
 . 
 currentUser 
 ; 
 // 
  
 Ask 
  
 the 
  
 user 
  
 to 
  
 re 
 - 
 authenticate 
  
 with 
  
 Microsoft 
 . 
 var 
  
 provider 
  
 = 
  
 new 
  
 firebase 
 . 
 auth 
 . 
 OAuthProvider 
 ( 
 'microsoft.com' 
 ); 
 // 
  
 Microsoft 
  
 provider 
  
 allows 
  
 the 
  
 ability 
  
 to 
  
 provide 
  
 a 
  
 login_hint 
 . 
 provider 
 . 
 setCustomParameters 
 ({ 
  
 login_hint 
 : 
  
 user 
 . 
 email 
 }); 
 user 
 . 
 reauthenticateWithPopup 
 ( 
 provider 
 ) 
  
 . 
 then 
 ( 
 function 
 ( 
 userCredential 
 ) 
  
 { 
  
 // 
  
 User 
  
 successfully 
  
 re 
 - 
 authenticated 
  
 and 
  
 does 
  
 not 
  
 require 
  
 a 
  
 second 
  
 factor 
  
 challenge 
 . 
  
 // 
  
 ... 
  
 }) 
  
 . 
 catch 
 ( 
 function 
 ( 
 error 
 ) 
  
 { 
  
 if 
  
 ( 
 error 
 . 
 code 
  
 == 
  
 'auth/multi-factor-auth-required' 
 ) 
  
 { 
  
 // 
  
 Handle 
  
 multi 
 - 
 factor 
  
 authentication 
 . 
  
 } 
  
 else 
  
 { 
  
 // 
  
 Unsupported 
  
 second 
  
 factor 
 . 
  
 } 
  
 else 
  
 { 
  
 // 
  
 Handle 
  
 other 
  
 errors 
 . 
  
 } 
  
 }); 
 

iOS

  var 
  
 provider 
  
 = 
  
 OAuthProvider 
 ( 
 providerID 
 : 
  
 "microsoft.com" 
 ) 
  
 // 
  
 Replace 
  
 nil 
  
 with 
  
 the 
  
 custom 
  
 class 
  
 that 
  
 conforms 
  
 to 
  
 AuthUIDelegate 
  
 // 
  
 you 
  
 created 
  
 in 
  
 last 
  
 step 
  
 to 
  
 use 
  
 a 
  
 customized 
  
 web 
  
 view 
 . 
  
 provider 
 . 
 getCredentialWith 
 ( 
 nil 
 ) 
  
 { 
  
 credential 
 , 
  
 error 
  
 in 
  
 Auth 
 . 
 auth 
 () 
 . 
 currentUser 
 . 
 reauthenticate 
 ( 
 with 
 : 
  
 credential 
 , 
  
 completion 
 : 
  
 { 
  
 ( 
 result 
 , 
  
 error 
 ) 
  
 in 
  
 let 
  
 authError 
  
 = 
  
 error 
  
 as 
  
 NSError 
 ? 
  
 if 
  
 ( 
 authError 
  
 == 
  
 nil 
  
 || 
  
 authError 
 !. 
 code 
  
 != 
  
 AuthErrorCode 
 . 
 secondFactorRequired 
 . 
 rawValue 
 ) 
  
 { 
  
 // 
  
 User 
  
 is 
  
 not 
  
 enrolled 
  
 with 
  
 a 
  
 second 
  
 factor 
  
 or 
  
 is 
  
 successfully 
  
 signed 
  
 in 
 . 
  
 // 
  
 ... 
  
 } 
  
 else 
  
 { 
  
 // 
  
 Handle 
  
 multi 
 - 
 factor 
  
 authentication 
 . 
  
 } 
  
 } 
  
 }) 
 

Android

  FirebaseUser 
  
 user 
  
 = 
  
 FirebaseAuth 
 . 
 getInstance 
 (). 
 getCurrentUser 
 (); 
 OAuthProvider 
 . 
 Builder 
  
 provider 
  
 = 
  
 OAuthProvider 
 . 
 newBuilder 
 ( 
 "microsoft.com" 
 ); 
 provider 
 . 
 addCustomParameter 
 ( 
 "login_hint" 
 , 
  
 user 
 . 
 getEmail 
 ()); 
 user 
 . 
 startActivityForReauthenticateWithProvider 
 ( 
 /* activity= */ 
  
 this 
 , 
  
 provider 
 . 
 build 
 ()) 
  
 . 
 addOnCompleteListener 
 ( 
  
 new 
  
 OnCompleteListener<AuthResult> 
 () 
  
 { 
  
 @Override 
  
 public 
  
 void 
  
 onComplete 
 ( 
 @NonNull 
  
 Task<AuthResult> 
  
 task 
 ) 
  
 { 
  
 if 
  
 ( 
 task 
 . 
 isSuccessful 
 ()) 
  
 { 
  
 // 
  
 User 
  
 successfully 
  
 re 
 - 
 authenticated 
  
 and 
  
 does 
  
 not 
  
 // 
  
 require 
  
 a 
  
 second 
  
 factor 
  
 challenge 
 . 
  
 // 
  
 ... 
  
 return 
 ; 
  
 } 
  
 if 
  
 ( 
 task 
 . 
 getException 
 () 
  
 instanceof 
  
 FirebaseAuthMultiFactorException 
 ) 
  
 { 
  
 // 
  
 Handle 
  
 multi 
 - 
 factor 
  
 authentication 
 . 
  
 } 
  
 else 
  
 { 
  
 // 
  
 Handle 
  
 other 
  
 errors 
  
 such 
  
 as 
  
 wrong 
  
 password 
 . 
  
 } 
  
 } 
  
 } 
 ); 
 

Revoking a recently-added second factor

When a user enrolls a second factor, Identity Platform sends a notification to their email. To protect against unauthorized activity, the email includes an option to reverse the addition of a second factor.

Identity Platform provides a default email template and handler, but you can also build your own. The following example shows how to create a custom handler:

Web version 8

  var 
  
 obfuscatedPhoneNumber 
 ; 
 firebase 
 . 
 auth 
 () 
 . 
 checkActionCode 
 ( 
 actionCode 
 ) 
  
 . 
 then 
 ( 
 function 
 ( 
 info 
 ) 
  
 { 
  
 // 
  
 operation 
  
 is 
  
 equal 
  
 to 
  
 // 
  
 firebase 
 . 
 auth 
 . 
 ActionCodeInfo 
 . 
 Operation 
 . 
 REVERT_SECOND_FACTOR_ADDITION 
  
 var 
  
 operation 
  
 = 
  
 info 
 [ 
 'operation' 
 ]; 
  
 // 
  
 info 
 . 
 data 
 . 
 multiFactorInfo 
  
 contains 
  
 the 
  
 data 
  
 corresponding 
  
 to 
  
 the 
  
 // 
  
 enrolled 
  
 second 
  
 factor 
  
 that 
  
 the 
  
 user 
  
 is 
  
 revoking 
 . 
  
 var 
  
 multiFactorInfo 
  
 = 
  
 info 
 [ 
 'data' 
 ][ 
 'multiFactorInfo' 
 ]; 
  
 obfuscatedPhoneNumber 
  
 = 
  
 multiFactorInfo 
 [ 
 'phoneNumber' 
 ]; 
  
 var 
  
 displayName 
  
 = 
  
 multiFactorInfo 
 [ 
 'displayName' 
 ]; 
  
 // 
  
 TODO 
 : 
  
 Display 
  
 a 
  
 message 
  
 to 
  
 the 
  
 end 
  
 user 
  
 about 
  
 the 
  
 second 
  
 factor 
  
 that 
  
 // 
  
 was 
  
 enrolled 
  
 before 
  
 the 
  
 user 
  
 can 
  
 confirm 
  
 the 
  
 action 
  
 to 
  
 revert 
  
 it 
 . 
  
 // 
  
 ... 
  
 // 
  
 On 
  
 confirmation 
 . 
  
 return 
  
 firebase 
 . 
 auth 
 () 
 . 
 applyActionCode 
 ( 
 actionCode 
 ) 
  
 }) 
 . 
 then 
 ( 
 function 
 () 
  
 { 
  
 // 
  
 Confirm 
  
 to 
  
 the 
  
 end 
  
 user 
  
 the 
  
 phone 
  
 number 
  
 was 
  
 removed 
  
 from 
  
 the 
  
 account 
 . 
  
 showUI 
 ( 
 'The phone number ' 
  
 + 
  
 obfuscatedPhoneNumber 
  
 + 
  
 ' has been removed as a second factor from your account.' 
  
 + 
  
 ' You may also want to reset your password if you suspect' 
  
 + 
  
 ' your account was compromised.' 
 ); 
  
 }) 
  
 . 
 catch 
 ( 
 function 
 ( 
 error 
 ) 
  
 { 
  
 // 
  
 Error 
  
 occurred 
  
 during 
  
 confirmation 
 . 
  
 The 
  
 code 
  
 might 
  
 have 
  
 expired 
  
 or 
  
 the 
  
 // 
  
 link 
  
 has 
  
 been 
  
 used 
  
 before 
 . 
  
 }); 
 

Web version 9

  const 
  
 { 
  
 getAuth 
 , 
  
 checkActionCode 
 , 
  
 applyActionCode 
 } 
  
 = 
  
 require 
 ( 
 "firebase/auth" 
 ); 
 const 
  
 auth 
  
 = 
  
 getAuth 
 ( 
 firebaseApp 
 ); 
 var 
  
 obfuscatedPhoneNumber 
 ; 
 checkActionCode 
 ( 
 auth 
 , 
  
 actionCode 
 ) 
  
 . 
 then 
 (( 
 info 
 ) 
  
 = 
>  
 { 
  
 // 
  
 Operation 
  
 is 
  
 equal 
  
 to 
  
 // 
  
 ActionCodeOperation 
 . 
 REVERT_SECOND_FACTOR_ADDITION 
  
 const 
  
 operation 
  
 = 
  
 info 
 [ 
 'operation' 
 ]; 
  
 // 
  
 info 
 . 
 data 
 . 
 multiFactorInfo 
  
 contains 
  
 the 
  
 data 
  
 corresponding 
  
 to 
  
 the 
  
 // 
  
 enrolled 
  
 second 
  
 factor 
  
 that 
  
 the 
  
 user 
  
 is 
  
 revoking 
 . 
  
 var 
  
 multiFactorInfo 
  
 = 
  
 info 
 [ 
 'data' 
 ][ 
 'multiFactorInfo' 
 ]; 
  
 obfuscatedPhoneNumber 
  
 = 
  
 multiFactorInfo 
 [ 
 'phoneNumber' 
 ]; 
  
 const 
  
 displayName 
  
 = 
  
 multiFactorInfo 
 [ 
 'displayName' 
 ]; 
  
 // 
  
 TODO 
 : 
  
 Display 
  
 a 
  
 message 
  
 to 
  
 the 
  
 end 
  
 user 
  
 about 
  
 the 
  
 second 
  
 factor 
  
 that 
  
 // 
  
 was 
  
 enrolled 
  
 before 
  
 the 
  
 user 
  
 can 
  
 confirm 
  
 the 
  
 action 
  
 to 
  
 revert 
  
 it 
 . 
  
 // 
  
 ... 
  
 // 
  
 On 
  
 confirmation 
 . 
  
 return 
  
 applyActionCode 
 ( 
 auth 
 , 
  
 actionCode 
 ) 
  
 }) 
 . 
 then 
 (() 
  
 = 
>  
 { 
  
 // 
  
 Confirm 
  
 to 
  
 the 
  
 end 
  
 user 
  
 the 
  
 phone 
  
 number 
  
 was 
  
 removed 
  
 from 
  
 the 
  
 account 
 . 
  
 showUI 
 ( 
 'The phone number ' 
  
 + 
  
 obfuscatedPhoneNumber 
  
 + 
  
 ' has been removed as a second factor from your account.' 
  
 + 
  
 ' You may also want to reset your password if you suspect' 
  
 + 
  
 ' your account was compromised.' 
 ); 
  
 }) 
  
 . 
 catch 
 (( 
 error 
 ) 
  
 = 
>  
 { 
  
 // 
  
 Error 
  
 occurred 
  
 during 
  
 confirmation 
 . 
  
 The 
  
 code 
  
 might 
  
 have 
  
 expired 
  
 or 
  
 the 
  
 // 
  
 link 
  
 has 
  
 been 
  
 used 
  
 before 
 . 
  
 }); 
 

iOS

  Auth 
 . 
 auth 
 (). 
 checkActionCode 
 ( 
 actionCode 
 ) 
  
 { 
  
 info 
 , 
  
 error 
  
 in 
  
 if 
  
 error 
  
 != 
  
 nil 
  
 { 
  
 // 
  
 Error 
  
 occurred 
  
 during 
  
 confirmation 
 . 
  
 The 
  
 code 
  
 might 
  
 have 
  
 expired 
  
 or 
  
 the 
  
 // 
  
 link 
  
 has 
  
 been 
  
 used 
  
 before 
 . 
  
 return 
  
 } 
  
 // 
  
 This 
  
 is 
  
 the 
  
 new 
  
 email 
  
 the 
  
 user 
  
 is 
  
 changing 
  
 to 
 . 
  
 let 
  
 email 
  
 = 
  
 info 
 ? 
 . 
 email 
  
 // 
  
 This 
  
 is 
  
 the 
  
 old 
  
 email 
 . 
  
 let 
  
 oldEmail 
  
 = 
  
 info 
 ? 
 . 
 previousEmail 
  
 // 
  
 operation 
  
 is 
  
 equal 
  
 to 
  
 // 
  
 firebase 
 . 
 auth 
 . 
 ActionCodeInfo 
 . 
 Operation 
 . 
 REVERT_SECOND_FACTOR_ADDITION 
  
 let 
  
 operation 
  
 = 
  
 info 
 ? 
 . 
 operation 
  
 // 
  
 info 
 . 
 multiFactorInfo 
  
 contains 
  
 the 
  
 data 
  
 corresponding 
  
 to 
  
 the 
  
 enrolled 
  
 second 
  
 // 
  
 factor 
  
 that 
  
 the 
  
 user 
  
 is 
  
 revoking 
 . 
  
 let 
  
 multiFactorInfo 
  
 = 
  
 info 
 ? 
 . 
 multiFactorInfo 
  
 let 
  
 obfuscatedPhoneNumber 
  
 = 
  
 ( 
 multiFactorInfo 
  
 as 
 ! 
  
 PhoneMultiFactorInfo 
 ). 
 phone 
  
 // 
  
 TODO 
 : 
  
 Display 
  
 a 
  
 message 
  
 to 
  
 the 
  
 end 
  
 user 
  
 that 
  
 the 
  
 email 
  
 address 
  
 of 
  
 the 
  
 account 
  
 is 
  
 // 
  
 going 
  
 to 
  
 be 
  
 changed 
  
 from 
  
 `fromEmail` 
  
 to 
  
 `email` 
  
 // 
  
  
  
 // 
  
 On 
  
 confirmation 
 . 
  
 return 
  
 Auth 
 . 
 auth 
 (). 
 applyActionCode 
 ( 
 actionCode 
 ) 
 } 
 

Android

  FirebaseAuth 
 . 
 getInstance 
 () 
  
 . 
 checkActionCode 
 ( 
 actionCode 
 ) 
  
 . 
 continueWithTask 
 ( 
  
 new 
  
 Continuation<ActionCodeResult 
 , 
  
 Task<Void> 
> () 
  
 { 
  
 @Override 
  
 public 
  
 Task<Void> 
  
 then 
 ( 
 Task<ActionCodeResult> 
  
 task 
 ) 
  
 throws 
  
 Exception 
  
 { 
  
 if 
  
 ( 
 ! 
 task 
 . 
 isSuccessful 
 ()) 
  
 { 
  
 // 
  
 Error 
  
 occurred 
  
 during 
  
 confirmation 
 . 
  
 The 
  
 code 
  
 might 
  
 have 
  
 expired 
  
 // 
  
 or 
  
 the 
  
 link 
  
 has 
  
 been 
  
 used 
  
 before 
 . 
  
 return 
  
 Tasks 
 . 
 forException 
 ( 
 task 
 . 
 getException 
 ()); 
  
 } 
  
 ActionCodeResult 
  
 result 
  
 = 
  
 task 
 . 
 getResult 
 (); 
  
 // 
  
 The 
  
 operation 
  
 is 
  
 equal 
  
 to 
  
 ActionCodeResult 
 . 
 REVERT_SECOND_FACTOR_ADDITION 
 . 
  
 int 
  
 operation 
  
 = 
  
 result 
 . 
 getOperation 
 (); 
  
 // 
  
 The 
  
 ActionCodeMultiFactorInfo 
  
 contains 
  
 the 
  
 data 
  
 corresponding 
  
 to 
  
 // 
  
 the 
  
 enrolled 
  
 second 
  
 factor 
  
 that 
  
 the 
  
 user 
  
 is 
  
 revoking 
 . 
  
 ActionCodeMultiFactorInfo 
  
 actionCodeInfo 
  
 = 
  
 ( 
 ActionCodeMultiFactorInfo 
 ) 
  
 result 
 . 
 getInfo 
 (); 
  
 PhoneMultiFactorInfo 
  
 multiFactorInfo 
  
 = 
  
 ( 
 PhoneMultiFactorInfo 
 ) 
  
 actionCodeInfo 
 . 
 getMultiFactorInfo 
 (); 
  
 String 
  
 obfuscatedPhoneNumber 
  
 = 
  
 multiFactorInfo 
 . 
 getPhoneNumber 
 (); 
  
 String 
  
 displayName 
  
 = 
  
 multiFactorInfo 
 . 
 getDisplayName 
 (); 
  
 // 
  
 We 
  
 can 
  
 now 
  
 display 
  
 a 
  
 message 
  
 to 
  
 the 
  
 end 
  
 user 
  
 about 
  
 the 
  
 second 
  
 // 
  
 factor 
  
 that 
  
 was 
  
 enrolled 
  
 before 
  
 they 
  
 confirm 
  
 the 
  
 action 
  
 to 
  
 revert 
  
 // 
  
 it 
 . 
  
 // 
  
 ... 
  
 // 
  
 On 
  
 user 
  
 confirmation 
 : 
  
 return 
  
 FirebaseAuth 
 . 
 getInstance 
 (). 
 applyActionCode 
 ( 
 actionCode 
 ); 
  
 } 
  
 } 
 ) 
  
 . 
 addOnCompleteListener 
 ( 
  
 new 
  
 OnCompleteListener<Void> 
 () 
  
 { 
  
 @Override 
  
 public 
  
 void 
  
 onComplete 
 ( 
 Task<Void> 
  
 task 
 ) 
  
 { 
  
 if 
  
 ( 
 task 
 . 
 isSuccessful 
 ()) 
  
 { 
  
 // 
  
 Display 
  
 a 
  
 message 
  
 to 
  
 the 
  
 user 
  
 that 
  
 the 
  
 second 
  
 factor 
  
 // 
  
 has 
  
 been 
  
 reverted 
 . 
  
 } 
  
 } 
  
 } 
 ); 
 

To learn more, see the Firebase documentation on Creating custom email action handlers .

Recovering a second factor

Identity Platform does not provide a built-in mechanism for recovering second factors. If a user loses access to their second factor, they will be locked out of their account. To prevent this from happening, consider the following:

  • Warning users that they will lose access to their account without their second factor.
  • Strongly encouraging users to register a backup secondary factor.
  • Using the Admin SDK to build a recovery flow that disables multi-factor authentication if the user can sufficiently verify their identity (for example, by uploading a recovery key or answering personal questions).
  • Granting your support team the ability to manage user accounts (including removing second factors), and provide an option for users to contact them if they are locked out of their account.

Password reset will not allow a user to bypass multi-factor authentication. If you reset a user's password using sendPasswordResetEmail() , they will still be required to pass the multi-factor challenge upon signing in with their new password.

Unenrolling a second factor

To unenroll a second factor, get it from the list of the user's enrolled factors, then call unenroll() . Since this is a sensitive operation, you'll need to re-authenticate the user first if they haven't signed in recently.

Web version 8

  var 
  
 options 
  
 = 
  
 user 
 . 
 multiFactor 
 . 
 enrolledFactors 
 ; 
 // 
  
 Ask 
  
 user 
  
 to 
  
 select 
  
 from 
  
 the 
  
 enrolled 
  
 options 
 . 
 return 
  
 user 
 . 
 multiFactor 
 . 
 unenroll 
 ( 
 options 
 [ 
 selectedIndex 
 ] 
 ) 
  
 . 
 then 
 ( 
 function 
 () 
  
 { 
  
 // 
  
 User 
  
 successfully 
  
 unenrolled 
  
 selected 
  
 factor 
 . 
  
 } 
 ); 
 

Web version 9

  const 
  
 multiFactorUser 
  
 = 
  
 multiFactor 
 ( 
 auth 
 . 
 currentUser 
 ); 
 const 
  
 options 
  
 = 
  
 multiFactorUser 
 . 
 enrolledFactors 
 // 
  
 Ask 
  
 user 
  
 to 
  
 select 
  
 from 
  
 the 
  
 enrolled 
  
 options 
 . 
 return 
  
 multiFactorUser 
 . 
 unenroll 
 ( 
 options 
 [ 
 selectedIndex 
 ] 
 ) 
  
 . 
 then 
 (() 
  
 = 
>  
 // 
  
 User 
  
 successfully 
  
 unenrolled 
  
 selected 
  
 factor 
 . 
  
 } 
 ); 
 

iOS

  // 
  
 Ask 
  
 user 
  
 to 
  
 select 
  
 from 
  
 the 
  
 enrolled 
  
 options 
 . 
 user 
 ? 
 . 
 multiFactor 
 . 
 unenroll 
 ( 
 with 
 : 
  
 ( 
 user 
 ? 
 . 
 multiFactor 
 . 
 enrolledFactors 
 [ 
 selectedIndex 
 ] 
 ) 
 ! 
 , 
  
 completion 
 : 
  
 { 
  
 ( 
 error 
 ) 
  
 in 
  
 // 
  
 ... 
 } 
 ) 
 

Android

  List<MultiFactorInfo> 
  
 options 
  
 = 
  
 user 
 . 
 getMultiFactor 
 (). 
 getEnrolledFactors 
 (); 
 // 
  
 Ask 
  
 user 
  
 to 
  
 select 
  
 from 
  
 the 
  
 enrolled 
  
 options 
 . 
 user 
 . 
 getMultiFactor 
 () 
  
 . 
 unenroll 
 ( 
 options 
 . 
 get 
 ( 
 selectedIndex 
 )) 
  
 . 
 addOnCompleteListener 
 ( 
  
 new 
  
 OnCompleteListener<Void> 
 () 
  
 { 
  
 @Override 
  
 public 
  
 void 
  
 onComplete 
 ( 
 @NonNull 
  
 Task<Void> 
  
 task 
 ) 
  
 { 
  
 if 
  
 ( 
 task 
 . 
 isSuccessful 
 ()) 
  
 { 
  
 // 
  
 Successfully 
  
 un 
 - 
 enrolled 
 . 
  
 } 
  
 } 
  
 } 
 ); 
 

In some cases, the user may be signed out after removing a second factor. Use onAuthStateChanged() to listen for this case, and ask the user to sign in again.

What's next

Design a Mobile Site
View Site in Mobile | Classic
Share by: