Melakukan Autentikasi Menggunakan Apple dengan JavaScript

Anda dapat mengizinkan pengguna melakukan autentikasi dengan Firebase menggunakan ID Apple mereka dengan Firebase SDK untuk menjalankan alur login OAuth 2.0 secara menyeluruh.

Sebelum memulai

Agar pengguna dapat login menggunakan Apple, pertama-tama konfigurasikan Login dengan Apple di situs developer Apple, lalu aktifkan Apple sebagai penyedia login untuk project Firebase Anda.

Bergabunglah dengan Program Developer Apple

Login dengan Apple hanya dapat dikonfigurasi oleh anggota Program Developer Apple.

Mengonfigurasi Login dengan Apple

Di situs Apple Developer, lakukan hal berikut:

  1. Kaitkan situs dengan aplikasi seperti yang dijelaskan di bagian pertama artikel Mengonfigurasi Login dengan Apple untuk web. Jika diminta, daftarkan URL berikut sebagai Return URL:

    https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler

    Anda bisa mendapatkan project ID Firebase di halaman setelan Firebase console.

    Setelah selesai, catat Service ID baru, yang akan diperlukan nanti.

  2. Membuat kunci pribadi Login dengan Apple. Anda akan memerlukan kunci pribadi dan ID kunci baru nanti.
  3. Jika Anda menggunakan salah satu fitur Firebase Authentication yang mengirimkan email kepada pengguna, termasuk login dengan link email, verifikasi alamat email, pembatalan perubahan akun, dan lainnya, konfigurasikan layanan relai email pribadi Apple lalu daftarkan noreply@YOUR_FIREBASE_PROJECT_ID.firebaseapp.com (atau domain template email yang disesuaikan) agar Apple dapat merelai email yang dikirim oleh Firebase Authentication ke alamat email Apple yang dianonimkan.

Mengaktifkan Apple sebagai penyedia login

  1. Tambahkan Firebase ke project Anda.
  2. Di Firebase console, buka bagian Auth. Pada tab Sign-in method, aktifkan penyedia Apple. Tentukan Service ID yang Anda buat di bagian sebelumnya. Selain itu, di bagian OAuth code flow configuration, masukkan Team ID Apple, kunci pribadi, dan ID kunci yang Anda buat di bagian sebelumnya.

Mematuhi persyaratan data anonim Apple

Login dengan Apple memungkinkan pengguna memilih opsi untuk membuat data miliknya anonim, termasuk alamat email, saat login. Pengguna yang memilih opsi ini memiliki alamat email dengan domain privaterelay.appleid.com. Jika menggunakan Login dengan Apple di aplikasi, Anda harus mematuhi kebijakan atau persyaratan developer yang berlaku dari Apple terkait ID Apple anonim ini.

Hal ini termasuk memperoleh persetujuan pengguna yang diperlukan sebelum Anda mengaitkan informasi identitas pribadi langsung apa pun dengan ID Apple anonim. Jika menggunakan Firebase Authentication, ini dapat meliputi tindakan-tindakan berikut:

  • Menautkan alamat email ke ID Apple anonim atau sebaliknya.
  • Menautkan nomor telepon ke ID Apple anonim atau sebaliknya.
  • Menautkan kredensial sosial non-anonim (Facebook, Google, dsb.) ke ID Apple anonim atau sebaliknya.

Daftar di atas tidak lengkap. Baca Apple Developer Program License Agreement di bagian Membership akun developer Anda untuk memastikan aplikasi Anda memenuhi persyaratan Apple.

Menangani alur login dengan Firebase SDK

Jika Anda mem-build aplikasi web, cara termudah untuk mengautentikasi pengguna dengan Firebase menggunakan akun Apple mereka adalah dengan menangani seluruh alur login dengan Firebase JavaScript SDK.

