Attivare l'autenticazione a più fattori basata su TOTP per la tua app
Questo documento descrive come aggiungere l'autenticazione a più fattori (MFA) con password monouso basata sul tempo (TOTP) alla tua app.
Identity Platform ti consente di utilizzare un TOTP come fattore aggiuntivo per l'MFA. Quando attivi questa funzionalità, gli utenti che tentano di accedere alla tua app vedono una richiesta di TOTP. Per generarlo, deve utilizzare un'app di autenticazione in grado di generare codici TOTP validi, come Google Authenticator.
Prima di iniziare
Attiva almeno un provider che supporti l'autenticazione a più fattori. Tieni presente che tutti i fornitori tranne i seguenti supportano l'autenticazione a più fattori:
- Autenticazione telefonica
- Autenticazione anonima
- Token di autenticazione personalizzati
- Apple Game Center
Assicurati che la tua app verifichi gli indirizzi email degli utenti. La MFA richiede la verifica dell'email. In questo modo si impedisce ai malintenzionati di registrarsi a un servizio con un indirizzo email che non possiedono e di bloccare il vero proprietario dell'indirizzo email aggiungendo un secondo fattore.
Assicurati di avere la versione della piattaforma corretta. L'autenticazione a più fattori TOTP è supportata solo nelle seguenti versioni dell'SDK:
Piattaforma Versioni SDK web v9.19.1+ SDK Android 22.1.0+ SDK iOS 10.12.0+
Abilitare l'autenticazione a più fattori basata su TOTP a livello di progetto
Per abilitare TOTP come secondo fattore, utilizza l'SDK Admin o chiama l'endpoint REST di configurazione del progetto.
Per utilizzare l'SDK Admin:
Se non l'hai ancora fatto, installa l'SDK Firebase Admin Node.js.
L'autenticazione a più fattori TOTP è supportata solo nelle versioni 11.6.0 e successive dell'SDK Firebase Admin Node.js.
Esegui questo comando:
import { getAuth } from 'firebase-admin/auth'; getAuth().projectConfigManager().updateProjectConfig( { multiFactorConfig: { providerConfigs: [{ state: "ENABLED", totpProviderConfig: { adjacentIntervals: NUM_ADJ_INTERVALS } }] } })Sostituisci quanto segue:
NUM_ADJ_INTERVALS: Il numero di intervalli di finestre temporali adiacenti da cui accettare i TOTP, da zero a dieci. Il valore predefinito è cinque.I TOTP funzionano garantendo che quando due parti (il prover e il validatore) generano OTP nella stessa finestra temporale (in genere di 30 secondi di durata), generano la stessa password. Tuttavia, per tenere conto della deriva dell'orologio tra le parti e del tempo di risposta umano, puoi configurare il servizio TOTP in modo che accetti anche i TOTP delle finestre adiacenti.
Per abilitare l'autenticazione MFA TOTP utilizzando l'API REST, esegui il comando seguente:
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
}
}]
}
}'
Sostituisci quanto segue:
PROJECT_ID: l'ID progetto.NUM_ADJ_INTERVALS: Il numero di intervalli della finestra temporale, da zero a dieci. Il valore predefinito è cinque.I TOTP funzionano garantendo che quando due parti (il prover e il validatore) generano OTP nella stessa finestra temporale (in genere di 30 secondi di durata), generano la stessa password. Tuttavia, per tenere conto della deriva dell'orologio tra le parti e del tempo di risposta umano, puoi configurare il servizio TOTP in modo che accetti anche i TOTP delle finestre adiacenti.
Attivare l'autenticazione a più fattori basata su TOTP a livello di tenant
Per attivare TOTP come secondo fattore per l'autenticazione MFA a livello di tenant, utilizza il seguente codice:
getAuth().tenantManager().updateTenant(TENANT_ID,
{
multiFactorConfig: {
state: 'ENABLED',
providerConfigs: [{
totpProviderConfig: {
adjacentIntervals: NUM_ADJ_INTERVALS
}
}]
}
})
Sostituisci quanto segue:
TENANT_ID: l'ID tenant stringa.NUM_ADJ_INTERVALS: Il numero di intervalli della finestra temporale, da zero a dieci. Il valore predefinito è cinque.
Per abilitare l'autenticazione MFA TOTP utilizzando l'API REST a livello di tenant, esegui il comando seguente:
curl -X PATCH "https://identitytoolkit.googleapis.com/v2/projects/PROJECT_ID/tenants/TENANT_ID?updateMask=mfaConfig" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
-H "X-Goog-User-Project: PROJECT_ID" \
-d \
'{
"mfaConfig": {
"providerConfigs": [{
"totpProviderConfig": {
"adjacentIntervals": NUM_ADJ_INTERVALS
}
}]
}
}'
Sostituisci quanto segue:
PROJECT_ID: l'ID progetto.TENANT_ID: l'ID tenant.NUM_ADJ_INTERVALS: Il numero di intervalli della finestra temporale, da zero a dieci. Il valore predefinito è cinque.
Scegliere un pattern di registrazione
Puoi scegliere se la tua app richiede l'autenticazione a più fattori e come e quando registrare i tuoi utenti. Alcuni pattern comuni includono:
Registra il secondo fattore dell'utente durante la registrazione. Utilizza questo metodo se la tua app richiede l'autenticazione a più fattori per tutti gli utenti.
Offri un'opzione ignorabile per registrare un secondo fattore durante la registrazione. Se vuoi incoraggiare, ma non richiedere, l'autenticazione a più fattori nella tua app, puoi utilizzare questo approccio.
Offrire la possibilità di aggiungere un secondo fattore dalla pagina di gestione dell'account o del profilo dell'utente, anziché dalla schermata di registrazione. In questo modo, l'attrito durante la procedura di registrazione viene ridotto al minimo, pur rendendo disponibile l'autenticazione a più fattori per gli utenti sensibili alla sicurezza.
Richiedi l'aggiunta di un secondo fattore in modo incrementale quando l'utente vuole accedere a funzionalità con requisiti di sicurezza più elevati.
Registra utenti in MFA TOTP
Dopo aver abilitato l'autenticazione a più fattori basata su TOTP come secondo fattore per la tua app, implementa la logica lato client per registrare gli utenti all'autenticazione a più fattori basata su TOTP:
Web
import {
multiFactor,
TotpMultiFactorGenerator,
TotpSecret
} from "firebase/auth";
multiFactorSession = await multiFactor(activeUser()).getSession();
totpSecret = await TotpMultiFactorGenerator.generateSecret(
multiFactorSession
);
// Display this URL as a QR code.
const url = totpSecret.generateQrCodeUrl( <user account id> , <app name> );
// Ask the user for the verification code from the OTP app by scanning the QR
// code.
const multiFactorAssertion = TotpMultiFactorGenerator.assertionForEnrollment(
totpSecret,
verificationCode
);
// Finalize the enrollment.
return multiFactor(user).enroll(multiFactorAssertion, mfaDisplayName);
Java
user.getMultiFactor().getSession()
.addOnCompleteListener(
new OnCompleteListener<MultiFactorSession>() {
@Override
public void onComplete(@NonNull Task<MultiFactorSession> task) {
if (task.isSuccessful()) {
// Get a multi-factor session for the user.
MultiFactorSession multiFactorSession = task.getResult();
TotpMultiFactorGenerator.generateSecret(multiFactorSession)
.addOnCompleteListener(
new OnCompleteListener<TotpSecret>() {
@Override
public void onComplete(@NonNull Task<TotpSecret> task){
if (task.isSuccessful()) {
TotpSecret secret = task.getResult();
// Display this URL as a QR code for the user to scan.
String qrCodeUrl = secret.generateQrCodeUrl();
// Display the QR code
// ...
// Alternatively, you can automatically load the QR code
// into a TOTP authenticator app with either default or
// specified fallback URL and activity.
// Default fallback URL and activity.
secret.openInOtpApp(qrCodeUrl);
// Specified fallback URL and activity.
// secret.openInOtpApp(qrCodeUrl, fallbackUrl, activity);
}
}
});
}
}
});
// Ask the user for the one-time password (otp) from the TOTP authenticator app.
MultiFactorAssertion multiFactorAssertion =
TotpMultiFactorGenerator.getAssertionForEnrollment(
secret, otp);
// Complete the enrollment.
user
.getMultiFactor()
.enroll(multiFactorAssertion, /* displayName= */ "My TOTP second factor")
.addOnCompleteListener(
new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
showToast("Successfully enrolled TOTP second factor!");
setResult(Activity.RESULT_OK);
finish();
}
}
});
Kotlin+KTX
user.multiFactor.session.addOnCompleteListener { task ->
if (task.isSuccessful) {
// Get a multi-factor session for the user.
val session: MultiFactorSession = task.result
val secret: TotpSecret = TotpMultiFactorGenerator.generateSecret(session)
// Display this URL as a QR code for the user to scan.
val qrCodeUrl = secret.generateQrCodeUrl()
// Display the QR code
// ...
// Alternatively, you can automatically load the QR code
// into a TOTP authenticator app with either default or
// specified fallback URL and activity.
// Default fallback URL and activity.
secret.openInOtpApp(qrCodeUrl)
// Specify a fallback URL and activity.
// secret.openInOtpApp(qrCodeUrl, fallbackUrl, activity);
}
}
// Ask the user for the one-time password (otp) from the TOTP authenticator app.
val multiFactorAssertion =
TotpMultiFactorGenerator.getAssertionForEnrollment(secret, otp)
// Complete enrollment.
user.multiFactor.enroll(multiFactorAssertion, /* displayName= */ "My TOTP second factor")
.addOnCompleteListener {
// ...
}
Swift
let user = Auth.auth().currentUser
// Get a multi-factor session for the user
user?.multiFactor.getSessionWithCompletion({ (session, error) in
TOTPMultiFactorGenerator.generateSecret(with: session!) {
(secret, error) in
let accountName = user?.email;
let issuer = Auth.auth().app?.name;
// Generate a QR code
let qrCodeUrl = secret?.generateQRCodeURL(withAccountName: accountName!, issuer: issuer!)
// Display the QR code
// ...
// Alternatively, you can automatically load the QR code
// into a TOTP authenticator app with default fallback UR
// and activity.
secret?.openInOTPAppWithQRCodeURL(qrCodeUrl!);
// Ask the user for the verification code after scanning
let assertion = TOTPMultiFactorGenerator.assertionForEnrollment(with: secret, oneTimePassword: onetimePassword)
// Complete the enrollment
user?.multiFactor.enroll(with: assertion, displayName: accountName) { (error) in
// ...
}
}
})
Objective-C
FIRUser *user = FIRAuth.auth.currentUser;
// Get a multi-factor session for the user
[user.multiFactor getSessionWithCompletion:^(FIRMultiFactorSession *_Nullable session, NSError *_Nullable error) {
// ...
[FIRTOTPMultiFactorGenerator generateSecretWithMultiFactorSession:session completion:^(FIRTOTPSecret *_Nullable secret, NSError *_Nullable error) {
NSString *accountName = user.email;
NSString *issuer = FIRAuth.auth.app.name;
// Generate a QR code
NSString *qrCodeUrl = [secret generateQRCodeURLWithAccountName:accountName issuer:issuer];
// Display the QR code
// ...
// Alternatively, you can automatically load the QR code
// into a TOTP authenticator app with default fallback URL
// and activity.
[secret openInOTPAppWithQRCodeURL:qrCodeUrl];
// Ask the user for the verification code after scanning
FIRTOTPMultiFactorAssertion *assertion = [FIRTOTPMultiFactorGenerator assertionForEnrollmentWithSecret:secret oneTimePassword:oneTimePassword];
// Complete the enrollment
[user.multiFactor enrollWithAssertion:assertion
displayName:displayName
completion:^(NSError *_Nullable error) {
// ...
}];
}];
}];
Accedere con un secondo fattore
Per consentire l'accesso degli utenti con l'autenticazione a più fattori TOTP, utilizza il seguente codice:
Web
import {
getAuth,
getMultiFactorResolver,
TotpMultiFactorGenerator,
PhoneMultiFactorGenerator,
signInWithEmailAndPassword
} from "firebase/auth";
const auth = getAuth();
signInWithEmailAndPassword(auth, email, password)
.then(function(userCredential) {
// The user is not enrolled with a second factor and is successfully
// signed in.
// ...
})
.catch(function(error) {
if (error.code === 'auth/multi-factor-auth-required') {
const resolver = getMultiFactorResolver(auth, error);
// Ask the user which second factor to use.
if (resolver.hints[selectedIndex].factorId ===
TotpMultiFactorGenerator.FACTOR_ID) {
// Ask the user for the OTP code from the TOTP app.
const multiFactorAssertion = TotpMultiFactorGenerator.assertionForSignIn(resolver.hints[selectedIndex].uid, otp);
// Finalize the sign-in.
return resolver.resolveSignIn(multiFactorAssertion).then(function(userCredential) {
// The user successfully signed in with the TOTP second factor.
});
} else if (resolver.hints[selectedIndex].factorId ===
PhoneMultiFactorGenerator.FACTOR_ID) {
// Handle the phone MFA.
} else {
// The second factor is unsupported.
}
}
// Handle other errors, such as a wrong password.
else if (error.code == 'auth/wrong-password') {
//...
}
});
Java
FirebaseAuth.getInstance()
.signInWithEmailAndPassword(email, password)
.addOnCompleteListener(
new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// The user is not enrolled with a second factor and is
// successfully signed in.
// ...
return;
}
if (task.getException() instanceof FirebaseAuthMultiFactorException) {
// The user is a multi-factor user. Second factor challenge is required.
FirebaseAuthMultiFactorException error =
(FirebaseAuthMultiFactorException) task.getException();
MultiFactorResolver multiFactorResolver = error.getResolver();
// Display the list of enrolled second factors, user picks one (selectedIndex) from the list.
MultiFactorInfo selectedHint = multiFactorResolver.getHints().get(selectedIndex);
if (selectedHint.getFactorId().equals(TotpMultiFactorGenerator.FACTOR_ID)) {
// Ask the user for the one-time password (otp) from the TOTP app.
// Initialize a MultiFactorAssertion object with the one-time password and enrollment id.
MultiFactorAssertion multiFactorAssertion =
TotpMultiFactorGenerator.getAssertionForSignIn(selectedHint.getUid(), otp);
// Complete sign-in.
multiFactorResolver
.resolveSignIn(multiFactorAssertion)
.addOnCompleteListener(
new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// User successfully signed in with the
// TOTP second factor.
}
}
});
} else if (selectedHint.getFactorId().equals(PhoneMultiFactorGenerator.FACTOR_ID)) {
// Handle Phone MFA.
} else {
// Unsupported second factor.
}
} else {
// Handle other errors such as wrong password.
}
}
});
Kotlin+KTX
FirebaseAuth.getInstance
.signInWithEmailAndPassword(email, password)
.addOnCompleteListener{ task ->
if (task.isSuccessful) {
// User is not enrolled with a second factor and is successfully
// signed in.
// ...
}
if (task.exception is FirebaseAuthMultiFactorException) {
// The user is a multi-factor user. Second factor challenge is
// required.
val multiFactorResolver:MultiFactorResolver =
(task.exception as FirebaseAuthMultiFactorException).resolver
// Display the list of enrolled second factors, user picks one (selectedIndex) from the list.
val selectedHint: MultiFactorInfo = multiFactorResolver.hints[selectedIndex]
if (selectedHint.factorId == TotpMultiFactorGenerator.FACTOR_ID) {
val multiFactorAssertion =
TotpMultiFactorGenerator.getAssertionForSignIn(selectedHint.uid, otp)
multiFactorResolver.resolveSignIn(multiFactorAssertion)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
// User successfully signed in with the
// TOTP second factor.
}
// ...
}
} else if (selectedHint.factor == PhoneMultiFactorGenerator.FACTOR_ID) {
// Handle Phone MFA.
} else {
// Invalid MFA option.
}
} else {
// Handle other errors, such as wrong password.
}
}
Swift
Auth.auth().signIn(withEmail: email, password: password) {
(result, error) in
if (error != nil) {
let authError = error! as NSError
if authError.code == AuthErrorCode.secondFactorRequired.rawValue {
let resolver = authError.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver
if resolver.hints[selectedIndex].factorID == TOTPMultiFactorID {
let assertion = TOTPMultiFactorGenerator.assertionForSignIn(withEnrollmentID: resolver.hints[selectedIndex].uid, oneTimePassword: oneTimePassword)
resolver.resolveSignIn(with: assertion) {
(authResult, error) in
if (error != nil) {
// User successfully signed in with second factor TOTP.
}
}
} else if (resolver.hints[selectedIndex].factorID == PhoneMultiFactorID) {
// User selected a phone second factor.
// ...
} else {
// Unsupported second factor.
// Note that only phone and TOTP second factors are currently supported.
// ...
}
}
}
else {
// The user is not enrolled with a second factor and is
// successfully signed in.
// ...
}
}
Objective-C
[FIRAuth.auth signInWithEmail:email
password:password
completion:^(FIRAuthDataResult * _Nullable authResult,
NSError * _Nullable error) {
if (error == nil || error.code != FIRAuthErrorCodeSecondFactorRequired) {
// User is not enrolled with a second factor and is successfully signed in.
// ...
} else {
// The user is a multi-factor user. Second factor challenge is required.
[self signInWithMfaWithError:error];
}
}];
- (void)signInWithMfaWithError:(NSError * _Nullable)error{
FIRMultiFactorResolver *resolver = error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];
// Ask user which second factor to use. Then:
FIRMultiFactorInfo *hint = (FIRMultiFactorInfo *) resolver.hints[selectedIndex];
if (hint.factorID == FIRTOTPMultiFactorID) {
// User selected a totp second factor.
// Ask user for verification code.
FIRMultiFactorAssertion *assertion = [FIRTOTPMultiFactorGenerator assertionForSignInWithEnrollmentID:hint.UID oneTimePassword:oneTimePassword];
[resolver resolveSignInWithAssertion:assertion
completion:^(FIRAuthDataResult *_Nullable authResult,
NSError *_Nullable error) {
if (error != nil) {
// User successfully signed in with the second factor TOTP.
}
}];
} else if (hint.factorID == FIRPhoneMultiFactorID) {
// User selected a phone second factor.
// ...
}
else {
// Unsupported second factor.
// Note that only phone and totp second factors are currently supported.
}
}
L'esempio precedente utilizza l'email e la password come primo fattore.
Annullare la registrazione all'autenticazione a più fattori TOTP
Questa sezione descrive come gestire l'annullamento della registrazione di un utente all'autenticazione a più fattori TOTP.
Se un utente ha eseguito la registrazione a più opzioni MFA e annulla la registrazione
dell'opzione attivata più di recente, riceve un auth/user-token-expired
e viene disconnesso. L'utente deve accedere di nuovo e verificare le proprie
credenziali esistenti, ad esempio un indirizzo email e una password.
Per annullare la registrazione dell'utente, gestire l'errore e attivare la riautenticazione, utilizza il seguente codice:
Web
import {
EmailAuthProvider,
TotpMultiFactorGenerator,
getAuth,
multiFactor,
reauthenticateWithCredential,
} from "firebase/auth";
try {
// Unenroll from TOTP MFA.
await multiFactor(currentUser).unenroll(mfaEnrollmentId);
} catch (error) {
if (error.code === 'auth/user-token-expired') {
// 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.
const credential = EmailAuthProvider.credential(email, password);
await reauthenticateWithCredential(
currentUser,
credential
);
}
}
Java
List<MultiFactorInfo> multiFactorInfoList = user.getMultiFactor().getEnrolledFactors();
// Select the second factor to unenroll
user
.getMultiFactor()
.unenroll(selectedMultiFactorInfo)
.addOnCompleteListener(
new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
// User successfully unenrolled the selected second factor.
}
else {
if (task.getException() instanceof FirebaseAuthInvalidUserException) {
// Handle reauthentication
}
}
}
});
Kotlin+KTX
val multiFactorInfoList = user.multiFactor.enrolledFactors
// Select the option to unenroll
user.multiFactor.unenroll(selectedMultiFactorInfo)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
// User successfully unenrolled the selected second factor.
}
else {
if (task.exception is FirebaseAuthInvalidUserException) {
// Handle reauthentication
}
}
}
Swift
user?.multiFactor.unenroll(with: (user?.multiFactor.enrolledFactors[selectedIndex])!,
completion: { (error) in
if (error.code == AuthErrorCode.userTokenExpired.rawValue) {
// Handle reauthentication
}
})
Objective-C
FIRMultiFactorInfo *unenrolledFactorInfo;
for (FIRMultiFactorInfo *enrolledFactorInfo in FIRAuth.auth.currentUser.multiFactor.enrolledFactors) {
// Pick one of the enrolled factors to delete.
}
[FIRAuth.auth.currentUser.multiFactor unenrollWithInfo:unenrolledFactorInfo
completion:^(NSError * _Nullable error) {
if (error.code == FIRAuthErrorCodeUserTokenExpired) {
// Handle reauthentication
}
}];
Passaggi successivi
- Gestisci gli utenti con autenticazione a più fattori in modo programmatico con l'SDK Admin.