使用 Apple 和 JavaScript 進行身份驗證

您可以使用 Firebase SDK 執行端對端 OAuth 2.0 登入流程,讓使用者使用其 Apple ID 透過 Firebase 進行身份驗證。

在你開始之前

若要使用 Apple 登入用戶,請先在 Apple 開發者網站上設定 Sign In with Apple,然後啟用 Apple 作為 Firebase 專案的登入提供者。

加入蘋果開發者計劃

「使用 Apple 登入」只能由Apple 開發者計畫的成員進行設定。

配置使用 Apple 登入

Apple 開發者網站上,執行以下操作:

  1. 依照設定使用 Apple 網頁版登入的第一部分所述將您的網站與應用程式相關聯。出現提示時,將以下 URL 註冊為返回 URL:

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

    您可以在Firebase 控制台設定頁面上取得您的 Firebase 專案 ID。

    完成後,記下新的服務 ID,下一部分將需要它。

  2. 建立使用 Apple 私鑰登入。在下一部分中,您將需要新的私鑰和金鑰 ID。
  3. 如果您使用 Firebase 驗證的任何向使用者發送電子郵件的功能(包括電子郵件連結登入、電子郵件地址驗證、帳戶變更撤銷等),請設定 Apple 私人電子郵件中繼服務並註冊noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com (或您的自訂電子郵件範本網域),以便 Apple 可以將 Firebase 驗證傳送的電子郵件轉送至匿名 Apple 電子郵件地址。

啟用 Apple 作為登入供應商

  1. 將 Firebase 新增到您的專案中
  2. Firebase 控制台中,開啟「驗證」部分。在「登入方法」標籤上,啟用Apple提供者。指定您在上一部分中建立的服務 ID。另外,在OAuth 程式碼流配置部分中,指定您的 Apple 團隊 ID 以及您在上一部分中建立的私鑰和金鑰 ID。

遵守 Apple 匿名資料要求

「使用 Apple 登入」為使用者提供了在登入時對其資料(包括電子郵件地址)進行匿名化的選項。選擇此選項的使用者的電子郵件地址的網域為privaterelay.appleid.com 。當您在應用程式中使用「透過 Apple 登入」時,您必須遵守 Apple 關於這些匿名 Apple ID 的任何適用的開發者政策或條款。

這包括在將任何直接識別個人資訊與匿名 Apple ID 關聯之前獲得任何所需的用戶同意。使用 Firebase 身份驗證時,這可能包括以下操作:

  • 將電子郵件地址連結到匿名 Apple ID,反之亦然。
  • 將電話號碼連結到匿名 Apple ID,反之亦然
  • 將非匿名社交憑證(Facebook、Google 等)連結到匿名 Apple ID,反之亦然。

上述列表並非詳盡無遺。請參閱開發者帳戶的「會員資格」部分中的 Apple 開發者計畫許可協議,以確保您的應用程式符合 Apple 的要求。

使用 Firebase SDK 處理登入流程

如果您正在建立 Web 應用程序,使用 Apple 帳戶透過 Firebase 對使用者進行身份驗證的最簡單方法是使用 Firebase JavaScript SDK 處理整個登入流程。

