Ikuti semua informasi yang diumumkan di Firebase Summit, dan pelajari bagaimana Firebase dapat membantu Anda mempercepat pengembangan aplikasi dan menjalankan aplikasi dengan percaya diri. Pelajari Lebih Lanjut

Tambahkan otentikasi multi-faktor ke aplikasi web Anda

Jika Anda telah mengupgrade ke Firebase Authentication dengan Identity Platform, Anda dapat menambahkan autentikasi multi-faktor SMS ke aplikasi web Anda.

Otentikasi multi-faktor meningkatkan keamanan aplikasi Anda. Sementara penyerang sering mengkompromikan kata sandi dan akun sosial, penyadapan pesan teks lebih sulit.

Sebelum kamu memulai

  1. Aktifkan setidaknya satu penyedia yang mendukung autentikasi multi-faktor. Setiap penyedia mendukung MFA, kecuali autentikasi telepon, autentikasi anonim, dan Apple Game Center.

  2. Pastikan aplikasi Anda memverifikasi email pengguna. MFA memerlukan verifikasi email. Ini mencegah pelaku jahat mendaftar ke layanan dengan email yang tidak mereka miliki, dan kemudian mengunci pemilik sebenarnya dengan menambahkan faktor kedua.

Menggunakan multi-tenancy

Jika Anda mengaktifkan otentikasi multi-faktor untuk digunakan di lingkungan multi-penyewa , pastikan untuk menyelesaikan langkah-langkah berikut (selain petunjuk lainnya dalam dokumen ini):

  1. Di GCP Console, pilih tenant yang ingin Anda ajak bekerja sama.

  2. Dalam kode Anda, setel bidang tenantId pada instance Auth ke ID tenant Anda. Sebagai contoh:

    Web version 9

    import { getAuth } from "firebase/auth";
    
    const auth = getAuth(app);
    auth.tenantId = "myTenantId1";
    

    Web version 8

    firebase.auth().tenantId = 'myTenantId1';
    

Mengaktifkan otentikasi multi-faktor

  1. Buka halaman Authentication > Sign-in method dari Firebase console.

  2. Di bagian Lanjutan , aktifkan Otentikasi Multi-faktor SMS .

    Anda juga harus memasukkan nomor telepon yang akan digunakan untuk menguji aplikasi Anda. Meskipun opsional, mendaftarkan nomor telepon uji sangat disarankan untuk menghindari pembatasan selama pengembangan.

  3. Jika Anda belum mengotorisasi domain aplikasi, tambahkan domain tersebut ke daftar yang diizinkan di halaman Authentication > Settings di Firebase console.

Memilih pola pendaftaran

Anda dapat memilih apakah aplikasi Anda memerlukan autentikasi multi-faktor, serta bagaimana dan kapan mendaftarkan pengguna Anda. Beberapa pola umum meliputi:

  • Daftarkan faktor kedua pengguna sebagai bagian dari pendaftaran. Gunakan metode ini jika aplikasi Anda memerlukan autentikasi multi-faktor untuk semua pengguna.

  • Tawarkan opsi yang dapat dilewati untuk mendaftarkan faktor kedua selama pendaftaran. Aplikasi yang ingin mendorong, tetapi tidak memerlukan, autentikasi multi-faktor mungkin lebih menyukai pendekatan ini.

  • Memberikan kemampuan untuk menambahkan faktor kedua dari akun pengguna atau halaman pengelolaan profil, bukan dari layar pendaftaran. Ini meminimalkan gesekan selama proses pendaftaran, sambil tetap membuat otentikasi multi-faktor tersedia untuk pengguna yang sensitif terhadap keamanan.

  • Memerlukan penambahan faktor kedua secara bertahap saat pengguna ingin mengakses fitur dengan persyaratan keamanan yang ditingkatkan.

Menyiapkan pemverifikasi reCAPTCHA

