Como trabalhar com usuários de vários fatores
Neste documento, você aprenderá como realizar tarefas comuns com usuários do Identity Platform inscritos na autenticação multifator.
Como atualizar o e-mail de um usuário
Os usuários multifator precisam sempre ter um endereço de e-mail verificado. Isso impede que agentes mal-intencionados se inscrevam no seu app com um e-mail que não pertencem a eles e bloqueie o proprietário real adicionando um segundo fator.
Para atualizar o e-mail de um usuário, use o método verifyBeforeUpdateEmail()
. Diferentemente
de updateEmail()
, este método requer que o usuário siga um link de verificação
antes que o Identity Platform atualize o endereço de e-mail. Exemplo:
Versão 8 para a Web
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.
});
Versão 9 para a Web
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.
}
}
});
Por padrão, o Identity Platform envia um e-mail ao usuário e fornece um gerenciador simples, baseado na Web, para processar a verificação. É possível personalizar esse fluxo de algumas maneiras.
Localização dos e-mails de verificação
Para localizar os e-mails enviados pelo Identity Platform, defina o código
do idioma antes de chamar verifyBeforeUpdateEmail()
:
Versão 8 para a Web
firebase.auth().languageCode = 'fr';
Versão 9 para a Web
import { getAuth } from "firebase/auth";
const auth = getAuth();
auth.languageCode = 'fr';
iOS
Auth.auth().languageCode = 'fr';
Android
FirebaseAuth.getInstance().setLanguageCode("fr");
Como passar um estado adicional
É possível usar as configurações de código de ação para incluir um estado adicional no e-mail de verificação ou processar a verificação em um app para dispositivos móveis. Por exemplo:
Versão 8 para a Web
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.
});
Versão 9 para a Web
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.
}
}
});
Como personalizar o gerenciador de verificação
Você pode criar seu próprio gerenciador para processar a verificação de e-mail. Veja no exemplo a seguir como verificar um código de ação e inspecionar os metadados antes de aplicá-lo:
Versão 8 para a Web
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.
});
Versão 9 para a Web
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.
}
}
});
Para saber mais, consulte o documento do Firebase sobre Como criar gerenciadores de ações de e-mail personalizados.
Como autenticar novamente um usuário
Mesmo que um usuário já esteja conectado, reautentique-o antes de executar operações confidenciais, como:
- Alterar uma senha.
- Adicionar ou remover um novo fator.
- Atualizar informações pessoais (como um endereço).
- Executar transações financeiras.
- Excluir a conta de um usuário.
Para autenticar novamente um usuário com e-mail e senha:
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.
}
}
});
Para autenticar novamente usando um provedor OAuth, como a 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.
}
}
});
Revogar um segundo fator adicionado recentemente
Quando um usuário inscreve um segundo fator, o Identity Platform envia uma notificação para seu e-mail. Para proteger contra atividades não autorizadas, o e-mail inclui uma opção para reverter a adição de um segundo fator.
O Identity Platform fornece um modelo de e-mail e um gerenciador padrão, mas você também pode criar seu próprio. O exemplo a seguir mostra como criar um gerenciador personalizado:
Versão 8 para a Web
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.
});
Versão 9 para a Web
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.
}
}
});
Para saber mais, consulte o documento do Firebase sobre Como criar gerenciadores de ações de e-mail personalizados.
Como recuperar um segundo fator
O Identity Platform não fornece um mecanismo integrado para recuperar segundos fatores. Se um usuário perder o acesso ao segundo fator, ele será bloqueado da conta. Para evitar que isso aconteça, considere o seguinte:
- Avise os usuários que eles perderão o acesso à conta sem o segundo fator.
- Incentivamos os usuários a registrar um fator secundário de backup.
- Usando o SDK Admin para criar um fluxo de recuperação que desativa a autenticação multifator se o usuário puder verificar a identidade dele (por exemplo, fazendo upload de uma chave de recuperação ou respondendo perguntas pessoais).
- Conceder à sua equipe de suporte a capacidade de gerenciar contas de usuário (incluindo a remoção de segundos de fatores) e oferecer uma opção para entrar em contato com eles, caso eles estejam bloqueados.
A redefinição de senha não permitirá que um usuário ignore a autenticação multifator.
Se você redefinir a senha de um usuário usando sendPasswordResetEmail()
, ele ainda
precisará passar pelo desafio multifator ao fazer login
com a nova senha.
Como cancelar a inscrição de um segundo fator
Para cancelar a inscrição de um segundo fator, acesse-o na lista de fatores inscritos
do usuário e, em seguida, chame unenroll()
. Como essa é uma operação confidencial, você
precisará autenticar novamente o usuário primeiro se ele não tiver feito login recentemente.
Versão 8 para a Web
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.
});
Versão 9 para a Web
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.
}
}
});
Em alguns casos, o usuário pode ser desconectado depois de remover um segundo fator.
Use onAuthStateChanged()
para ouvir este caso e peça
ao usuário para fazer login novamente.
A seguir
Adicione a autenticação multifator ao seu app da Web, iOS ou Android.
Gerencie usuários multifator de maneira programática.