Menambahkan autentikasi multi-faktor ke aplikasi iOS

Dokumen ini menunjukkan cara menambahkan autentikasi multi-faktor SMS ke aplikasi iOS.

Autentikasi multi-faktor meningkatkan keamanan aplikasi Anda. Meskipun penyerang sering membobol sandi dan akun media sosial, menyadap pesan teks lebih sulit dilakukan.

Sebelum memulai

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

  2. Pastikan aplikasi Anda memverifikasi email pengguna. MFA memerlukan verifikasi email. Tindakan ini mencegah pelaku kejahatan mendaftar ke layanan dengan email yang bukan miliknya, lalu mengunci pemilik sebenarnya dari akunnya dengan menambahkan faktor kedua.

Mengaktifkan autentikasi multi-faktor

  1. Buka halaman Identity Platform MFA di konsol Google Cloud.
    Buka halaman MFA

  2. Di kotak berjudul SMS-Based Multi-Factor Authentication, klik Enable.

  3. Masukkan nomor telepon yang akan digunakan untuk menguji aplikasi. Meskipun opsional, mendaftarkan nomor telepon pengujian sangat direkomendasikan untuk menghindari throttling selama tahap pengembangan.

  4. Jika Anda belum memberikan otorisasi pada domain aplikasi, tambahkan domain tersebut ke daftar yang diizinkan dengan mengklik Tambahkan domain di sebelah kanan.

  5. Klik Simpan.

Memverifikasi aplikasi

Identity Platform perlu memverifikasi bahwa permintaan SMS berasal dari aplikasi Anda. Anda dapat melakukannya dengan dua cara:

  • Notifikasi APN senyap: Saat Anda memproses login pengguna untuk pertama kalinya, Identity Platform dapat mengirim notifikasi push senyap ke perangkat pengguna. Autentikasi dapat dilanjutkan jika aplikasi menerima notifikasi. Perlu diperhatikan bahwa mulai iOS 8.0, Anda tidak perlu meminta pengguna mengizinkan notifikasi push untuk menggunakan metode ini.

  • Verifikasi reCAPTCHA: Jika Anda tidak dapat mengirim notifikasi senyap (misalnya, karena pengguna telah menonaktifkan refresh di latar belakang, atau Anda menguji aplikasi di simulator iOS), Anda dapat menggunakan reCAPTCHA. Dalam banyak kasus, reCAPTCHA akan otomatis selesai dengan sendirinya tanpa interaksi pengguna.

Menggunakan notifikasi senyap

Untuk mengaktifkan notifikasi APNs agar dapat digunakan dengan Identity Platform:

  1. Di Xcode, aktifkan notifikasi push untuk project Anda.

  2. Upload kunci autentikasi APN Anda menggunakan Firebase Console (perubahan Anda akan otomatis diterapkan ke Google Cloud Identity Platform). Jika belum memiliki kunci autentikasi APNs, lihat bagian Mengonfigurasi APNs dengan FCM untuk mempelajari cara mendapatkannya.

    1. Buka Firebase Console.

    2. Buka Project Settings.

    3. Pilih tab Cloud Messaging.

    4. Di bagian APNs authentication key, pada bagian iOS app configuration, klik Upload.

    5. Pilih kunci Anda.

    6. Tambahkan ID kunci untuk kunci tersebut. Anda dapat menemukan ID kunci di bagian Certificates, Identifiers & Profiles di Apple Developer Member Center.

    7. Klik Upload.

Jika sudah memiliki sertifikat APNs, Anda dapat menguploadnya.

Menggunakan verifikasi reCAPTCHA

Untuk mengaktifkan SDK klien agar dapat menggunakan reCAPTCHA:

  1. Buka konfigurasi project di Xcode.

  2. Klik dua kali pada nama project dalam tampilan hierarki di sebelah kiri.

  3. Pilih aplikasi dari bagian Targets.

  4. Pilih tab Info.

  5. Perluas bagian URL Types.

  6. Klik tombol +.

  7. Masukkan client ID terbalik di kolom URL Schemes. Anda dapat menemukan nilai ini tercantum di file konfigurasi GoogleService-Info.plist sebagai REVERSED_CLIENT_ID.

Jika sudah selesai, konfigurasi Anda akan terlihat seperti contoh berikut:

Skema kustom

Secara opsional, Anda dapat menyesuaikan cara aplikasi menyajikan SFSafariViewController atau UIWebView saat menampilkan reCAPTCHA. Untuk melakukannya, buat class kustom yang sesuai dengan protokol FIRAuthUIDelegate, dan teruskan ke verifyPhoneNumber:UIDelegate:completion:.