若要使用 Firebase JavaScript SDK 處理登入流程,請依照下列步驟操作:

  1. 使用對應的提供者 ID apple.com建立OAuthProvider的實例。

    Web modular API

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

    Web namespaced API

    var provider = new firebase.auth.OAuthProvider('apple.com');
  2. 選用:指定您想要從身分驗證提供者要求的預設範圍之外的其他 OAuth 2.0 範圍。

    Web modular API

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

    Web namespaced API

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

    預設情況下,當啟用「每個電子郵件地址一個帳戶」時,Firebase 會要求電子郵件和名稱範圍。如果您將此設定變更為每個電子郵件地址多個帳戶,Firebase 不會向 Apple 要求任何範圍,除非您指定。

  3. 可選:如果您想以英語以外的語言顯示 Apple 的登入螢幕,請設定locale參數。請參閱「使用 Apple 登入」文件以了解支援的區域設定。

    Web modular API

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

    Web namespaced API

    provider.setCustomParameters({
      // Localize the Apple authentication screen in French.
      locale: 'fr'
    });
  4. 使用 OAuth 提供者物件透過 Firebase 進行身份驗證。您可以透過開啟彈出視窗或重定向到登入頁面來提示使用者使用其 Apple 帳戶登入。重定向方法在行動裝置上是首選。

    • 若要使用彈出視窗登錄,請呼叫signInWithPopup()

      Web modular API

      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 namespaced API

      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;
      
          // ...
        });
    • 若要透過重定向到登錄頁面來登錄,請呼叫signInWithRedirect()

    使用signInWithRedirectlinkWithRedirectreauthenticateWithRedirect時請遵循最佳實務

    Web modular API

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

    Web namespaced API

    firebase.auth().signInWithRedirect(provider);

    使用者完成登入並返回頁面後,可以透過呼叫getRedirectResult()來取得登入結果:

    Web modular API

    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 namespaced API

    // 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;
    
        // ...
      });

    這也是您可以捕獲和處理錯誤的地方。有關錯誤代碼的列表,請參閱API 參考

    與 Firebase Auth 支援的其他提供者不同,Apple 不提供照片 URL。

    此外,當使用者選擇不與應用程式分享其電子郵件時,Apple 會為該使用者提供一個獨特的電子郵件地址(格式為xyz@privaterelay.appleid.com ),並與您的應用程式共用。如果您設定了私人電子郵件轉發服務,Apple 會將發送到匿名地址的電子郵件轉發到使用者的真實電子郵件地址。

    Apple 僅在使用者首次登入時與應用程式共用使用者訊息,例如顯示名稱。通常,Firebase 會在使用者首次登入 Apple 時儲存顯示名稱,您可以透過firebase.auth().currentUser.displayName 。但是,如果您之前使用 Apple 將使用者登入應用程式而不使用 Firebase,則 Apple 不會向 Firebase 提供使用者的顯示名稱。

重新驗證和帳戶鏈接

相同的模式可用於reauthenticateWithPopup()reauthenticateWithRedirect() ,您可以使用它們為需要最近登入的敏感操作檢索新憑證:

Web modular API

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 namespaced API

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;

    // ...
  });

並且,您可以使用linkWithPopup()linkWithRedirect()將不同的身分提供者連結到現有帳戶。

請注意,Apple 要求您在將用戶的 Apple 帳戶連結到其他資料之前獲得用戶的明確同意。

例如,要將 Facebook 帳戶連結到目前 Firebase 帳戶,請使用使用者登入 Facebook 時獲得的存取權杖:

Web modular API

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 namespaced API

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

在 Chrome 擴充功能中使用 Firebase 進行驗證

如果您正在建立 Chrome 擴充功能應用程序,請參閱Offscreen Documents 指南

請注意,您仍然必須向 Apple 驗證自訂網域,類似於預設的 firebaseapp.com 網域:

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

令牌撤銷

Apple 要求支援帳戶建立的應用程式必須允許使用者在應用程式內發起刪除其帳戶的操作,如應用程式商店審查指南中所述

為了滿足此要求,請執行下列步驟:

  1. 確保填寫了「使用 Apple 登入」提供者配置的「服務 ID」「OAuth 代碼流配置」部分,如「配置使用 Apple 登入」部分所述。

  2. 由於使用「透過 Apple 登入」建立使用者時,Firebase 不會儲存使用者令牌,因此您必須要求使用者重新登錄,然後才能撤銷其令牌並刪除帳戶。

    然後,從OAuthCredential取得 Apple OAuth 存取令牌,並使用它呼叫revokeAccessToken(auth, token)以撤銷 Apple OAuth 存取令牌。

    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. 最後,刪除使用者帳戶(以及所有關聯資料)。