Sebelum Anda dapat mengirim kode SMS, Anda perlu mengonfigurasi pemverifikasi reCAPTCHA. Firebase menggunakan reCAPTCHA untuk mencegah penyalahgunaan dengan memastikan bahwa permintaan verifikasi nomor telepon berasal dari salah satu domain yang diizinkan aplikasi Anda.

Anda tidak perlu menyiapkan klien reCAPTCHA secara manual; objek RecaptchaVerifier SDK klien secara otomatis membuat dan menginisialisasi kunci dan rahasia klien yang diperlukan.

Menggunakan reCAPTCHA yang tidak terlihat

Objek RecaptchaVerifier mendukung reCAPTCHA yang tidak terlihat , yang sering dapat memverifikasi pengguna tanpa memerlukan interaksi apa pun. Untuk menggunakan reCAPTCHA tak terlihat, buat RecaptchaVerifier dengan parameter size disetel ke invisible , dan tentukan ID elemen UI yang memulai pendaftaran multi-faktor:

Web version 9

import { RecaptchaVerifier } from "firebase/auth";

const recaptchaVerifier = new RecaptchaVerifier("sign-in-button", {
    "size": "invisible",
    "callback": function(response) {
        // reCAPTCHA solved, you can proceed with
        // phoneAuthProvider.verifyPhoneNumber(...).
        onSolvedRecaptcha();
    }
}, auth);

Web version 8

var recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
'size': 'invisible',
'callback': function(response) {
  // reCAPTCHA solved, you can proceed with phoneAuthProvider.verifyPhoneNumber(...).
  onSolvedRecaptcha();
}
});

Menggunakan widget reCAPTCHA

Untuk menggunakan widget reCAPTCHA yang terlihat, buat elemen HTML untuk memuat widget, lalu buat objek RecaptchaVerifier dengan ID penampung UI. Anda juga dapat secara opsional menyetel callback yang dipanggil saat reCAPTCHA diselesaikan atau kedaluwarsa:

Web version 9

import { RecaptchaVerifier } from "firebase/auth";

const recaptchaVerifier = new RecaptchaVerifier(
    "recaptcha-container",

    // Optional reCAPTCHA parameters.
    {
      "size": "normal",
      "callback": function(response) {
        // reCAPTCHA solved, you can proceed with 
        // phoneAuthProvider.verifyPhoneNumber(...).
        onSolvedRecaptcha();
      },
      "expired-callback": function() {
        // Response expired. Ask user to solve reCAPTCHA again.
        // ...
      }
    }, auth
);

Web version 8

var recaptchaVerifier = new firebase.auth.RecaptchaVerifier(
  'recaptcha-container',
  // Optional reCAPTCHA parameters.
  {
    'size': 'normal',
    'callback': function(response) {
      // reCAPTCHA solved, you can proceed with phoneAuthProvider.verifyPhoneNumber(...).
      // ...
      onSolvedRecaptcha();
    },
    'expired-callback': function() {
      // Response expired. Ask user to solve reCAPTCHA again.
      // ...
    }
  });

Pra-render reCAPTCHA

Secara opsional, Anda dapat melakukan pra-render reCAPTCHA sebelum memulai pendaftaran dua faktor:

Web version 9

recaptchaVerifier.render()
    .then(function (widgetId) {
        window.recaptchaWidgetId = widgetId;
    });

Web version 8

recaptchaVerifier.render()
  .then(function(widgetId) {
    window.recaptchaWidgetId = widgetId;
  });

Setelah render() diselesaikan, Anda mendapatkan ID widget reCAPTCHA, yang dapat Anda gunakan untuk melakukan panggilan ke reCAPTCHA API :

var recaptchaResponse = grecaptcha.getResponse(window.recaptchaWidgetId);

Mendaftarkan faktor kedua