Untuk menangani alur login dengan Firebase JavaScript SDK, ikuti langkah-langkah berikut:

  1. Buat instance OAuthProvider menggunakan ID penyedia apple.com yang sesuai.

    Web

    import { OAuthProvider } from "firebase/auth";
    
    const provider = new OAuthProvider('apple.com');

    Web

    var provider = new firebase.auth.OAuthProvider('apple.com');
  2. Opsional: Tentukan cakupan OAuth 2.0 tambahan selain default yang ingin diminta dari penyedia autentikasi.

    Web

    provider.addScope('email');
    provider.addScope('name');

    Web

    provider.addScope('email');
    provider.addScope('name');

    Secara default, jika One account per email address diaktifkan, Firebase akan meminta cakupan email dan nama. Jika Anda mengubah setelan menjadi Multiple accounts per email address, Firebase tidak akan meminta cakupan apa pun dari Apple kecuali jika Anda menentukannya.

  3. Opsional: Jika ingin menampilkan layar login Apple dalam bahasa, selain bahasa Inggris, tetapkan locale parameter. Baca dokumentasi Login dengan Apple untuk lokalitas yang didukung.

    Web

    provider.setCustomParameters({
      // Localize the Apple authentication screen in French.
      locale: 'fr'
    });

    Web

    provider.setCustomParameters({
      // Localize the Apple authentication screen in French.
      locale: 'fr'
    });
  4. Lakukan autentikasi dengan Firebase menggunakan objek penyedia OAuth. Anda bisa meminta pengguna untuk login dengan Akun Apple mereka, baik dengan membuka jendela pop-up maupun dengan mengalihkan mereka ke halaman login. Untuk perangkat seluler, sebaiknya gunakan metode pengalihan.

    • Untuk login dengan jendela pop-up, panggil signInWithPopup():

      Web

      import { getAuth, signInWithPopup, OAuthProvider } from "firebase/auth";
      
      const auth = getAuth();
      signInWithPopup(auth, provider)
        .then((result) => {
          // The signed-in user info.
          const user = result.user;
      
          // Apple credential
          const credential = OAuthProvider.credentialFromResult(result);
          const accessToken = credential.accessToken;
          const idToken = credential.idToken;
      
          // IdP data available using getAdditionalUserInfo(result)
          // ...
        })
        .catch((error) => {
          // Handle Errors here.
          const errorCode = error.code;
          const errorMessage = error.message;
          // The email of the user's account used.
          const email = error.customData.email;
          // The credential that was used.
          const credential = OAuthProvider.credentialFromError(error);
      
          // ...
        });

      Web

      firebase
        .auth()
        .signInWithPopup(provider)
        .then((result) => {
          /** @type {firebase.auth.OAuthCredential} */
          var credential = result.credential;
      
          // The signed-in user info.
          var user = result.user;
      
          // You can also get the Apple OAuth Access and ID Tokens.
          var accessToken = credential.accessToken;
          var idToken = credential.idToken;
      
          // IdP data available using getAdditionalUserInfo(result)
        // ...
        })
        .catch((error) => {
          // Handle Errors here.
          var errorCode = error.code;
          var errorMessage = error.message;
          // The email of the user's account used.
          var email = error.email;
          // The firebase.auth.AuthCredential type that was used.
          var credential = error.credential;
      
          // ...
        });
    • Untuk login dengan beralih ke halaman login, panggil signInWithRedirect():

    Web

    import { getAuth, signInWithRedirect } from "firebase/auth";
    
    const auth = getAuth();
    signInWithRedirect(auth, provider);

    Web

    firebase.auth().signInWithRedirect(provider);

    Setelah pengguna menyelesaikan proses login dan kembali ke halaman, Anda dapat memperoleh hasil login dengan memanggil getRedirectResult():

    Web

    import { getAuth, getRedirectResult, OAuthProvider } from "firebase/auth";
    
    // Result from Redirect auth flow.
    const auth = getAuth();
    getRedirectResult(auth)
      .then((result) => {
        const credential = OAuthProvider.credentialFromResult(result);
        if (credential) {
          // You can also get the Apple OAuth Access and ID Tokens.
          const accessToken = credential.accessToken;
          const idToken = credential.idToken;
        }
        // The signed-in user info.
        const user = result.user;
      })
      .catch((error) => {
        // Handle Errors here.
        const errorCode = error.code;
        const errorMessage = error.message;
        // The email of the user's account used.
        const email = error.customData.email;
        // The credential that was used.
        const credential = OAuthProvider.credentialFromError(error);
    
        // ...
      });

    Web

    // Result from Redirect auth flow.
    firebase
      .auth()
      .getRedirectResult()
      .then((result) => {
        if (result.credential) {
          /** @type {firebase.auth.OAuthCredential} */
          var credential = result.credential;
    
          // You can get the Apple OAuth Access and ID Tokens.
          var accessToken = credential.accessToken;
          var idToken = credential.idToken;
    
          // IdP data available in result.additionalUserInfo.profile.
          // ...
        }
        // The signed-in user info.
        var user = result.user;
      })
      .catch((error) => {
        // Handle Errors here.
        var errorCode = error.code;
        var errorMessage = error.message;
        // The email of the user's account used.
        var email = error.email;
        // The firebase.auth.AuthCredential type that was used.
        var credential = error.credential;
    
        // ...
      });

    Di sini juga Anda dapat menemukan dan menangani error. Untuk mengetahui daftar kode error, baca Referensi API.

    Tidak seperti penyedia lain yang didukung oleh Firebase Auth, Apple tidak memberikan URL foto.

    Selain itu, jika pengguna memilih untuk tidak membagikan email kepada aplikasi, Apple akan menyediakan alamat email unik untuk pengguna tersebut (dengan format xyz@privaterelay.appleid.com), yang dibagikan kepada aplikasi Anda. Jika Anda mengonfigurasi layanan relai email pribadi, Apple akan meneruskan email yang dikirim ke alamat anonim tersebut ke alamat email asli pengguna.

    Apple hanya membagikan informasi pengguna, misalnya nama tampilan, kepada aplikasi saat pengguna login untuk pertama kalinya. Biasanya, Firebase menyimpan nama tampilan saat pengguna login dengan Apple untuk pertama kalinya, yang dapat Anda peroleh dengan firebase.auth().currentUser.displayName. Namun, jika sebelumnya Anda menggunakan Apple untuk memproses login pengguna ke aplikasi tanpa menggunakan Firebase, Apple tidak akan memberikan nama tampilan pengguna kepada Firebase.

