Google 致力于为黑人社区推动种族平等。查看具体举措

使用带有 JavaScript 的 Apple 进行身份验证

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

通过使用 Firebase SDK 执行端到端 OAuth 2.0 登录流程,您可以让您的用户使用他们的 Apple ID 通过 Firebase 进行身份验证。

在你开始之前

要使用 Apple 登录用户,首先在 Apple 的开发者网站上配置使用 Apple 登录,然后启用 Apple 作为您的 Firebase 项目的登录提供商。

加入苹果开发者计划

Sign In with Apple 只能由Apple Developer Program的成员配置。

配置使用 Apple 登录

Apple Developer网站上,执行以下操作:

  1. 将您的网站关联到您的应用程序,如为 Web 配置登录 Apple的第一部分中所述。出现提示时,将以下 URL 注册为返回 URL:

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

    您可以在Firebase 控制台设置页面上获取您的 Firebase 项目 ID。

    完成后,记下您的新服务 ID,您将在下一节中用到它。

  2. 创建一个 Sign In with Apple private key 。在下一节中,您将需要新的私钥和密钥 ID。
  3. 如果您使用 Firebase 身份验证的任何向用户发送电子邮件的功能,包括电子邮件链接登录、电子邮件地址验证、帐户更改撤销等,请配置 Apple 私人电子邮件中继服务并注册noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com (或您自定义的电子邮件模板域),以便 Apple 可以将 Firebase 身份验证发送的电子邮件转发到匿名的 Apple 电子邮件地址。

启用 Apple 作为登录提供商

  1. 将 Firebase 添加到您的项目中。
  2. 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 处理登录流程,请执行以下步骤:

  1. 使用相应的提供者 ID apple.com创建OAuthProvider的实例。

    Web version 9

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

    Web version 8

    var provider = new firebase.auth.OAuthProvider('apple.com');
  2. 可选:指定超出您希望从身份验证提供程序请求的默认范围的其他 OAuth 2.0 范围。

    Web version 9

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

    Web version 8

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

    默认情况下,启用每个电子邮件地址一个帐户时,Firebase 会请求电子邮件和名称范围。如果您将此设置更改为Multiple accounts per email address ,除非您指定,否则 Firebase 不会向 Apple 请求任何范围。

  3. 可选:如果要以英语以外的语言显示 Apple 的登录屏幕,请设置locale参数。有关支持的语言环境,请参阅使用 Apple 文档登录

    Web version 9

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

    Web version 8

    provider.setCustomParameters({
      // Localize the Apple authentication screen in French.
      locale: 'fr'
    });
  4. 使用 OAuth 提供程序对象通过 Firebase 进行身份验证。您可以通过打开弹出窗口或重定向到登录页面来提示您的用户使用他们的 Apple 帐户登录。在移动设备上首选重定向方法。

    • 要使用弹出窗口登录,请调用signInWithPopup()

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

      Web version 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()

    使用signInWithRedirectlinkWithRedirectreauthenticateWithRedirect时遵循最佳实践

    Web version 9

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

    Web version 8

    firebase.auth().signInWithRedirect(provider);

    用户完成登录返回页面后,可以调用getRedirectResult()获取登录结果:

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

    Web version 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。

    此外,当用户选择不与应用程序共享他们的电子邮件时,Apple 会为该用户提供一个唯一的电子邮件地址(格式xyz@privaterelay.appleid.com ),并将其与您的应用程序共享。如果您配置了私人电子邮件中继服务,Apple 会将发送到匿名地址的电子邮件转发到用户的真实电子邮件地址。

    Apple 仅在用户首次登录时与应用共享用户信息,例如显示名称。通常,Firebase 会在用户首次登录 Apple 时存储显示名称,您可以通过firebase.auth().currentUser.displayName 。但是,如果您之前使用 Apple 在不使用 Firebase 的情况下让用户登录应用程序,Apple 将不会向 Firebase 提供用户的显示名称。

重新验证和帐户链接

相同的模式可用于reauthenticateWithPopup()reauthenticateWithRedirect() ,您可以使用它来为需要最近登录的敏感操作检索新凭证:

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

    // ...
  });

Web version 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 获得的访问令牌:

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

Web version 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. Firebase 控制台中打开您的项目。
  2. 身份验证部分中,打开登录方法页面。
  3. 将如下所示的 URI 添加到授权域列表中:
    chrome-extension://CHROME_EXTENSION_ID

只有弹出操作( signInWithPopuplinkWithPopupreauthenticateWithPopup )可用于 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 进行身份验证:

  1. 使用用户的 Apple 帐户登录用户并获取用户的 Apple ID 令牌。您可以通过多种方式完成此操作。例如,如果您的 Node.js 应用程序有一个浏览器前端:

    1. 在您的后端,生成一个随机字符串(“nonce”)并计算其 SHA256 哈希值。 nonce 是一次性使用值,用于验证后端和 Apple 的身份验证服务器之间的单次往返。

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

      Web version 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.slice(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 身份验证响应服务器端获取 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 version 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);
      });

    Web version 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对象获取用户的基本个人资料信息。请参阅管理用户

  • 在您的 Firebase Realtime Database 和 Cloud Storage Security Rules中,您可以从auth变量中获取登录用户的唯一用户 ID,并使用它来控制用户可以访问的数据。

您可以允许用户使用多个身份验证提供程序登录您的应用程序,方法是将身份验证提供程序凭据链接到现有用户帐户。