Untuk mendaftarkan faktor sekunder baru bagi pengguna:

  1. Otentikasi ulang pengguna.

  2. Minta pengguna memasukkan nomor telepon mereka.

  3. Inisialisasi pemverifikasi reCAPTCHA seperti yang diilustrasikan di bagian sebelumnya. Lewati langkah ini jika instance RecaptchaVerifier sudah dikonfigurasi:

    Web version 9

    import { RecaptchaVerifier } from "firebase/auth";
    
    const recaptchaVerifier = new RecaptchaVerifier('recaptcha-container-id', undefined, auth);
    

    Web version 8

    var recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container-id');
    
  4. Dapatkan sesi multi-faktor untuk pengguna:

    Web version 9

    import { multiFactor } from "firebase/auth";
    
    multiFactor(user).getSession().then(function (multiFactorSession) {
        // ...
    });
    

    Web version 8

    user.multiFactor.getSession().then(function(multiFactorSession) {
      // ...
    })
    
  5. Inisialisasi objek PhoneInfoOptions dengan nomor telepon pengguna dan sesi multi-faktor:

    Web version 9

    // Specify the phone number and pass the MFA session.
    const phoneInfoOptions = {
      phoneNumber: phoneNumber,
      session: multiFactorSession
    };
    

    Web version 8

    // Specify the phone number and pass the MFA session.
    var phoneInfoOptions = {
      phoneNumber: phoneNumber,
      session: multiFactorSession
    };
    
  6. Kirim pesan verifikasi ke ponsel pengguna:

    Web version 9

    import { PhoneAuthProvider } from "firebase/auth";
    
    const phoneAuthProvider = new PhoneAuthProvider(auth);
    phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
        .then(function (verificationId) {
            // verificationId will be needed to complete enrollment.
        });
    

    Web version 8

    var phoneAuthProvider = new firebase.auth.PhoneAuthProvider();
    // Send SMS verification code.
    return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
      .then(function(verificationId) {
        // verificationId will be needed for enrollment completion.
      })
    

    Meskipun tidak diwajibkan, merupakan praktik terbaik untuk memberi tahu pengguna sebelumnya bahwa mereka akan menerima pesan SMS, dan tarif standar berlaku.

  7. Jika permintaan gagal, setel ulang reCAPTCHA, lalu ulangi langkah sebelumnya sehingga pengguna dapat mencoba lagi. Perhatikan bahwa verifyPhoneNumber() akan secara otomatis menyetel ulang reCAPTCHA saat muncul kesalahan, karena token reCAPTCHA hanya digunakan satu kali.

    Web version 9

    grecaptcha.reset(window.recaptchaWidgetId);
    
    // Or, if you haven't stored the widget ID:
    recaptchaVerifier.render()
      .then(function(widgetId) {
        grecaptcha.reset(widgetId);
      });
    

    Web version 8

    grecaptcha.reset(window.recaptchaWidgetId);
    
    // Or, if you haven't stored the widget ID:
    recaptchaVerifier.render()
      .then(function(widgetId) {
        grecaptcha.reset(widgetId);
      });
    
  8. Setelah kode SMS dikirim, minta pengguna untuk memverifikasi kode:

    Web version 9

    // Ask user for the verification code. Then:
    const cred = PhoneAuthProvider.credential(verificationId, verificationCode);
    

    Web version 8

    // Ask user for the verification code. Then:
    var cred = firebase.auth.PhoneAuthProvider.credential(verificationId, verificationCode);
    
  9. Inisialisasi objek MultiFactorAssertion dengan PhoneAuthCredential :

    Web version 9

    import { PhoneMultiFactorGenerator } from "firebase/auth";
    
    const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);
    

    Web version 8

    var multiFactorAssertion = firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
    
  10. Lengkapi pendaftaran. Secara opsional, Anda dapat menentukan nama tampilan untuk faktor kedua. Ini berguna untuk pengguna dengan beberapa faktor detik, karena nomor telepon disembunyikan selama alur autentikasi (misalnya, +1******1234).

    Web version 9

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    multiFactor(user).enroll(multiFactorAssertion, "My personal phone number");
    

    Web version 8

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    user.multiFactor.enroll(multiFactorAssertion, 'My personal phone number');
    

Kode di bawah ini menunjukkan contoh lengkap mendaftarkan faktor kedua:

Web version 9