進階:在 Node.js 中使用 Firebase 進行身份驗證

要在 Node.js 應用程式中使用 Firebase 進行身份驗證:

  1. 使用使用者的 Apple 帳戶登入並取得使用者的 Apple ID 令牌。您可以透過多種方式來完成此任務。例如,如果您的 Node.js 應用程式有一個瀏覽器前端:

    1. 在後端,產生一個隨機字串(“隨機數”)並計算其 SHA256 雜湊值。隨機數是一個一次性使用值,用於驗證後端和 Apple 身份驗證伺服器之間的單次往返。

      Web modular API

      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 namespaced API

      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. 在登入頁面上,在「使用 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. 從 POSTed 驗證回應伺服器端取得 Apple ID 令牌:

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

    另請參閱配置您的網頁以使用 Apple 登入

  2. 取得使用者的 Apple ID 令牌後,使用它來建立 Credential 對象,然後使用該憑證登入使用者:

    Web modular API

    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 namespaced API

    // 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);
      });

下一步

使用者首次登入後,系統會建立新的使用者帳戶,並將其連結到使用者登入時所使用的憑證(即使用者名稱和密碼、電話號碼或驗證提供者資訊)。此新帳戶將作為 Firebase 專案的一部分存儲,並且可用於識別專案中每個應用程式中的用戶,無論用戶如何登入。

  • 在您的應用程式中,了解使用者身份驗證狀態的建議方法是在Auth物件上設定觀察者。然後,您可以從User物件中取得使用者的基本個人資料資訊。請參閱管理用戶

  • 在 Firebase 即時資料庫和雲端儲存安全性規則中,您可以從auth變數取得登入使用者的唯一使用者 ID,並使用它來控制使用者可以存取哪些資料。

您可以透過將身分驗證提供者憑證連結到現有使用者帳戶,允許使用者使用多個驗證提供者登入您的應用程式。

若要登出用戶,請呼叫signOut

Web modular API

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

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

Web namespaced API

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

您可以使用 Firebase SDK 執行端對端 OAuth 2.0 登入流程,讓使用者使用其 Apple ID 透過 Firebase 進行身份驗證。

在你開始之前

若要使用 Apple 登入用戶,請先在 Apple 開發者網站上設定 Sign In with Apple,然後啟用 Apple 作為 Firebase 專案的登入提供者。

加入蘋果開發者計劃

「使用 Apple 登入」只能由Apple 開發者計畫的成員進行設定。

配置使用 Apple 登入

Apple 開發者網站上,執行以下操作:

  1. 依照設定使用 Apple 網頁版登入的第一部分所述將您的網站與應用程式相關聯。出現提示時,將以下 URL 註冊為返回 URL:

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

    您可以在Firebase 控制台設定頁面上取得您的 Firebase 專案 ID。

    完成後,記下新的服務 ID,下一部分將需要它。

  2. 建立使用 Apple 私鑰登入。在下一部分中,您將需要新的私鑰和金鑰 ID。
  3. 如果您使用 Firebase 驗證的任何向使用者發送電子郵件的功能(包括電子郵件連結登入、電子郵件地址驗證、帳戶變更撤銷等),請設定 Apple 私人電子郵件中繼服務並註冊noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com (或您的自訂電子郵件範本網域),以便 Apple 可以將 Firebase 驗證傳送的電子郵件轉送至匿名 Apple 電子郵件地址。

啟用 Apple 作為登入供應商

  1. 將 Firebase 新增到您的專案中
  2. Firebase 控制台中,開啟「驗證」部分。在「登入方法」標籤上,啟用Apple提供者。指定您在上一部分中建立的服務 ID。另外,在OAuth 程式碼流配置部分中,指定您的 Apple 團隊 ID 以及您在上一部分中建立的私鑰和金鑰 ID。

遵守 Apple 匿名資料要求

