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
-
Add multi-factor authentication to your web , iOS , or Android app.
-
Manage multi-factor users programmatically .