import {
    multiFactor, PhoneAuthProvider, PhoneMultiFactorGenerator,
    RecaptchaVerifier
} from "firebase/auth";

const recaptchaVerifier = new RecaptchaVerifier('recaptcha-container-id', undefined, auth);
multiFactor(user).getSession()
    .then(function (multiFactorSession) {
        // Specify the phone number and pass the MFA session.
        const phoneInfoOptions = {
            phoneNumber: phoneNumber,
            session: multiFactorSession
        };

        const phoneAuthProvider = new PhoneAuthProvider(auth);

        // Send SMS verification code.
        return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier);
    }).then(function (verificationId) {
        // Ask user for the verification code. Then:
        const cred = PhoneAuthProvider.credential(verificationId, verificationCode);
        const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);

        // Complete enrollment.
        return multiFactor(user).enroll(multiFactorAssertion, mfaDisplayName);
    });

Web version 8

var recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container-id');
user.multiFactor.getSession().then(function(multiFactorSession) {
  // Specify the phone number and pass the MFA session.
  var phoneInfoOptions = {
    phoneNumber: phoneNumber,
    session: multiFactorSession
  };
  var phoneAuthProvider = new firebase.auth.PhoneAuthProvider();
  // Send SMS verification code.
  return phoneAuthProvider.verifyPhoneNumber(
      phoneInfoOptions, recaptchaVerifier);
})
.then(function(verificationId) {
  // Ask user for the verification code.
  var cred = firebase.auth.PhoneAuthProvider.credential(verificationId, verificationCode);
  var multiFactorAssertion = firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
  // Complete enrollment.
  return user.multiFactor.enroll(multiFactorAssertion, mfaDisplayName);
});

Selamat! Anda berhasil mendaftarkan faktor otentikasi kedua untuk pengguna.

Membuat pengguna masuk dengan faktor kedua