「使用 Apple 登入」為使用者提供了在登入時對其資料(包括電子郵件地址)進行匿名化的選項。選擇此選項的使用者的電子郵件地址的網域為privaterelay.appleid.com 。當您在應用程式中使用「透過 Apple 登入」時,您必須遵守 Apple 關於這些匿名 Apple ID 的任何適用的開發者政策或條款。

這包括在將任何直接識別個人資訊與匿名 Apple ID 關聯之前獲得任何所需的用戶同意。使用 Firebase 身份驗證時,這可能包括以下操作:

  • 將電子郵件地址連結到匿名 Apple ID,反之亦然。
  • 將電話號碼連結到匿名 Apple ID,反之亦然
  • 將非匿名社交憑證(Facebook、Google 等)連結到匿名 Apple ID,反之亦然。

上述列表並非詳盡無遺。請參閱開發者帳戶的「會員資格」部分中的 Apple 開發者計畫許可協議,以確保您的應用程式符合 Apple 的要求。

使用 Firebase SDK 處理登入流程

如果您正在建立 Web 應用程序,使用 Apple 帳戶透過 Firebase 對使用者進行身份驗證的最簡單方法是使用 Firebase JavaScript SDK 處理整個登入流程。

若要使用 Firebase JavaScript SDK 處理登入流程,請依照下列步驟操作:

  1. 使用對應的提供者 ID apple.com建立OAuthProvider的實例。

    Web modular API

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

    Web namespaced API

    var provider = new firebase.auth.OAuthProvider('apple.com');
  2. 選用:指定您想要從身分驗證提供者要求的預設範圍之外的其他 OAuth 2.0 範圍。

    Web modular API

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

    Web namespaced API

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

    預設情況下,當啟用「每個電子郵件地址一個帳戶」時,Firebase 會要求電子郵件和名稱範圍。如果您將此設定變更為每個電子郵件地址多個帳戶,Firebase 不會向 Apple 要求任何範圍,除非您指定。

  3. 可選:如果您想以英語以外的語言顯示 Apple 的登入螢幕,請設定locale參數。請參閱「使用 Apple 登入」文件以了解支援的區域設定。

    Web modular API

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

    Web namespaced API

    provider.setCustomParameters({
      // Localize the Apple authentication screen in French.
      locale: 'fr'
    });
  4. 使用 OAuth 提供者物件透過 Firebase 進行身份驗證。您可以透過開啟彈出視窗或重定向到登入頁面來提示使用者使用其 Apple 帳戶登入。重定向方法在行動裝置上是首選。

    • 若要使用彈出視窗登錄,請呼叫signInWithPopup()

      Web modular API

      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 namespaced API

      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;
      
          // ...
        });
    • 若要透過重定向到登錄頁面來登錄,請呼叫signInWithRedirect()

    使用signInWithRedirectlinkWithRedirectreauthenticateWithRedirect時請遵循最佳實務

    Web modular API

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

    Web namespaced API

    firebase.auth().signInWithRedirect(provider);

    使用者完成登入並返回頁面後,可以透過呼叫getRedirectResult()來取得登入結果:

    Web modular API

    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 namespaced API

    // 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;
    
        // ...
      });

    這也是您可以捕獲和處理錯誤的地方。有關錯誤代碼的列表,請參閱API 參考

    與 Firebase Auth 支援的其他提供者不同,Apple 不提供照片 URL。

    此外,當使用者選擇不與應用程式分享其電子郵件時,Apple 會為該使用者提供一個獨特的電子郵件地址(格式為xyz@privaterelay.appleid.com ),並與您的應用程式共用。如果您設定了私人電子郵件轉發服務,Apple 會將發送到匿名地址的電子郵件轉發到使用者的真實電子郵件地址。

    Apple 僅在使用者首次登入時與應用程式共用使用者訊息,例如顯示名稱。通常,Firebase 會在使用者首次登入 Apple 時儲存顯示名稱,您可以透過firebase.auth().currentUser.displayName 。但是,如果您之前使用 Apple 將使用者登入應用程式而不使用 Firebase,則 Apple 不會向 Firebase 提供使用者的顯示名稱。