Autentikasi ulang dan penautan akun

Pola yang sama dapat digunakan dengan reauthenticateWithPopup() dan reauthenticateWithRedirect(), yang dapat Anda gunakan untuk mengambil kredensial baru untuk operasi sensitif yang memerlukan login terbaru:

Web

import { getAuth, reauthenticateWithPopup, OAuthProvider } from "firebase/auth";

// Result from Redirect auth flow.
const auth = getAuth();
const provider = new OAuthProvider('apple.com');

reauthenticateWithPopup(auth.currentUser, provider)
  .then((result) => {
    // User is re-authenticated with fresh tokens minted and can perform
    // sensitive operations like account deletion, or updating their email
    // address or password.

    // The signed-in user info.
    const user = result.user;

    // You can also get the Apple OAuth Access and ID Tokens.
    const credential = OAuthProvider.credentialFromResult(result);
    const accessToken = credential.accessToken;
    const idToken = credential.idToken;

    // ...
  })
  .catch((error) => {
    // Handle Errors here.
    const errorCode = error.code;
    const errorMessage = error.message;
    // The email of the user's account used.
    const email = error.customData.email;
    // The credential that was used.
    const credential = OAuthProvider.credentialFromError(error);

    // ...
  });

Web

const provider = new firebase.auth.OAuthProvider('apple.com');

firebase
  .auth()
  .currentUser
  .reauthenticateWithPopup(provider)
  .then((result) => {
    // User is re-authenticated with fresh tokens minted and can perform
    // sensitive operations like account deletion, or updating their email
    // address or password.
    /** @type {firebase.auth.OAuthCredential} */
    var credential = result.credential;

    // The signed-in user info.
    var user = result.user;
     // You can also get the Apple OAuth Access and ID Tokens.
    var accessToken = credential.accessToken;
    var idToken = credential.idToken;

    // IdP data available in result.additionalUserInfo.profile.
      // ...
  })
  .catch((error) => {
    // Handle Errors here.
    var errorCode = error.code;
    var errorMessage = error.message;
    // The email of the user's account used.
    var email = error.email;
    // The firebase.auth.AuthCredential type that was used.
    var credential = error.credential;

    // ...
  });

Selain itu, Anda dapat menggunakan linkWithPopup() dan linkWithRedirect() untuk menautkan berbagai penyedia identitas ke akun yang ada.

Perlu diperhatikan bahwa Apple mewajibkan Anda untuk mendapatkan persetujuan eksplisit dari pengguna sebelum Anda menautkan akun Apple mereka ke data lain.

Misalnya, untuk menautkan akun Facebook ke akun Firebase saat ini, gunakan token akses yang Anda peroleh saat pengguna login ke Facebook:

Web

import { getAuth, linkWithPopup, FacebookAuthProvider } from "firebase/auth";

const auth = getAuth();
const provider = new FacebookAuthProvider();
provider.addScope('user_birthday');

// Assuming the current user is an Apple user linking a Facebook provider.
linkWithPopup(auth.currentUser, provider)
    .then((result) => {
      // Facebook credential is linked to the current Apple user.
      // ...

      // The user can now sign in to the same account
      // with either Apple or Facebook.
    })
    .catch((error) => {
      // Handle error.
    });