Untuk memasukkan pengguna dengan verifikasi SMS dua faktor:

  1. Masuk pengguna dengan faktor pertama mereka, lalu tangkap kesalahan auth/multi-factor-auth-required . Kesalahan ini berisi resolver, petunjuk tentang faktor kedua yang terdaftar, dan sesi dasar yang membuktikan bahwa pengguna berhasil diautentikasi dengan faktor pertama.

    Misalnya, jika faktor pertama pengguna adalah email dan kata sandi:

    Web version 9

    import { getAuth, getMultiFactorResolver} from "firebase/auth";
    
    const auth = getAuth();
    signInWithEmailAndPassword(auth, email, password)
        .then(function (userCredential) {
            // User successfully signed in and is not enrolled with a second factor.
        })
        .catch(function (error) {
            if (error.code == 'auth/multi-factor-auth-required') {
                // The user is a multi-factor user. Second factor challenge is required.
                resolver = getMultiFactorResolver(auth, error);
                // ...
            } else if (error.code == 'auth/wrong-password') {
                // Handle other errors such as wrong password.
            }
    });
    

    Web version 8

    firebase.auth().signInWithEmailAndPassword(email, password)
      .then(function(userCredential) {
        // User successfully signed in and is not enrolled with a second factor.
      })
      .catch(function(error) {
        if (error.code == 'auth/multi-factor-auth-required') {
          // The user is a multi-factor user. Second factor challenge is required.
          resolver = error.resolver;
          // ...
        } else if (error.code == 'auth/wrong-password') {
          // Handle other errors such as wrong password.
        } ...
      });
    

    Jika faktor pertama pengguna adalah penyedia federasi, seperti OAuth, SAML, atau OIDC, tangkap kesalahan setelah memanggil signInWithPopup() atau signInWithRedirect() .

  2. Jika pengguna memiliki beberapa faktor sekunder yang terdaftar, tanyakan yang mana yang akan digunakan:

    Web version 9

    // Ask user which second factor to use.
    // You can get the masked phone number via resolver.hints[selectedIndex].phoneNumber
    // You can get the display name via resolver.hints[selectedIndex].displayName
    
    if (resolver.hints[selectedIndex].factorId === PhoneMultiFactorGenerator.FACTOR_ID) {
        // User selected a phone second factor.
        // ...
    } else {
        // Unsupported second factor.
        // Note that only phone second factors are currently supported.
    }
    

    Web version 8

    // Ask user which second factor to use.
    // You can get the masked phone number via resolver.hints[selectedIndex].phoneNumber
    // You can get the display name via resolver.hints[selectedIndex].displayName
    if (resolver.hints[selectedIndex].factorId === firebase.auth.PhoneMultiFactorGenerator.FACTOR_ID) {
      // User selected a phone second factor.
      // ...
    } else {
      // Unsupported second factor.
      // Note that only phone second factors are currently supported.
    }
    
  3. Inisialisasi pemverifikasi reCAPTCHA seperti yang diilustrasikan di bagian sebelumnya. Lewati langkah ini jika instance RecaptchaVerifier sudah dikonfigurasi:

    Web version 9

    import { RecaptchaVerifier } from "firebase/auth";
    
    recaptchaVerifier = new RecaptchaVerifier('recaptcha-container-id', undefined, auth);
    

    Web version 8

    var recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container-id');
    
  4. Inisialisasi objek PhoneInfoOptions dengan nomor telepon pengguna dan sesi multi-faktor. Nilai-nilai ini terkandung dalam objek resolver yang diteruskan ke kesalahan auth/multi-factor-auth-required :

    Web version 9

    const phoneInfoOptions = {
        multiFactorHint: resolver.hints[selectedIndex],
        session: resolver.session
    };
    

    Web version 8

    var phoneInfoOptions = {
      multiFactorHint: resolver.hints[selectedIndex],
      session: resolver.session
    };
    
  5. Kirim pesan verifikasi ke ponsel pengguna:

    Web version 9

    // Send SMS verification code.
    const phoneAuthProvider = new PhoneAuthProvider(auth);
    phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
        .then(function (verificationId) {
            // verificationId will be needed for sign-in completion.
        });
    

    Web version 8

    var phoneAuthProvider = new firebase.auth.PhoneAuthProvider();
    // Send SMS verification code.
    return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
      .then(function(verificationId) {
        // verificationId will be needed for sign-in completion.
      })
    
  6. Jika permintaan gagal, setel ulang reCAPTCHA, lalu ulangi langkah sebelumnya sehingga pengguna dapat mencoba lagi:

    Web version 9

    grecaptcha.reset(window.recaptchaWidgetId);
    
    // Or, if you haven't stored the widget ID:
    recaptchaVerifier.render()
      .then(function(widgetId) {
        grecaptcha.reset(widgetId);
      });
    

    Web version 8

    grecaptcha.reset(window.recaptchaWidgetId);
    
    // Or, if you haven't stored the widget ID:
    recaptchaVerifier.render()
      .then(function(widgetId) {
        grecaptcha.reset(widgetId);
      });
    
  7. Setelah kode SMS dikirim, minta pengguna untuk memverifikasi kode:

    Web version 9

    const cred = PhoneAuthProvider.credential(verificationId, verificationCode);
    

    Web version 8

    // Ask user for the verification code. Then:
    var cred = firebase.auth.PhoneAuthProvider.credential(verificationId, verificationCode);
    
  8. Inisialisasi objek MultiFactorAssertion dengan PhoneAuthCredential :

    Web version 9

    const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);
    

    Web version 8

    var multiFactorAssertion = firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
    
  9. Panggil resolver.resolveSignIn() untuk menyelesaikan autentikasi sekunder. Anda kemudian dapat mengakses hasil masuk asli, yang menyertakan kredensial autentikasi dan data khusus penyedia standar:

    Web version 9

    // Complete sign-in. This will also trigger the Auth state listeners.
    resolver.resolveSignIn(multiFactorAssertion)
        .then(function (userCredential) {
            // userCredential will also contain the user, additionalUserInfo, optional
            // credential (null for email/password) associated with the first factor sign-in.
    
            // For example, if the user signed in with Google as a first factor,
            // userCredential.additionalUserInfo will contain data related to Google 
            // provider that the user signed in with.
            // - user.credential contains the Google OAuth credential.
            // - user.credential.accessToken contains the Google OAuth access token.
            // - user.credential.idToken contains the Google OAuth ID token.
        });
    

    Web version 8

    // Complete sign-in. This will also trigger the Auth state listeners.
    resolver.resolveSignIn(multiFactorAssertion)
      .then(function(userCredential) {
        // userCredential will also contain the user, additionalUserInfo, optional
        // credential (null for email/password) associated with the first factor sign-in.
        // For example, if the user signed in with Google as a first factor,
        // userCredential.additionalUserInfo will contain data related to Google provider that
        // the user signed in with.
        // user.credential contains the Google OAuth credential.
        // user.credential.accessToken contains the Google OAuth access token.
        // user.credential.idToken contains the Google OAuth ID token.
      });
    