重新驗證和帳戶鏈接

相同的模式可用於reauthenticateWithPopup()reauthenticateWithRedirect() ,您可以使用它們為需要最近登入的敏感操作檢索新憑證:

Web modular API

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 namespaced API

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;

    // ...
  });

並且,您可以使用linkWithPopup()linkWithRedirect()將不同的身分提供者連結到現有帳戶。

請注意,Apple 要求您在將用戶的 Apple 帳戶連結到其他資料之前獲得用戶的明確同意。

例如,要將 Facebook 帳戶連結到目前 Firebase 帳戶,請使用使用者登入 Facebook 時獲得的存取權杖:

Web modular API

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 namespaced API

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

在 Chrome 擴充功能中使用 Firebase 進行驗證

如果您正在建立 Chrome 擴充功能應用程序,請參閱Offscreen Documents 指南

請注意,您仍然必須向 Apple 驗證自訂網域,類似於預設的 firebaseapp.com 網域:

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

令牌撤銷

Apple 要求支援帳戶建立的應用程式必須允許使用者在應用程式內發起刪除其帳戶的操作,如應用程式商店審查指南中所述

為了滿足此要求,請執行下列步驟:

  1. 確保填寫了「使用 Apple 登入」提供者配置的「服務 ID」「OAuth 代碼流配置」部分,如「配置使用 Apple 登入」部分所述。

  2. 由於使用「透過 Apple 登入」建立使用者時,Firebase 不會儲存使用者令牌,因此您必須要求使用者重新登錄,然後才能撤銷其令牌並刪除帳戶。

    然後,從OAuthCredential取得 Apple OAuth 存取令牌,並使用它呼叫revokeAccessToken(auth, token)以撤銷 Apple OAuth 存取令牌。

    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. 最後,刪除使用者帳戶(以及所有關聯資料)。

進階:在 Node.js 中使用 Firebase 進行身份驗證

要在 Node.js 應用程式中使用 Firebase 進行身份驗證:

  1. 使用使用者的 Apple 帳戶登入並取得使用者的 Apple ID 令牌。您可以透過多種方式來完成此任務。例如,如果您的 Node.js 應用程式有一個瀏覽器前端:

    1. 在後端,產生一個隨機字串(“隨機數”)並計算其 SHA256 雜湊值。隨機數是一個一次性使用值,用於驗證後端和 Apple 身份驗證伺服器之間的單次往返。

      Web modular API

      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 namespaced API

      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. 在登入頁面上,在「使用 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. 從 POSTed 驗證回應伺服器端取得 Apple ID 令牌:

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

    另請參閱配置您的網頁以使用 Apple 登入

  2. 取得使用者的 Apple ID 令牌後,使用它來建立 Credential 對象,然後使用該憑證登入使用者:

    Web modular API

    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 namespaced API

    // 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);
      });

下一步

使用者首次登入後,系統會建立新的使用者帳戶,並將其連結到使用者登入時所使用的憑證(即使用者名稱和密碼、電話號碼或驗證提供者資訊)。此新帳戶將作為 Firebase 專案的一部分存儲,並且可用於識別專案中每個應用程式中的用戶,無論用戶如何登入。

  • 在您的應用程式中,了解使用者身份驗證狀態的建議方法是在Auth物件上設定觀察者。然後,您可以從User物件中取得使用者的基本個人資料資訊。請參閱管理用戶

  • 在 Firebase 即時資料庫和雲端儲存安全性規則中,您可以從auth變數取得登入使用者的唯一使用者 ID,並使用它來控制使用者可以存取哪些資料。

您可以透過將身分驗證提供者憑證連結到現有使用者帳戶,允許使用者使用多個驗證提供者登入您的應用程式。

若要登出用戶,請呼叫signOut

Web modular API

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

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

Web namespaced API

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