If you've upgraded to Firebase Authentication with Identity Platform, you can add time-based one-time password(TOTP) multi-factor authentication (MFA) to your app.
Firebase Authentication with Identity Platform lets you use a TOTP as an additional factor for MFA. When youenable this feature, users attempting to sign in to your app see a request for aTOTP. To generate it, they must use an authenticator app capable of generatingvalid TOTP codes, such as Google Authenticator.
Before you begin
Enable at least one provider that supports MFA. Note that all providersexcept the following support MFA:
- Phone auth
- Anonymous auth
- Custom auth tokens
- Apple Game Center
Ensure your app verifies user email addresses. MFA requires emailverification. This prevents malicious actors from registering for a servicewith an email address that they don't own, and then locking out the actualowner of the email address by adding a second factor.
If you haven't done so already, install theFirebase Android SDK.
TOTP MFA is only supported on theAndroid SDK version v22.1.0and above.
Enable TOTP MFA
To enable TOTP as a second factor, use the Admin SDK or call the projectconfiguration REST endpoint.
To use the Admin SDK, do the following:
If you haven't done so already, install theFirebase Admin Node.js SDK.
See AlsoTurn On MFA | CISATutorial - multifactor authentication for B2B - Microsoft Entra External IDTOTP: Time Based One Time Passwords | Security EncyclopediaSet up Authenticator App for User AccountTOTP MFA is only supported on Firebase Admin Node.js SDK versions 11.6.0 andabove.
Run the following:
import { getAuth } from 'firebase-admin/auth';getAuth().projectConfigManager().updateProjectConfig({ multiFactorConfig: { providerConfigs: [{ state: "ENABLED", totpProviderConfig: { adjacentIntervals: NUM_ADJ_INTERVALS } }] }})
Replace the following:
NUM_ADJ_INTERVALS
: The number of adjacenttime-window intervals from which to accept TOTPs, from zero to ten. Thedefault is five.TOTPs work by ensuring that when two parties (the prover and thevalidator) generate OTPs within the same time window (typically 30 secondslong), they generate the same password. However, to accommodate clockdrift between parties and human response time, you can configure the TOTPservice to also accept TOTPs from adjacent windows.
To enable TOTP MFA using the REST API, run the following:
curl -X PATCH "https://identitytoolkit.googleapis.com/admin/v2/projects/PROJECT_ID/config?updateMask=mfa" \ -H "Authorization: Bearer $(gcloud auth print-access-token)" \ -H "Content-Type: application/json" \ -H "X-Goog-User-Project: PROJECT_ID" \ -d \ '{ "mfa": { "providerConfigs": [{ "state": "ENABLED", "totpProviderConfig": { "adjacentIntervals": NUM_ADJ_INTERVALS } }] } }'
Replace the following:
PROJECT_ID
: The project ID.NUM_ADJ_INTERVALS
: The number of time-windowintervals, from zero to ten. The default is five.TOTPs work by ensuring that when two parties (the prover and thevalidator) generate OTPs within the same time window (typically 30 secondslong), they generate the same password. However, to accommodate clockdrift between parties and human response time, you can configure the TOTPservice to also accept TOTPs from adjacent windows.
Choose an enrollment pattern
You can choose whether your app requires multi-factor authentication, and howand when to enroll your users. Some common patterns include the following:
Enroll the user's second factor as part of registration. Use thismethod if your app requires multi-factor authentication for all users.
Offer a skippable option to enroll a second factor during registration. If youwant to encourage but not require multi-factor authentication in your app, youmight use this approach.
Provide the ability to add a second factor from the user's account or profilemanagement page, instead of the sign-up screen. This minimizes friction duringthe registration process, while still making multi-factor authenticationavailable for security-sensitive users.
Require adding a second factor incrementally when the user wants to accessfeatures with increased security requirements.
Enroll users in TOTP MFA
After you enable TOTP MFA as a second factor for your app, implement client-sidelogic to enroll users in TOTP MFA:
Re-authenticate the user.
Generate a TOTP secret for the authenticated user:
// Generate a TOTP secret.Firebase.auth.currentUser.multiFactor.session .addOnSuccessListener { multiFactorSession -> TotpMultiFactorGenerator.generateSecret(multiFactorSession) .addOnSuccessListener { totpSecret -> // Display the secret to the user and prompt them to // enter it into their authenticator app. (See the next // step.) } }
Display the secret to the user and prompt them to enter it into theirauthenticator app:
// Display this key:val secret = totpSecret.sharedSecretKey
In addition to displaying the secret key, you can attempt to automaticallyadd it to the device's default authenticator app. To do so, generate aGoogle Authenticator-compatible key URI,and pass it to
openInOtpApp()
:val qrCodeUri = totpSecret.generateQrCodeUrl( currentUser.email ?: "default account", "Your App Name")totpSecret.openInOtpApp(qrCodeUri)
After the user adds their secret to their authenticator app, it will startgenerating TOTPs.
Prompt the user to type the TOTP displayed by their authenticator app anduse it to finalize MFA enrollment:
// Ask the user for a verification code from the authenticator app.val verificationCode = // Code from user input.// Finalize the enrollment.val multiFactorAssertion = TotpMultiFactorGenerator .getAssertionForEnrollment(totpSecret, verificationCode)Firebase.auth.currentUser.multiFactor.enroll(multiFactorAssertion, "TOTP") .addOnSuccessListener { // Enrollment complete. }
Sign in users with a second factor
To sign in users with TOTP MFA, use the following code:
Call one of the
signInWith
- methods as you would if you weren't using MFA.(For example,signInWithEmailAndPassword()
.) If the method throws anFirebaseAuthMultiFactorException
, start your app's MFA flow.Firebase.auth.signInWithEmailAndPassword(email, password) .addOnSuccessListener { result -> // If the user is not enrolled with a second factor and provided valid // credentials, sign-in succeeds. // (If your app requires MFA, this could be considered an error // condition, which you would resolve by forcing the user to enroll a // second factor.) // ... } .addOnFailureListener { exception -> when (exception) { is FirebaseAuthMultiFactorException -> { // Initiate your second factor sign-in flow. (See next step.) // ... } } }
Your app's MFA flow should first prompt the user to choose the second factorthey want to use. You can get a list of supported second factors byexamining the
hints
property of aMultiFactorResolver
instance:val enrolledFactors = exception.resolver.hints.map { it.displayName }
If the user chooses to use TOTP, prompt them to type the TOTP displayed ontheir authenticator app and use it to sign in:
when (exception.resolver.hints[selectedIndex].factorId) { TotpMultiFactorGenerator.FACTOR_ID -> { val otpFromAuthenticator = // OTP typed by the user. val assertion = TotpMultiFactorGenerator.getAssertionForSignIn( exception.resolver.hints[selectedIndex].uid, otpFromAuthenticator ) exception.resolver.resolveSignIn(assertion) .addOnSuccessListener { result -> // Successfully signed in! } .addOnFailureListener { resolveError -> // Invalid or expired OTP. } } PhoneMultiFactorGenerator.FACTOR_ID -> { // Handle SMS second factor. }}
Unenroll from TOTP MFA
This section describes how to handle a user unenrolling from TOTP MFA.
If a user has signed up for multiple MFA options, and if they unenrollfrom the most recently enabled option, they receive an auth/user-token-expired
and are logged out. The user must sign in again and verify theirexisting credentials—for example, an email address and password.
To unenroll the user, handle the error, and trigger reauthentication, use thefollowing code:
Firebase.auth.currentUser.multiFactor.unenroll(mfaEnrollmentId) .addOnSuccessListener { // Second factor unenrolled. } .addOnFailureListener { exception -> when (exception) { is FirebaseAuthInvalidUserException -> { // Second factor unenrolled. If the user was signed out, re-authenticate // them. // For example, if they signed in with a password, prompt them to // provide it again, then call `reauthenticateWithCredential()` as shown // below. val credential = EmailAuthProvider.getCredential(email, password) currentUser.reauthenticate(credential) .addOnSuccessListener { // Success! } .addOnFailureListener { // Bad email address and password combination. } } } }
What's next
- Manage multi-factor usersprogrammatically with the Admin SDK.