Web

const provider = new firebase.auth.FacebookAuthProvider();
provider.addScope('user_birthday');

// Assuming the current user is an Apple user linking a Facebook provider.
firebase.auth().currentUser.linkWithPopup(provider)
    .then((result) => {
      // Facebook credential is linked to the current Apple user.
      // Facebook additional data available in result.additionalUserInfo.profile,

      // Additional Facebook OAuth access token can also be retrieved.
      // result.credential.accessToken

      // The user can now sign in to the same account
      // with either Apple or Facebook.
    })
    .catch((error) => {
      // Handle error.
    });

Melakukan autentikasi dengan Firebase di ekstensi Chrome

Jika Anda membangun aplikasi ekstensi Chrome, baca artikel Panduan Dokumen di Luar Layar.

Perlu diperhatikan bahwa Anda tetap harus memverifikasi domain kustom dengan Apple sama seperti domain firebaseapp.com default:

http://auth.custom.example.com/.well-known/apple-developer-domain-association.txt

Pencabutan token

Apple mewajibkan aplikasi yang mendukung pembuatan akun harus mengizinkan pengguna untuk memulai penghapusan akun di dalam aplikasi, seperti yang dijelaskan dalam Panduan Ulasan App Store

Untuk memenuhi persyaratan ini, terapkan langkah-langkah berikut:

  1. Pastikan Anda mengisi bagian Service ID dan Konfigurasi alur kode OAuth dari konfigurasi penyedia Login dengan Apple, seperti yang diuraikan dalam bagian Konfigurasikan Login dengan Apple.

  2. Karena Firebase tidak menyimpan token pengguna saat pengguna dibuat dengan Login dengan Apple, Anda harus meminta pengguna untuk login lagi sebelum mencabut token dan menghapus akun.

    Kemudian, dapatkan token akses OAuth Apple dari OAuthCredential, dan gunakan untuk memanggil revokeAccessToken(auth, token) guna mencabut token akses OAuth Apple.

    const provider = new OAuthProvider('apple.com');
    provider.addScope('email');
    provider.addScope('name');
    
    const auth = getAuth();
    signInWithPopup(auth, provider).then(result => {
      // Get the Apple OAuth access token.
      const credential = OAuthProvider.credentialFromResult(result);
      const accessToken = credential.accessToken;
    
      // Revoke the Apple OAuth access token.
      revokeAccessToken(auth, accessToken)
        .then(() => {
          // Token revoked.
    
          // Delete the user account.
          // ...
        })
        .catch(error => {
          // An error happened.
          // ...
        });
    });
    
  3. Terakhir, hapus akun pengguna (dan semua data terkait)

Lanjutan: Melakukan autentikasi dengan Firebase di Node.js

