通過使用 Firebase SDK 執行端到端 OAuth 2.0 登錄流程,您可以讓您的用戶使用他們的 Apple ID 通過 Firebase 進行身份驗證。
在你開始之前
要使用 Apple 登錄用戶,首先在 Apple 的開發者網站上配置 Sign In with Apple,然後啟用 Apple 作為您的 Firebase 項目的登錄提供商。
加入蘋果開發者計劃
Sign In with Apple 只能由Apple Developer Program的成員配置。
配置使用 Apple 登錄
在Apple Developer網站上,執行以下操作:
將您的網站關聯到您的應用程序,如為 Web 配置登錄 Apple 的第一部分中所述。出現提示時,將以下 URL 註冊為返回 URL:
https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler
您可以在Firebase 控制台設置頁面上獲取您的 Firebase 項目 ID。
完成後,記下您的新服務 ID,您將在下一節中用到它。
- 創建一個 Sign In with Apple private key 。在下一節中,您將需要新的私鑰和密鑰 ID。
如果您使用 Firebase 身份驗證的任何向用戶發送電子郵件的功能,包括電子郵件鏈接登錄、電子郵件地址驗證、帳戶更改撤銷等,請配置 Apple 私人電子郵件中繼服務並註冊
noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com
(或您自定義的電子郵件模板域),以便 Apple 可以將 Firebase 身份驗證發送的電子郵件轉發到匿名的 Apple 電子郵件地址。
啟用 Apple 作為登錄提供商
- 將 Firebase 添加到您的項目中。
- 在Firebase 控制台中,打開Auth部分。在登錄方法選項卡上,啟用Apple提供商。指定您在上一節中創建的服務 ID。此外,在OAuth 代碼流配置部分,指定您的 Apple Team ID 以及您在上一節中創建的私鑰和密鑰 ID。
符合 Apple 匿名數據要求
Sign In with Apple 為用戶提供了在登錄時匿名化他們的數據的選項,包括他們的電子郵件地址。選擇此選項的用戶擁有域為privaterelay.appleid.com
的電子郵件地址。當您在您的應用程序中使用 Sign In with Apple 時,您必須遵守 Apple 關於這些匿名 Apple ID 的任何適用的開發者政策或條款。
這包括在將任何直接識別個人信息與匿名 Apple ID 相關聯之前獲得任何必要的用戶同意。使用 Firebase 身份驗證時,這可能包括以下操作:
- 將電子郵件地址鏈接到匿名的 Apple ID,反之亦然。
- 將電話號碼鏈接到匿名的 Apple ID,反之亦然
- 將非匿名社交憑證(Facebook、Google 等)鏈接到匿名 Apple ID,反之亦然。
上面的列表並不詳盡。請參閱您開發者帳戶的“會員資格”部分中的 Apple 開發者計劃許可協議,以確保您的應用程序符合 Apple 的要求。
使用 Firebase SDK 處理登錄流程
如果您正在構建網絡應用程序,使用 Apple 帳戶通過 Firebase 對用戶進行身份驗證的最簡單方法是使用 Firebase JavaScript SDK 處理整個登錄流程。
要使用 Firebase JavaScript SDK 處理登錄流程,請執行以下步驟:
使用相應的提供者 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');
可選:指定超出您希望從身份驗證提供程序請求的默認範圍的其他 OAuth 2.0 範圍。
Web modular API
provider.addScope('email'); provider.addScope('name');
Web namespaced API
provider.addScope('email'); provider.addScope('name');
默認情況下,啟用每個電子郵件地址一個帳戶時,Firebase 會請求電子郵件和名稱範圍。如果您將此設置更改為Multiple accounts per email address ,除非您指定,否則 Firebase 不會向 Apple 請求任何範圍。
可選:如果要以英語以外的語言顯示 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' });
使用 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()
:
使用
signInWithRedirect
、linkWithRedirect
或reauthenticateWithRedirect
時遵循最佳實踐。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 擴展應用,則必須添加您的 Chrome 擴展 ID:
- 在Firebase 控制台中打開您的項目。
- 在身份驗證部分中,打開登錄方法頁面。
- 將如下所示的 URI 添加到授權域列表中:
chrome-extension://CHROME_EXTENSION_ID
只有彈出操作( signInWithPopup
、 linkWithPopup
和reauthenticateWithPopup
)可用於 Chrome 擴展,因為 Chrome 擴展不能使用 HTTP 重定向。您應該從後台頁面腳本而不是瀏覽器操作彈出窗口調用這些方法,因為身份驗證彈出窗口將取消瀏覽器操作彈出窗口。彈出方法只能在使用Manifest V2 的擴展中使用。較新的Manifest V3只允許以 service workers 的形式運行後台腳本,根本無法執行彈窗操作。
在 Chrome 擴展程序的清單文件中,確保將https://apis.google.com
URL 添加到content_security_policy
白名單。
請注意,您仍必須像驗證默認的 firebaseapp.com 域一樣向 Apple 驗證自定義域:
http://auth.custom.example.com/.well-known/apple-developer-domain-association.txt
高級:在 Node.js 中使用 Firebase 進行身份驗證
要在 Node.js 應用程序中使用 Firebase 進行身份驗證:
使用用戶的 Apple 帳戶登錄用戶並獲取用戶的 Apple ID 令牌。您可以通過多種方式完成此操作。例如,如果您的 Node.js 應用程序有一個瀏覽器前端:
在您的後端,生成一個隨機字符串(“nonce”)併計算其 SHA256 哈希值。 nonce 是一次性使用值,用於驗證後端和 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');
在您的登錄頁面上,在您的 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>
從 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 登錄。
獲取用戶的 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 Realtime Database 和 Cloud Storage Security Rules中,您可以從
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. });