Kode di bawah ini menunjukkan contoh lengkap masuk ke pengguna multi-faktor:

Web version 9

import {
    getAuth,
    getMultiFactorResolver,
    PhoneAuthProvider,
    PhoneMultiFactorGenerator,
    RecaptchaVerifier,
    signInWithEmailAndPassword
} from "firebase/auth";

const recaptchaVerifier = new RecaptchaVerifier('recaptcha-container-id', undefined, auth);

const auth = getAuth();
signInWithEmailAndPassword(auth, email, password)
    .then(function (userCredential) {
        // 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 user which second factor to use.
            if (resolver.hints[selectedIndex].factorId ===
                PhoneMultiFactorGenerator.FACTOR_ID) {
                const phoneInfoOptions = {
                    multiFactorHint: resolver.hints[selectedIndex],
                    session: resolver.session
                };
                const phoneAuthProvider = new PhoneAuthProvider(auth);
                // Send SMS verification code
                return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
                    .then(function (verificationId) {
                        // Ask user for the SMS verification code. Then:
                        const cred = PhoneAuthProvider.credential(
                            verificationId, verificationCode);
                        const multiFactorAssertion =
                            PhoneMultiFactorGenerator.assertion(cred);
                        // Complete sign-in.
                        return resolver.resolveSignIn(multiFactorAssertion)
                    })
                    .then(function (userCredential) {
                        // User successfully signed in with the second factor phone number.
                    });
            } else {
                // Unsupported second factor.
            }
        } else if (error.code == 'auth/wrong-password') {
            // Handle other errors such as wrong password.
        }
    });

Web version 8

var resolver;
firebase.auth().signInWithEmailAndPassword(email, password)
  .then(function(userCredential) {
    // User is not enrolled with a second factor and is successfully signed in.
    // ...
  })
  .catch(function(error) {
    if (error.code == 'auth/multi-factor-auth-required') {
      resolver = error.resolver;
      // Ask user which second factor to use.
      if (resolver.hints[selectedIndex].factorId ===
          firebase.auth.PhoneMultiFactorGenerator.FACTOR_ID) {
        var phoneInfoOptions = {
          multiFactorHint: resolver.hints[selectedIndex],
          session: resolver.session
        };
        var phoneAuthProvider = new firebase.auth.PhoneAuthProvider();
        // Send SMS verification code
        return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
          .then(function(verificationId) {
            // Ask user for the SMS verification code.
            var cred = firebase.auth.PhoneAuthProvider.credential(
                verificationId, verificationCode);
            var multiFactorAssertion =
                firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
            // Complete sign-in.
            return resolver.resolveSignIn(multiFactorAssertion)
          })
          .then(function(userCredential) {
            // User successfully signed in with the second factor phone number.
          });
      } else {
        // Unsupported second factor.
      }
    } else if (error.code == 'auth/wrong-password') {
      // Handle other errors such as wrong password.
    } ...
  });

Selamat! Anda berhasil masuk sebagai pengguna menggunakan autentikasi multi-faktor.

Apa berikutnya