要注销用户,请调用signOut

Web version 9

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

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

Web version 8

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

通过使用 Firebase SDK 执行端到端 OAuth 2.0 登录流程,您可以让您的用户使用他们的 Apple ID 通过 Firebase 进行身份验证。

在你开始之前

要使用 Apple 登录用户,首先在 Apple 的开发者网站上配置使用 Apple 登录,然后启用 Apple 作为您的 Firebase 项目的登录提供商。

加入苹果开发者计划

Sign In with Apple 只能由Apple Developer Program的成员配置。

配置使用 Apple 登录

Apple Developer网站上,执行以下操作:

  1. 将您的网站关联到您的应用程序,如为 Web 配置登录 Apple的第一部分中所述。出现提示时,将以下 URL 注册为返回 URL:

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

    您可以在Firebase 控制台设置页面上获取您的 Firebase 项目 ID。

    完成后,记下您的新服务 ID,您将在下一节中用到它。

  2. 创建一个 Sign In with Apple private key 。在下一节中,您将需要新的私钥和密钥 ID。
  3. 如果您使用 Firebase 身份验证的任何向用户发送电子邮件的功能,包括电子邮件链接登录、电子邮件地址验证、帐户更改撤销等,请配置 Apple 私人电子邮件中继服务并注册noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com (或您自定义的电子邮件模板域),以便 Apple 可以将 Firebase 身份验证发送的电子邮件转发到匿名的 Apple 电子邮件地址。

启用 Apple 作为登录提供商

  1. 将 Firebase 添加到您的项目中。
  2. 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 处理登录流程,请执行以下步骤:

  1. 使用相应的提供者 ID apple.com创建OAuthProvider的实例。

    Web version 9

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

    Web version 8

    var provider = new firebase.auth.OAuthProvider('apple.com');
  2. 可选:指定超出您希望从身份验证提供程序请求的默认范围的其他 OAuth 2.0 范围。

    Web version 9

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

    Web version 8

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

    默认情况下,启用每个电子邮件地址一个帐户时,Firebase 会请求电子邮件和名称范围。如果您将此设置更改为Multiple accounts per email address ,除非您指定,否则 Firebase 不会向 Apple 请求任何范围。

  3. 可选:如果要以英语以外的语言显示 Apple 的登录屏幕,请设置locale参数。有关支持的语言环境,请参阅使用 Apple 文档登录

    Web version 9

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

    Web version 8

    provider.setCustomParameters({
      // Localize the Apple authentication screen in French.
      locale: 'fr'
    });
  4. 使用 OAuth 提供程序对象通过 Firebase 进行身份验证。您可以通过打开弹出窗口或重定向到登录页面来提示您的用户使用他们的 Apple 帐户登录。在移动设备上首选重定向方法。

    • 要使用弹出窗口登录,请调用signInWithPopup()

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

      Web version 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()

    使用signInWithRedirectlinkWithRedirectreauthenticateWithRedirect时遵循最佳实践

    Web version 9

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

    Web version 8

    firebase.auth().signInWithRedirect(provider);

    用户完成登录返回页面后,可以调用getRedirectResult()获取登录结果:

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

    Web version 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。

    此外,当用户选择不与应用程序共享他们的电子邮件时,Apple 会为该用户提供一个唯一的电子邮件地址(格式xyz@privaterelay.appleid.com ),并将其与您的应用程序共享。如果您配置了私人电子邮件中继服务,Apple 会将发送到匿名地址的电子邮件转发到用户的真实电子邮件地址。

    Apple 仅在用户首次登录时与应用共享用户信息,例如显示名称。通常,Firebase 会在用户首次登录 Apple 时存储显示名称,您可以通过firebase.auth().currentUser.displayName 。但是,如果您之前使用 Apple 在不使用 Firebase 的情况下让用户登录应用程序,Apple 将不会向 Firebase 提供用户的显示名称。

重新验证和帐户链接

相同的模式可用于reauthenticateWithPopup()reauthenticateWithRedirect() ,您可以使用它来为需要最近登录的敏感操作检索新凭证:

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

    // ...
  });

Web version 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 获得的访问令牌:

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

Web version 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. Firebase 控制台中打开您的项目。
  2. 身份验证部分中,打开登录方法页面。
  3. 将如下所示的 URI 添加到授权域列表中:
    chrome-extension://CHROME_EXTENSION_ID

只有弹出操作( signInWithPopuplinkWithPopupreauthenticateWithPopup )可用于 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 进行身份验证:

  1. 使用用户的 Apple 帐户登录用户并获取用户的 Apple ID 令牌。您可以通过多种方式完成此操作。例如,如果您的 Node.js 应用程序有一个浏览器前端:

    1. 在您的后端,生成一个随机字符串(“nonce”)并计算其 SHA256 哈希值。 nonce 是一次性使用值,用于验证后端和 Apple 的身份验证服务器之间的单次往返。

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

      Web version 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.slice(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 身份验证响应服务器端获取 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 version 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);
      });

    Web version 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对象获取用户的基本个人资料信息。请参阅管理用户

  • 在您的 Firebase Realtime Database 和 Cloud Storage Security Rules中,您可以从auth变量中获取登录用户的唯一用户 ID,并使用它来控制用户可以访问的数据。

您可以允许用户使用多个身份验证提供程序登录您的应用程序,方法是将身份验证提供程序凭据链接到现有用户帐户。

要注销用户,请调用signOut

Web version 9

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

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

Web version 8

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