Memilih pola pendaftaran

Anda dapat memilih apakah aplikasi memerlukan autentikasi multi-faktor atau tidak, serta cara dan waktu untuk mendaftarkan pengguna. Beberapa pola yang umum antara lain:

  • Mendaftarkan faktor kedua pengguna sebagai bagian dari pendaftaran. Gunakan metode ini jika aplikasi Anda memerlukan autentikasi multi-faktor untuk semua pengguna. Perhatikan bahwa akun harus memiliki alamat email terverifikasi untuk mendaftarkan faktor kedua, sehingga alur pendaftaran harus mengakomodasi hal ini.

  • Menawarkan opsi yang dapat dilewati untuk mendaftarkan faktor kedua selama pendaftaran. Aplikasi yang ingin mendukung, tetapi tidak memerlukan, autentikasi multi-faktor dapat memilih pendekatan ini.

  • Memungkinkan untuk menambahkan faktor kedua dari halaman pengelolaan akun atau profil pengguna, bukan dari layar pendaftaran. Hal ini akan meminimalkan hambatan selama proses pendaftaran, sekaligus tetap menyediakan autentikasi multi-faktor untuk pengguna yang rentan terhadap ancaman keamanan.

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

Mendaftarkan faktor kedua

Untuk mendaftarkan faktor sekunder baru untuk pengguna:

  1. Autentikasi ulang pengguna.

  2. Minta pengguna untuk memasukkan nomor teleponnya.

  3. Dapatkan sesi multi-faktor untuk pengguna:

    Swift

    authResult.user.multiFactor.getSessionWithCompletion() { (session, error) in
      // ...
    }
    

    Objective-C

    [authResult.user.multiFactor
      getSessionWithCompletion:^(FIRMultiFactorSession * _Nullable session,
                                NSError * _Nullable error) {
        // ...
    }];
    
  4. Kirim pesan verifikasi ke ponsel pengguna. Pastikan nomor telepon diformat dengan + utama dan tanpa tanda baca atau spasi kosong lainnya (misalnya: +15105551234)

    Swift

    // Send SMS verification code.
    PhoneAuthProvider.provider().verifyPhoneNumber(
      phoneNumber,
      uiDelegate: nil,
      multiFactorSession: session) { (verificationId, error) in
        // verificationId will be needed for enrollment completion.
    }
    

    Objective-C

    // Send SMS verification code.
    [FIRPhoneAuthProvider.provider verifyPhoneNumber:phoneNumber
                                          UIDelegate:nil
                                  multiFactorSession:session
                                          completion:^(NSString * _Nullable verificationID,
                                                        NSError * _Nullable error) {
        // verificationId will be needed for enrollment completion.
    }];
    

    Meskipun tidak diwajibkan, sebaiknya beri tahu pengguna terlebih dahulu bahwa mereka akan menerima pesan SMS, dan dikenakan tarif standar.

    Metode verifyPhoneNumber() memulai proses verifikasi aplikasi di latar belakang menggunakan notifikasi push senyap. Jika notifikasi push senyap tidak tersedia, tantangan reCAPTCHA akan dikeluarkan.

  5. Setelah kode SMS dikirim, minta pengguna untuk memverifikasi kode tersebut. Kemudian, gunakan respons untuk membuat PhoneAuthCredential:

    Swift

    // Ask user for the verification code. Then:
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId,
      verificationCode: verificationCode)
    

    Objective-C

    // Ask user for the SMS verification code. Then:
    FIRPhoneAuthCredential *credential = [FIRPhoneAuthProvider.provider
                                           credentialWithVerificationID:verificationID
                                           verificationCode:kPhoneSecondFactorVerificationCode];
    
  6. Lakukan inisialisasi objek pernyataan:

    Swift

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    

    Objective-C

    FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
    
  7. Selesaikan pendaftaran. Secara opsional, Anda dapat menentukan nama tampilan untuk faktor kedua. Hal ini berguna bagi pengguna dengan beberapa faktor kedua, karena nomor telepon disamarkan selama alur autentikasi (misalnya, +1******1234).

    Swift

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    user.multiFactor.enroll(with: assertion, displayName: displayName) { (error) in
      // ...
    }
    

    Objective-C

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    [authResult.user.multiFactor enrollWithAssertion:assertion
                                         displayName:nil
                                          completion:^(NSError * _Nullable error) {
        // ...
    }];
    

Kode di bawah menunjukkan contoh lengkap proses pendaftaran faktor kedua:

