Catch up on everthing we announced at this year's Firebase Summit. Learn more

使用 Apple 和 JavaScript 進行身份驗證

通過使用 Firebase SDK 執行端到端 OAuth 2.0 登錄流程,您可以讓您的用戶使用他們的 Apple ID 向 Firebase 進行身份驗證。

在你開始之前

要使用 Apple 登錄用戶,請首先在 Apple 的開發者網站上配置使用 Apple 登錄,然後啟用 Apple 作為 Firebase 項目的登錄提供程序。

加入 Apple 開發者計劃

登錄與蘋果只能由成員進行配置的蘋果開發者計劃

配置使用 Apple 登錄

蘋果開發者網站,請執行以下操作:

  1. 在第一部分中描述關聯你的網站,你的應用程序的網絡與蘋果公司配置登錄。出現提示時,將以下 URL 註冊為返回 URL:

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

    你可以得到你的火力地堡項目ID火力地堡控制台設置頁面

    完成後,記下您的新服務 ID,您將在下一部分中用到它。

  2. 創建一個登錄與蘋果的私鑰。在下一部分中,您將需要新的私鑰和密鑰 ID。
  3. 如果你使用任何的火力地堡認證的功能,發送電子郵件給用戶,包括電子郵件鏈接登錄,電子郵件地址驗證,賬戶撤銷的變化,和其他人,配置了蘋果私人電子郵件中繼服務和註冊noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com (或您自定義的電子郵件模板域),以便 Apple 可以將 Firebase 身份驗證發送的電子郵件中繼到匿名的 Apple 電子郵件地址。

啟用 Apple 作為登錄提供商

  1. 添加火力地堡到您的項目
  2. 火力地堡控制台,打開驗證部分。在登錄方法選項卡,使蘋果的供應商。指定您在上一節中創建的服務 ID。此外,在OAuth碼流的配置部分,指定您的蘋果團隊ID和私有密鑰和密鑰ID您在上一節中創建。

遵守 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的一個實例。

    網頁版 9

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

    網頁版 8

    var provider = new firebase.auth.OAuthProvider('apple.com');
  2. 可選:指定其他的OAuth 2.0範圍超出了預設的,你想從身份驗證提供者的要求。

    網頁版 9

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

    網頁版 8

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

    默認情況下,啟用每個電子郵件地址帳戶,火力地堡請求電子郵件,姓名等範圍。如果更改此設置每個電子郵件地址多個帳戶,除非你指定它們火力地堡不要求從蘋果的任何範圍。

  3. 可選:如果您想在英語以外的語言顯示蘋果的登錄畫面中,將locale參數。請參閱登錄與蘋果文檔的支持的語言環境。

    網頁版 9

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

    網頁版 8

    provider.setCustomParameters({
      // Localize the Apple authentication screen in French.
      locale: 'fr'
    });
  4. 使用 OAuth 提供程序對象通過 Firebase 進行身份驗證。您可以通過打開彈出窗口或重定向到登錄頁面來提示您的用戶使用他們的 Apple 帳戶登錄。在移動設備上首選重定向方法。

    • 要使用彈出窗口,呼叫登錄signInWithPopup()

      網頁版 9

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

      網頁版 8

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

      網頁版 9

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

      網頁版 8

      firebase.auth().signInWithRedirect(provider);

      用戶完成登錄之後,返回到頁面上,你可以通過調用拿到簽入結果getRedirectResult()

      網頁版 9

      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.email;
          // The credential that was used.
          const credential = OAuthProvider.credentialFromError(error);
      
          // ...
        });

      網頁版 8

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

    此外,當用戶選擇不與應用程序,蘋果公司規定一個唯一的電子郵件地址的用戶(形式分享他們的電子郵件xyz@privaterelay.appleid.com ,其股您的應用程序)。如果您配置了私人電子郵件中繼服務,Apple 會將發送到匿名地址的電子郵件轉發到用戶的真實電子郵件地址。

    蘋果只股票的用戶信息,如顯示名稱與應用第一次在用戶登錄。通常情況下,火力地堡商店的顯示名稱的第一次用戶登錄在與蘋果,你可以得到firebase.auth().currentUser.displayName 。但是,如果您之前使用 Apple 登錄用戶而不使用 Firebase,Apple 將不會向 Firebase 提供用戶的顯示名稱。

重新認證和賬戶關聯

同樣的模式可以被用來reauthenticateWithPopup()reauthenticateWithRedirect()您可以用它來檢索需要最近登錄敏感操作到新的憑據:

網頁版 9

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.email;
    // The credential that was used.
    const credential = OAuthProvider.credentialFromError(error);

    // ...
  });

網頁版 8

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;

    // ...
  })
  .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 獲得的訪問令牌:

網頁版 9

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

網頁版 8

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 擴展程序應用程序,則必須添加您的 Chrome 擴展程序 ID:

  1. 打開在項目火力地堡控制台
  2. 身份驗證部分,打開登錄方法頁。
  3. 添加一個URI類似下面的授權域的列表:
    chrome-extension://CHROME_EXTENSION_ID

只有彈出操作( signInWithPopuplinkWithPopupreauthenticateWithPopup )可供選擇Chrome瀏覽器擴展,如Chrome擴展不能使用HTTP重定向。您應該從後台頁面腳本而不是瀏覽器操作彈出窗口調用這些方法,因為身份驗證彈出窗口將取消瀏覽器操作彈出窗口。彈出窗口的方法可以只使用擴展使用清單V2 。較新的清單V3只允許在服務人員的形式,不能在所有執行彈出操作後台腳本。

在您的Chrome擴展程序清單文件確保您添加https://apis.google.com URL到content_security_policy允許列表。

請注意,您仍然必須與 Apple 驗證自定義域,類似於默認的 firebaseapp.com 域:

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

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

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

  1. 使用他們的 Apple 帳戶登錄用戶並獲取用戶的 Apple ID 令牌。您可以通過多種方式完成此操作。例如,如果您的 Node.js 應用程序有一個瀏覽器前端:

    1. 在您的後端,生成一個隨機字符串(“nonce”)併計算其 SHA256 哈希值。 nonce 是一次性使用值,用於驗證後端和 Apple 身份驗證服務器之間的單次往返。

      網頁版 9

      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.substr(0, length);
      };
      
      const unhashedNonce = generateNonce(10);
      
      // SHA256-hashed nonce in hex
      const hashedNonceHex = crypto.createHash('sha256')
        .update(unhashedNonce).digest().toString('hex');

      網頁版 8

      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.substr(0, length);
      };
      
      const unhashedNonce = generateNonce(10);
      
      // SHA256-hashed nonce in hex
      const hashedNonceHex = crypto.createHash('sha256')
        .update(unhashedNonce).digest().toString('hex');
    2. 在您的登錄頁面上,在您的 Sign In with 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 auth 響應服務器端獲取 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).
        }
      });
      

    另請參閱配置您的登錄頁面與蘋果

  2. 獲取用戶的 Apple ID 令牌後,使用它構建 Credential 對象,然後使用該憑據登錄用戶:

    網頁版 9

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

    網頁版 8

    // 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對象。請參閱管理用戶

  • 在你的火力地堡實時數據庫和雲存儲安全規則,你可以得到簽署的,從用戶的唯一的用戶ID auth的變量,並用它來控制哪些數據的用戶可以訪問。

您可以允許用戶通過使用多個身份驗證提供登錄到您的應用程序連接身份驗證提供憑據到現有的用戶帳戶。

要註銷用戶,請撥打signOut

網頁版 9

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

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

網頁版 8

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