Untuk melakukan autentikasi dengan Firebase pada aplikasi Node.js:

  1. Proses login pengguna dengan Akun Apple dan dapatkan token ID Apple pengguna tersebut. Anda dapat melakukannya dengan beberapa cara. Misalnya, jika aplikasi Node.js Anda memiliki front end browser:

    1. Pada backend Anda, buat string acak (“nonce”) dan proses hash SHA256-nya. Nonce adalah nilai sekali pakai untuk memvalidasi satu perjalanan dua arah antara backend Anda dan server autentikasi Apple.

      Web

      const crypto = require("crypto");
      const string_decoder = require("string_decoder");
      
      // Generate a new random string for each sign-in
      const generateNonce = (length) => {
        const decoder = new string_decoder.StringDecoder("ascii");
        const buf = Buffer.alloc(length);
        let nonce = "";
        while (nonce.length < length) {
          crypto.randomFillSync(buf);
          nonce = decoder.write(buf);
        }
        return nonce.slice(0, length);
      };
      
      const unhashedNonce = generateNonce(10);
      
      // SHA256-hashed nonce in hex
      const hashedNonceHex = crypto.createHash('sha256')
        .update(unhashedNonce).digest().toString('hex');

      Web

      const crypto = require("crypto");
      const string_decoder = require("string_decoder");
      
      // Generate a new random string for each sign-in
      const generateNonce = function(length) {
        const decoder = new string_decoder.StringDecoder("ascii");
        const buf = Buffer.alloc(length);
        var nonce = "";
        while (nonce.length < length) {
          crypto.randomFillSync(buf);
          nonce = decoder.write(buf);
        }
        return nonce.slice(0, length);
      };
      
      const unhashedNonce = generateNonce(10);
      
      // SHA256-hashed nonce in hex
      const hashedNonceHex = crypto.createHash('sha256')
        .update(unhashedNonce).digest().toString('hex');
    2. Di halaman login, tentukan nonce hasil hash dalam konfigurasi Login dengan Apple:

      <script src="https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js"></script>
      <div id="appleid-signin" data-color="black" data-border="true" data-type="sign in"></div>
      <script>
          AppleID.auth.init({
              clientId: YOUR_APPLE_CLIENT_ID,
              scope: 'name email',
              redirectURI: URL_TO_YOUR_REDIRECT_HANDLER,  // See the next step.
              state: '[STATE]',  // Optional value that Apple will send back to you
                                 // so you can return users to the same context after
                                 // they sign in.
              nonce: HASHED_NONCE  // The hashed nonce you generated in the previous step.
          });
      </script>
      
    3. Dapatkan token ID Apple dari sisi server respons autentikasi POSTed:

      app.post('/redirect', (req, res) => {
        const savedState = req.cookies.__session;
        const code = req.body.code;
        const state = req.body.state;
        const appleIdToken = req.body.id_token;
        if (savedState !== state || !code) {
          res.status(403).send('403: Permission denied');
        } else {
          // Sign in with Firebase using appleIdToken. (See next step).
        }
      });
      

    Baca juga artikel Mengonfigurasi Halaman Web untuk Login dengan Apple.

  2. Setelah Anda mendapatkan token ID Apple pengguna, gunakan token tersebut untuk mem-build objek Kredensial, kemudian terapkan login pengguna dengan kredensial tersebut:

    Web

    import { getAuth, signInWithCredential, OAuthProvider } from "firebase/auth";
    
    const auth = getAuth();
    
    // Build Firebase credential with the Apple ID token.
    const provider = new OAuthProvider('apple.com');
    const authCredential = provider.credential({
      idToken: appleIdToken,
      rawNonce: unhashedNonce,
    });
    
    // Sign in with credential form the Apple user.
    signInWithCredential(auth, authCredential)
      .then((result) => {
        // User signed in.
      })
      .catch((error) => {
        // An error occurred. If error.code == 'auth/missing-or-invalid-nonce',
        // make sure you're sending the SHA256-hashed nonce as a hex string
        // with your request to Apple.
        console.log(error);
      });

    Web

    // Build Firebase credential with the Apple ID token.
    const provider = new firebase.auth.OAuthProvider('apple.com');
    const authCredential = provider.credential({
      idToken: appleIdToken,
      rawNonce: unhashedNonce,
    });
    
    // Sign in with credential form the Apple user.
    firebase.auth().signInWithCredential(authCredential)
      .then((result) => {
        // User signed in.
      })
      .catch((error) => {
        // An error occurred. If error.code == 'auth/missing-or-invalid-nonce',
        // make sure you're sending the SHA256-hashed nonce as a hex string
        // with your request to Apple.
        console.log(error);
      });

Langkah berikutnya

Setelah pengguna login untuk pertama kalinya, akun pengguna baru akan dibuat dan ditautkan ke kredensial, yaitu nama pengguna dan sandi, nomor telepon, atau informasi penyedia autentikasi, yang digunakan pengguna tersebut untuk login. Akun baru ini disimpan sebagai bagian dari project Firebase Anda, dan dapat digunakan untuk mengidentifikasi pengguna di setiap aplikasi dalam project, terlepas dari cara pengguna login.

  • Di aplikasi Anda, cara yang direkomendasikan untuk mengetahui status autentikasi pengguna adalah dengan menetapkan observer pada objek Auth. Selanjutnya, Anda bisa mendapatkan informasi profil dasar pengguna dari objek User. Baca bagian Mengelola Pengguna.

  • Di Aturan Keamanan Firebase Realtime Database dan Cloud Storage, Anda bisa mendapatkan ID pengguna unik milik pengguna yang login dari variabel auth, dan menggunakannya untuk mengontrol data yang dapat diakses oleh pengguna.

Anda dapat mengizinkan pengguna untuk login ke aplikasi menggunakan beberapa penyedia autentikasi dengan menautkan kredensial penyedia autentikasi ke akun pengguna yang ada.

Untuk memproses logout pengguna, panggil signOut:

Web

import { getAuth, signOut } from "firebase/auth";

const auth = getAuth();
signOut(auth).then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});

Web

firebase.auth().signOut().then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});