Swift

let user = Auth.auth().currentUser
user?.multiFactor.getSessionWithCompletion({ (session, error) in
  // Send SMS verification code.
  PhoneAuthProvider.provider().verifyPhoneNumber(
    phoneNumber,
    uiDelegate: nil,
    multiFactorSession: session
  ) { (verificationId, error) in
    // verificationId will be needed for enrollment completion.
    // Ask user for the verification code.
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId!,
      verificationCode: phoneSecondFactorVerificationCode)
    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    user?.multiFactor.enroll(with: assertion, displayName: displayName) { (error) in
      // ...
    }
  }
})

Objective-C

FIRUser *user = FIRAuth.auth.currentUser;
[user.multiFactor getSessionWithCompletion:^(FIRMultiFactorSession * _Nullable session,
                                              NSError * _Nullable error) {
    // Send SMS verification code.
    [FIRPhoneAuthProvider.provider
      verifyPhoneNumber:phoneNumber
      UIDelegate:nil
      multiFactorSession:session
      completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
        // verificationId will be needed for enrollment completion.

        // Ask user for the verification code.
        // ...

        // Then:
        FIRPhoneAuthCredential *credential =
            [FIRPhoneAuthProvider.provider credentialWithVerificationID:verificationID
                                                        verificationCode:kPhoneSecondFactorVerificationCode];
        FIRMultiFactorAssertion *assertion =
            [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];

        // Complete enrollment. This will update the underlying tokens
        // and trigger ID token change listener.
        [user.multiFactor enrollWithAssertion:assertion
                                  displayName:displayName
                                    completion:^(NSError * _Nullable error) {
            // ...
        }];
    }];
}];

Selamat! Anda berhasil mendaftarkan faktor autentikasi kedua untuk pengguna.

Memproses login pengguna dengan faktor kedua

Untuk memproses login pengguna dengan verifikasi SMS dua faktor:

  1. Proses login pengguna dengan faktor pertamanya, lalu tangkap error yang menunjukkan bahwa autentikasi multi faktor diperlukan. Error ini berisi resolver, petunjuk tentang faktor kedua yang didaftarkan, dan sesi yang mendasarinya yang membuktikan bahwa pengguna berhasil diautentikasi dengan faktor pertama.

    Misalnya, jika faktor pertama pengguna adalah email dan sandi:

    Swift

    Auth.auth().signIn(
      withEmail: email,
      password: password
    ) { (result, error) in
      let authError = error as NSError
      if authError?.code == AuthErrorCode.secondFactorRequired.rawValue {
        // The user is a multi-factor user. Second factor challenge is required.
        let resolver =
          authError!.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver
        // ...
      } else {
        // Handle other errors such as wrong password.
      }
    }
    

    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.
        }
    }];
    

    Jika faktor pertama pengguna adalah penyedia gabungan, seperti OAuth, tangkap error setelah memanggil getCredentialWith().

  2. Jika pengguna telah mendaftarkan beberapa faktor sekunder, tanyakan kepada mereka faktor mana yang ingin digunakan. Anda bisa mendapatkan nomor telepon yang disamarkan dengan resolver.hints[selectedIndex].phoneNumber dan nama tampilan dengan resolver.hints[selectedIndex].displayName.

    Swift

    // Ask user which second factor to use. Then:
    if resolver.hints[selectedIndex].factorID == PhoneMultiFactorID {
      // User selected a phone second factor.
      // ...
    } else if resolver.hints[selectedIndex].factorID == TotpMultiFactorID {
      // User selected a TOTP second factor.
      // ...
    } else {
      // Unsupported second factor.
    }
    

    Objective-C

    FIRMultiFactorResolver *resolver =
        (FIRMultiFactorResolver *) error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];
    
    // Ask user which second factor to use. Then:
    FIRPhoneMultiFactorInfo *hint = (FIRPhoneMultiFactorInfo *) resolver.hints[selectedIndex];
    if (hint.factorID == FIRPhoneMultiFactorID) {
      // User selected a phone second factor.
      // ...
    } else if (hint.factorID == FIRTOTPMultiFactorID) {
      // User selected a TOTP second factor.
      // ...
    } else {
      // Unsupported second factor.
    }
    
  3. Kirim pesan verifikasi ke ponsel pengguna:

    Swift

    // Send SMS verification code.
    let hint = resolver.hints[selectedIndex] as! PhoneMultiFactorInfo
    PhoneAuthProvider.provider().verifyPhoneNumber(
      with: hint,
      uiDelegate: nil,
      multiFactorSession: resolver.session
    ) { (verificationId, error) in
      // verificationId will be needed for sign-in completion.
    }
    

    Objective-C

    // Send SMS verification code
    [FIRPhoneAuthProvider.provider
      verifyPhoneNumberWithMultiFactorInfo:hint
      UIDelegate:nil
      multiFactorSession:resolver.session
      completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
        if (error != nil) {
            // Failed to verify phone number.
        }
    }];
    
  4. Setelah kode SMS dikirim, minta pengguna untuk memverifikasi kode dan menggunakannya untuk membuat PhoneAuthCredential:

    Swift

    // Ask user for the verification code. Then:
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId!,
      verificationCode: verificationCodeFromUser)
    

    Objective-C

    // Ask user for the SMS verification code. Then:
    FIRPhoneAuthCredential *credential =
        [FIRPhoneAuthProvider.provider
          credentialWithVerificationID:verificationID
                      verificationCode:verificationCodeFromUser];
    
  5. Lakukan inisialisasi objek pernyataan dengan kredensial:

    Swift

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    

    Objective-C

    FIRMultiFactorAssertion *assertion =
        [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
    
  6. Selesaikan proses login. Selanjutnya, Anda dapat mengakses hasil login asli, yang mencakup kredensial autentikasi dan data khusus penyedia standar:

    Swift

    // Complete sign-in. This will also trigger the Auth state listeners.
    resolver.resolveSignIn(with: assertion) { (authResult, error) in
      // authResult 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,
      // authResult.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.
    }
    

    Objective-C

    // Complete sign-in.
    [resolver resolveSignInWithAssertion:assertion
                              completion:^(FIRAuthDataResult * _Nullable authResult,
                                            NSError * _Nullable error) {
        if (error != nil) {
            // User successfully signed in with the second factor phone number.
        }
    }];
    

Kode di bawah menunjukkan contoh lengkap proses login pengguna multi-faktor:

Swift

Auth.auth().signIn(
  withEmail: email,
  password: password
) { (result, error) in
  let authError = error as NSError?
  if authError?.code == AuthErrorCode.secondFactorRequired.rawValue {
    let resolver =
      authError!.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver

    // Ask user which second factor to use.
    // ...

    // Then:
    let hint = resolver.hints[selectedIndex] as! PhoneMultiFactorInfo

    // Send SMS verification code
    PhoneAuthProvider.provider().verifyPhoneNumber(
      with: hint,
      uiDelegate: nil,
      multiFactorSession: resolver.session
    ) { (verificationId, error) in
      if error != nil {
        // Failed to verify phone number.
      }
      // Ask user for the SMS verification code.
      // ...

      // Then:
      let credential = PhoneAuthProvider.provider().credential(
        withVerificationID: verificationId!,
        verificationCode: verificationCodeFromUser)
      let assertion = PhoneMultiFactorGenerator.assertion(with: credential)

      // Complete sign-in.
      resolver.resolveSignIn(with: assertion) { (authResult, error) in
        if error != nil {
          // User successfully signed in with the second factor phone number.
        }
      }
    }
  }
}

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 {
        FIRMultiFactorResolver *resolver =
            (FIRMultiFactorResolver *) error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];

        // Ask user which second factor to use.
        // ...

        // Then:
        FIRPhoneMultiFactorInfo *hint = (FIRPhoneMultiFactorInfo *) resolver.hints[selectedIndex];

        // Send SMS verification code
        [FIRPhoneAuthProvider.provider
          verifyPhoneNumberWithMultiFactorInfo:hint
                                    UIDelegate:nil
                            multiFactorSession:resolver.session
                                    completion:^(NSString * _Nullable verificationID,
                                                NSError * _Nullable error) {
            if (error != nil) {
                // Failed to verify phone number.
            }

            // Ask user for the SMS verification code.
            // ...

            // Then:
            FIRPhoneAuthCredential *credential =
                [FIRPhoneAuthProvider.provider
                  credentialWithVerificationID:verificationID
                              verificationCode:kPhoneSecondFactorVerificationCode];
            FIRMultiFactorAssertion *assertion =
                [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];

            // Complete sign-in.
            [resolver resolveSignInWithAssertion:assertion
                                      completion:^(FIRAuthDataResult * _Nullable authResult,
                                                    NSError * _Nullable error) {
                if (error != nil) {
                    // User successfully signed in with the second factor phone number.
                }
            }];
        }];
    }
}];

Selamat! Anda berhasil memproses login pengguna menggunakan autentikasi multi-faktor.

Langkah berikutnya