使用 Identity Platform 和 Google 身份向 Firestore 验证用户的身份


本文档介绍如何使用 Identity Platform 作为用户身份和访问权限管理平台,设置对 Firestore 数据库的基于用户的访问权限控制。通过 Identity Platform,您可以在应用中添加身份验证层,以便保护和管理客户凭据。Firestore 是一种基于文档的灵活 NoSQL 数据库。它使用名为 Firestore 安全规则的规则语言来控制对此数据的访问,因此您无需编写服务器端授权代码。

本文档适用于想要将 Firestore 与 Firestore 安全规则结合使用并想要使用外部登录提供方(如 Google)对用户进行 Identity Platform 身份验证的开发者和安全专业人员。本文档中的代码演示了使用 Identity Platform 和 Firestore 的两种方法:

  • REST API 调用,使用 JavaScript 调用 Identity Platform 和 Firestore API。这种方法可让您完全控制 Web 应用创建 Identity Platform 请求的方式。
  • Identity Platform Client SDK,使用 Identity Platform Client SDK 和 Firestore SDK 管理 Identity Platform 的登录过程以及 Firestore 查询方式。该 SDK 提供基于 Identity Platform REST API 的 JavaScript 封装容器函数,让您可以通过 JavaScript 函数和对象调用 Identity Platform,而无需手动创建 HTTP 请求。

Identity Platform Client SDK 和 Firebase Client SDK 共享相同的 SDK。SDK 支持 Identity Platform 的所有功能。为了保持向后兼容性,该 SDK 会保留 Firebase 品牌元素。

架构

下图展示了本文档介绍的逻辑架构:

逻辑架构图。

除了 Identity Platform 和 Firestore 之外,本文档还使用并演示以下组件:

  • Web 应用:允许用户使用 Google 身份登录 Identity Platform 的应用。然后,它会向 Firestore 查询已登录用户的相关信息。
  • Google 登录:本示例中使用的身份提供方。
  • 身份验证处理程序:从 Google 获取响应、使用 Identity Platform 执行登录并将结果发送回 Web 应用以完成登录的服务端点。

目标

  • 为您的 Google Cloud 项目设置 Identity Platform。
  • 将 Google 添加为 Identity Platform 的登录提供方。
  • 使用 Firestore 安全规则控制对 Firestore 数据库的访问。
  • 使用 Identity Platform API 和 Identity Platform Client SDK 让用户登录 Web 应用。
  • 使用 Firestore REST API 和 Firestore JavaScript 客户端 SDK 从客户端 Web 应用安全地访问 Firestore。

费用

在本文档中,您将使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。 Google Cloud 新用户可能有资格申请免费试用

准备工作

  1. 在 Google Cloud Console 中的项目选择器页面上,选择或创建一个 Google Cloud 项目

    转到“项目选择器”

    如果您选择现有项目,则必须选择满足以下条件的项目:

    • 未启用 Datastore。
    • 未启用 App Engine。
    • 项目不包含具有现有安全规则的 Firestore 数据库。您可以使用本文档中介绍的规则覆盖所有现有规则。
  2. 确保您的 Google Cloud 项目已启用结算功能

  3. 启用 Identity Platform:
    1. 转到 Google Cloud 控制台中的 Identity Platform Marketplace 页面。

      前往 Identity Platform Marketplace 页面

    2. 点击启用 Identity Platform,然后等待操作完成。
  4. 启用 Firestore:
    1. 在 Google Cloud 控制台中,打开左侧的菜单,然后选择 Firestore
    2. 选择 Firestore 模式。点击选择原生模式
    3. 选择数据存储位置。选择离您最近的区域。点击创建数据库

完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理

配置 Identity Platform

如需在 Identity Platform 中启用用户身份验证,您必须添加身份提供商。

在 Identity Platform 中配置 Google 提供商之前,您必须先配置 Google OAuth 同意屏幕。用户首次登录您的 Web 应用时,会看到此同意屏幕。在 OAuth 同意屏幕配置中,您可以设置应用的名称、应用徽标和支持电子邮件地址等属性。

  1. 在 Google Cloud 控制台中,转到身份提供商页面。

    前往“身份提供商”页面

  2. 点击添加提供商
  3. 选择 Google,然后在新的身份提供商页面中点击 API 和服务链接。系统会在新标签页中打开凭据页面。请勿关闭上一个标签页,因为您需要从凭据页面复制到新的身份提供商页面。
  4. 凭据页面上,点击 Web 客户端(由 Google 服务自动创建)凭据。
  5. 复制客户端 ID 值,然后转到显示新的身份提供商页面的标签页,然后将相应值粘贴到 Web 客户端 ID 字段中。重复此步骤,将客户端密钥值复制到 Web 客户端密钥字段。
  6. 新的身份提供商页面上,点击保存
  7. 返回凭据页面,然后点击 OAuth 同意屏幕
  8. 选择外部,然后点击创建
  9. OAuth 同意屏幕页面上,添加以下信息:
    • 应用名称Identity Platform Tutorial
    • 支持电子邮件:从下拉列表中选择您的电子邮件地址。
  10. 点击保存
  11. 修改应用注册页面上,添加以下信息:
    • 应用名称Identity Platform Tutorial
    • 用户支持电子邮件:从下拉列表中选择您的电子邮件地址。
    • 电子邮件地址:输入您的电子邮件地址。
  12. 点击保存并继续
  13. 范围可选信息页面上,点击保存并继续
  14. 摘要页面上,点击返回信息中心

配置 Firestore

您创建的新 Firestore 数据库目前为空。新的 Firestore 数据库还具有一组默认的安全规则,允许任何人对数据库执行读取操作。这些默认规则可阻止任何人向数据库写入数据。在接下来的步骤中,您将向数据库填充数据,并更新安全规则,以将读取(查询)请求限制给获得授权的用户。

在 Firestore 中创建测试数据

在此过程中,您可以通过从 Web 应用查询 Firestore 集合来测试安全规则。

首先,创建多个文档来帮助测试 Firestore 安全规则。集合和字段名称区分大小写。请使用集合和字段的小写名称来防止 Web 应用在将查询发送到 Firestore 时失败。

  1. 在 Google Cloud 控制台中转到 Firestore 页面
  2. 点击启动集合
  3. 集合 ID 字段中,输入 customers
  4. 使用以下信息创建文档:

    文档 IDbob@example.com

    字段名称 字段类型 字段值
    name 字符串 Bob
    公司 字符串
    
    ExampleOrganization
  5. 点击 保存并继续添加

  6. 点击清除字段值

  7. 请输入以下信息:

  8. 文档 ID:您的电子邮件地址

    字段名称 字段类型 字段值
    name 字符串 您的姓名
    公司 字符串 您的公司名称
  9. 点击保存

创建 Firestore 安全规则

可以使用用户身份验证元数据、来自传入查询的数据以及数据库中的现有数据来评估 Firestore 安全规则。

在此过程中,您将基于登录用户的电子邮件地址和提供商名称为 Firestore 创建安全规则。

使用 Firebase 控制台来管理 Firestore 安全规则。

  1. 打开 Firebase 控制台,然后点击您的项目。
  2. 点击屏幕左侧的 Firestore
  3. 在 Firestore 页面上,点击规则标签页。
  4. 如果您选择使用其 Firestore 数据库包含安全规则的现有项目,并且打算在完成本文档中的过程时清理该项目,请记下现有的安全规则。
  5. 将现有规则替换为以下规则:

    rules_version = '2';
    service cloud.firestore {
     match /databases/{database}/documents {
      match /customers/{customerID} {
      allow read:
       if request.auth.uid != null
         && request.auth.token.firebase.sign_in_provider == "google.com"
         && request.auth.token.email == customerID
        }
      }
    }
    

    该规则为 customers 集合授予只读权限。该规则将验证您是否已通过在 Identity Platform 中配置的 Google 登录提供方登录。它还确保您只能检索具有与电子邮件地址匹配的客户 ID 的文档。

  6. 点击发布

配置测试环境

要测试您的 Firestore 安全规则,请先创建一个要求用户登录的 Web 应用。GitHub 上有该 Web 应用,并可下载到 Cloud Shell 环境中,您可以在其中测试该应用。用户登录后,Web 应用将从 Firestore 中读取文档并显示其内容。

配置 Web 应用

  1. 转到“Identity Platform 提供商”页面。

    转到“Identity Platform 提供商”页面

  2. 在页面的右侧,点击应用设置详情
  3. apiKeyauthDomain 旁边列出的值复制到剪贴板,然后点击关闭
  4. 点击页面顶部的 Google Cloud,然后从项目信息卡中复制项目 ID
  5. 点击在 Cloud Shell 中打开以打开 Cloud Shell,克隆 GitHub 代码库,然后打开 config.js 文件。当出现“在 Cloud Shell 中打开”对话框时,点击确认

    在 Cloud Shell 中打开

  6. config.js 文件中,将占位符 [API_KEY][AUTH_DOMAIN][PROJECT_ID] 替换为您在上一步中复制的值。当代码向 Identity Platform 发送请求时,代码会将这些值注入到网址和消息正文中。

注册自定义身份验证处理程序

用户访问您的 Web 应用时,他们将被重定向为使用 Google 作为身份提供商登录。用户成功登录 Google 后,Google 会将带有用户令牌的重定向响应 (302) 返回给身份验证处理程序,如架构图中所示。在 OAuth 2.0 中,您必须事先在提供商的配置中注册每个重定向网址,以防止提供商将令牌发送到未知的目的地。OAuth 2.0 网站上的重定向网址注册页说明了此限制的原因。

在此步骤中,您将更新已获授权的重定向网址列表,以信任本文档中使用的身份验证处理程序。

网址列表是 Google OAuth 2.0 客户端配置的一部分,该页面是您配置 Identity Platform 时复制客户端 ID 和客户端密钥的页面。

在此架构中,您使用两种不同的身份验证处理程序:

  • Identity Platform 托管的身份验证处理程序。
  • Web 应用托管的自定义身份验证处理程序。

可通过以下端点访问 Identity Platform 托管的身份验证处理程序,该端点由 Google 管理:https://[YOUR_PROJECT_ID].firebaseapp.com/__/auth/handler

要使用此处理程序,您无需在 Google OAuth 2.0 的客户端设置中更新授权的网址列表。您在本文档中前面启用 Identity Platform 后,它会自动将网址添加到已获授权的网址列表中。

如果使用 Identity Platform Client SDK,则 SDK 使用此内置身份验证处理程序。Identity Platform 的身份验证处理程序要求您的 Web 应用使用此 SDK,因为 SDK 与处理程序交换状态对象。例如,在用户成功登录到 Identity Platform 之后,SDK 会通知处理程序将用户重定向到何处。

对于由 Web 应用托管的自定义身份验证处理程序,当您直接从 JavaScript 使用 Identity Platform REST API 时,建议您实现并托管自己的身份验证处理程序,而不是 SDK。

本文档介绍了一个示例身份验证处理程序,该处理程序在从 Google 收到用户令牌时管理 Identity Platform 的登录过程。您必须将自定义处理程序的网址添加到 Google OAuth 2.0 客户端设置中的“已获授权的网址”列表中。

在本文档中,您将从 Cloud Shell 运行 Web 应用。启动 Web 应用后,找到您的 Cloud Shell 实例的主机名并相应地更新提供商的配置。

  1. 从 Cloud Shell 运行 Web 应用:

    npm install
    node app.js
    
  2. 等待以下输出显示:Example app listening on port 8080!

  3. 点击 Web 预览图标,然后点击在端口 8080 上预览。等待新标签页显示网页,然后将值复制到身份验证处理程序网址(适用于 Google OAuth 2.0 客户端)下。

  4. 转到凭据页面。
    转到“凭据”页面

  5. 凭据页面上,点击Web 客户端(由 Google 服务自动创建)

  6. 已获授权的重定向 URI 页面上,点击添加 URI,然后粘贴您先前复制的网址。

  7. 点击保存,并使 Web 应用继续运行。

向 Web 应用网域授权

使用 Identity Platform 身份验证处理程序时,该处理程序会将您以及用户信息和令牌重定向回 Web 应用。为防止将您的信息发送到未经授权的网域,您需要对运行 Web 应用的网域进行授权。

  1. 返回到该 Web 应用的默认网页,然后将值复制到“主机名(适用于 Identity Platform 已获授权的网域)”下。
  2. 转到 Identity Platform“设置”页面。

    转到 Identity Platform“设置”页面

  3. 点击安全 标签页,然后点击添加网域
  4. 粘贴您复制的网域,点击添加,然后点击保存
  5. 让 Web 应用在 Cloud Shell 中运行。它将成为下一个任务的必备条件。

使用 Google 身份登录到 Identity Platform

下图扩展了本指南开头部分的概要架构图。此图表涉及本文档介绍的身份验证过程的详细信息,以显示事件的时间顺序流程。事件以用户点击登录按钮开始,以 Web 应用结尾,使用用户的身份从 Firestore 检索数据:

概要架构;

  1. 您的 Web 应用的用户在 Web 应用中点击使用 Google 账号登录
  2. Web 应用向 Identity Platform 查询所选身份提供商(本例中为 Google)的登录网址。
  3. Web 应用会将用户重定向至身份提供商的登录页面,以及一个指向身份验证处理程序的回调网址。
  4. 在提供商登录页面上,用户输入其凭据并同意 Web 应用所请求的授权。
  5. 用户成功登录后,该提供商会生成一个令牌,并向之前提供的回调网址发送重定向。
  6. 身份验证处理程序会收到 Google 签发的令牌,并将其发送到 Identity Platform 以让用户登录。Identity Platform 会验证令牌,让用户登录,并返回 Identity Platform 颁发的 ID 令牌和刷新令牌以及该用户的信息。Identity Platform 让用户首次登录时,会在其数据库中创建一份匹配的用户个人资料。Google 提供的账号信息用于填充用户个人资料。
  7. 您登录 Identity Platform 后,处理程序会将您以及它从 Identity Platform 获取的新令牌重定向回 Web 应用。
  8. 为了向 Firestore 发送请求,Web 应用会将用户的 ID 令牌附加到每个 Firestore 请求中。Firestore 安全规则规定,Firestore 将没有 ID 令牌的任何请求视为匿名请求,因此请求会遭拒。
  9. Identity Platform 签发的 ID 令牌会在一小时后过期。如果 ID 令牌过期,Web 应用将使用缓存的刷新令牌从 Identity Platform 中检索新的 ID 令牌。

示例 Web 应用演示了如何通过两种方法与 Identity Platform 和 Firestore 互动。

第一种方法是使用 Identity Platform REST API:

  • 此方法使用自定义代码,该代码使用 JavaScript 调用 Identity Platform REST API。
  • API 调用在 site/identity-platform-auth-helper.js 文件中实现。身份验证处理程序在 views/auth-handler.ejs 文件中实现。
  • 帮助程序和处理程序交换状态对象,以便在您成功登录后将您重定向回 Web 应用。

第二种方法是使用 Identity Platform Client SDK:

  • 使用此技术,SDK 可处理登录过程。
  • SDK 实现所有必需的 API 调用,并向开发者公开了一组函数,以控制要启动的登录过程。

使用 Identity Platform REST API 登录

有两个主要 API 调用可用于控制将 Google 作为身份提供商的登录过程。

用于控制 Google 作为提供商的登录过程的两个主要 API 调用

  • 获取提供商网址和标识符。accounts.createAuthUri 方法返回给定身份提供商的授权网址。然后,Web 应用会读取返回的授权网址,以使用所选身份提供商(例如 Google)启动登录过程。

    以下代码段展示了如何调用此 API:

    IdentityPlatformAuthHelper.prototype.createAuthUri = function(providerId, tenantId) {
      // https://cloud.google.com/identity-platform/docs/reference/rest/v1/accounts/createAuthUri
      const createAuthUriUrl = `${this.identityPlatformBaseUrl}/accounts:createAuthUri?key=${config.apiKey}`;
      const request = {
        'providerId' : providerId,
        'tenantId' : tenantId,
        'continueUri' : this.authHandlerUrl,
      };
    
      return fetch(
          createAuthUriUrl,
          {
            contentType: 'application/json',
            method: 'POST',
            body: JSON.stringify(request)
          }
        )
      .then(response => response.json())
      .then(data => {
        return {
          "authUri" : data.authUri,
          "sessionId" : data.sessionId
        };
      })
      .catch(error => {
        console.error(error);
      });
    };
  • 使用 Google 签发的令牌登录到 Identity Platform。accounts.signInWithIdp 方法使用身份提供商提供的授权响应让用户登录 Identity Platform。此 API 使用 Identity Platform 颁发的新令牌响应此请求。Web 应用从身份提供商收到成功的授权响应后,便会调用此 API。以下代码段展示了如何调用此 API:

    IdentityPlatformAuthHelper.prototype.signInWithIdp = function(data) {
      authState = this.getAuthState();
      this.authHandlerUrl = authState.authHandlerUrl;
    
      // https://cloud.google.com/identity-platform/docs/reference/rest/v1/accounts/signInWithIdp
      const signInWithIdpUrl = `${this.identityPlatformBaseUrl}/accounts:signInWithIdp?key=${config.apiKey}`;
    
      const request = {
          'requestUri' : this.authHandlerUrl,
          'sessionId' : authState.sessionId,
          'returnRefreshToken' : true,
          'returnSecureToken' : true,
          'tenantId' : authState.tenantId
        };
    
      if (authState.providerId == 'google.com' || authState.providerId.startsWith('saml.')) {
        request.postBody = `${data}&providerId=${authState.providerId}`;
      } else {
        throw new Error('This sample script only supports the google.com and SAML providers for Identity Platform');
      }
    
      fetch(
          signInWithIdpUrl,
          {
            contentType: 'application/json',
            method: 'POST',
            body: JSON.stringify(request)
          }
        )
      .then(response => response.json())
      .then(data => {
        this.user = data;
        this.signedInHandler(this.user);
      })
      .catch(error => {
        console.error(error);
      });
    }

    postBody 字段的值具有不同的格式,具体取决于所选身份提供商及其使用的授权协议。该代码使用 OpenID Connect (OIDC) 令牌处理 Google 身份提供商,而使用提供的 SAML 响应处理基于 SAML 的身份提供商。如果您使用的是其他类型的授权令牌(例如 OAuth 2.0 或 OAuth 1.0 访问令牌),请参阅相应提供商的 API 文档

用户登录您的 Web 应用后,Web 应用可以将查询发送到 Firestore。

Web 应用向 Firestore 发送查询时的事件流

在代码触发向 Firestore REST API 发送请求之前,代码会将 Identity Platform 签发的 ID 令牌添加到请求中。以下代码段展示了如何创建请求:

function showCustomerInformation(userEmail) {
  $('#customer-information').show();
  $('#output').empty();

  const idTokenPromise = authHelper.getIdToken();
  const firestoreEndpoint = 'https://firestore.googleapis.com/v1';
  const defaultDbPath = `projects/${config.projectId}/databases/(default)/documents`;
  const collectionId = 'customers';

  // Call Firestore via its REST API and authenticate with the user's ID token
  idTokenPromise
  .then(idToken => {
    console.log(`JWT Token: ${idToken}`);
    return fetch(
      `${firestoreEndpoint}/${defaultDbPath}/${collectionId}/${userEmail}`,
      {
        headers: {
          'Authorization': 'Bearer ' + idToken
        },
        contentType: 'application/json',
        method: 'GET'
      })
  })
  .then(response => response.json())
  .then(data => {
      if (data.error) {
        throw data.error.message;
      }
      var fields = data.fields;
      $('#output').append($('<p>').text(`Id: ${userEmail}`));
      $('#output').append($('<p>').text(`Name: ${fields.name.stringValue}`));
      $('#output').append($('<p>').text(`Company: ${fields.company.stringValue}`));
      $('#output').append($('<p>').text(`Doc path: ${data.name}`));
      $('#output').append($('<p>').text(`Doc URL: ${firestoreEndpoint}/${data.name}`));
  })
  .catch(error => {
    console.error(error);
    $('#output').text("Error: " + JSON.stringify(error));
  });
}

IdentityPlatformAuthHelper.getIdToken() 函数通过检索浏览器已缓存的令牌以 JSON Web 令牌 (JWT) 的形式返回有效的 ID 令牌。如果令牌已过期,则该函数会通过调用 Identity Platform API 将刷新令牌交换为新的 ID 令牌,从而续订令牌。

以下代码段展示了如何检查现有 ID 令牌是仍然有效还是已过期,以及如何通过调用 Identity Platform 来刷新令牌:

IdentityPlatformAuthHelper.prototype.getIdToken = function() {
  const token = this.jwtDecode(this.user.idToken);

  // If exp has passed, refresh the token
  if (Date.now() > token.payload.exp * 1000) {
    return this.refreshToken(this.user.refreshToken);
  }
  return Promise.resolve(this.user.idToken);
}

IdentityPlatformAuthHelper.prototype.jwtDecode = function(t) {
  const token = {};
  token.raw = t;
  token.header = JSON.parse(window.atob(t.split('.')[0]));
  token.payload = JSON.parse(window.atob(t.split('.')[1]));
  return token;
}

IdentityPlatformAuthHelper.prototype.refreshToken = function(refreshToken) {
  // https://cloud.google.com/identity-platform/docs/reference/rest/client#section-refresh-token
  const tokenUrl = `https://securetoken.googleapis.com/v1/token?key=${config.apiKey}`;
  const requestBody = new URLSearchParams(`grant_type=refresh_token&refresh_token=${refreshToken}`);

  return fetch(
      tokenUrl,
      {
        contentType: 'application/x-www-form-urlencoded',
        method: 'POST',
        body: requestBody
      }
    )
  .then(response => response.json())
  .then(data => {
    this.user.idToken = data.id_token;
    this.user.refreshToken = data.refresh_token;
    return this.user.idToken;
  })
  .catch(error => {
    console.error(error);
  });
}

按照以下步骤使用您的 Google 身份登录 Identity Platform:

  1. 返回到显示 Web 应用的默认页面的标签页。如果您已经关闭该标签页,请返回 Cloud Shell 页面,点击 Web 预览,然后点击在端口 8080 上预览。等待新标签页显示网页。
  2. 在浏览器中更改地址,以显示 customer-info-with-api.html 页面。新网址的格式如下:https://random_prefix-devshell.appspot.com/customer-info-with-api.html
  3. 点击使用 Google 账号登录,然后使用您的凭据登录。登录后,系统会显示一个包含您的电子邮件地址的文本框。

    如果您想解码 JWT 以查看 Identity Platform 和 Google 提供的用户信息,请执行以下操作。或者,请跳至下一步。

    用户信息包含在 JWT 的载荷(第二)部分,且采用 base64 编码。如需解码 JWT 的第二部分,并使用 jq 打印 JSON 文件中的信息,请在 Cloud Shell 中运行以下命令:

    token=[PASTE_JWT_STRING_HERE]
    echo $token | awk '{split($0, a, "."); print a[2]; }' | base64 -d | jq
    

    继续查询 Firestore 以获取其他文档。

  4. 点击获取客户信息。系统会显示您在 Cloud Firestore 数据库中输入的姓名和公司名称。

  5. 将电子邮件地址更改为 bob@example.com,然后点击使用 Google 账号登录。此次的响应是以下错误消息:

    Error: "Missing or insufficient permissions." The security rule you added to Firestore limits your access to documents with an ID that matches your email address, as it appears in the token that Identity Platform created.

  6. 不要关闭网页。您将在下一步骤中用到它。

使用 Identity Platform Client SDK 登录

您可以使用 Identity Platform Client SDK(而不是以手动方式)编写向 Identity Platform 发出的请求。该 SDK 会管理登录过程,并提供相应功能来控制登录过程,例如使用哪个提供商,或是使用重定向还是弹出式窗口。

要使用 Identity Platform 客户端 SDK,您需要在 HTML 页面中添加多个脚本文件。以下代码段显示了您需要的脚本:

<script src="https://www.gstatic.com/firebasejs/7.14.4/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.14.4/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.14.4/firebase-firestore.js"></script>

以下代码段展示了如何使用 SDK 通过 Google 提供商进行登录。

$('#sign-in').click((event) => {
  provider = new firebase.auth.GoogleAuthProvider();
  //firebase.auth().signInWithPopup(provider)
  firebase.auth().signInWithRedirect(provider)
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
    console.error(error);
  });
});

firebase.auth().onAuthStateChanged(function(user) {
  if (user) {
    $('#logged-out').hide();
    /* If the provider gives a display name, use the name for the
    personal welcome message. Otherwise, use the user's email. */
    const welcomeName = user.displayName ? user.displayName : user.email;
    console.log(firebase.auth().currentUser);
    $('#user').text(welcomeName);
    $('#logged-in').show();
    $('#email').val(firebase.auth().currentUser.email);
  } else {
    $('#logged-in').hide();
    $('#logged-out').show();
    $('#email').val('');
  }
  $('#customer-information').hide();
});

$('#sign-out').click(function(event) {
  firebase.auth().signOut().then(function() {
    console.log('Sign out successful');
  }, function(error) {
    console.error(error);
  });
});

firebase.auth().signInWithRedirect() 函数通过将用户重定向到提供方的登录页面,在同一浏览器窗口中启动登录过程。使用 GoogleAuthProvider 会指示该函数启动 Google 登录过程。

您可以通过调用 signInWithPopup 函数将重定向行为替换为弹出行为。

要使用其他身份验证提供商,请添加任何实现 firebase.auth.AuthProvider 接口的类型。为确保您纳入所有必需的参数,请遵循您所选提供商的文档。

firebase.auth().onAuthStateChanged 函数是一个在登录和退出时触发的观察者。登录时,Web 应用代码使用从用户对象中获取的信息填充网页并隐藏登录按钮。退出时,代码会清除网页并再次显示登录按钮。

Identity Platform Client SDK 可与 Firestore SDK 集成。在每次查询时,Firestore SDK 都会通过从 Identity Platform Client SDK 中提取来附加一个有效 ID 令牌。Identity Platform Client SDK 负责在到期时刷新 ID 令牌。

以下代码段展示了如何使用 Firestore SDK 查询 Firestore:

function showCustomerInformation(userEmail) {
  $('#customer-information').show();
  $('#output').empty();

  const db = firebase.firestore();
  const collectionId = 'customers';

  query = db.collection(collectionId).doc(userEmail).get();
  query.then((doc) => {
    var fields = doc.data();
    $('#output').append($('<p>').text(`Id: ${doc.id}`));
    $('#output').append($('<p>').text(`Name: ${fields.name}`));
    $('#output').append($('<p>').text(`Company: ${fields.company}`));
  }).catch((error) => {
    console.error(error);
    $('#output').text("Error: " + error.toString());
  });
}

请注意,您无需编写代码即可将 ID 令牌添加到查询中。Firestore SDK 和 Identity Platform 客户端 SDK 会处理身份验证过程。

执行以下步骤,使用您的 Google 身份登录 Identity Platform,然后查询 Firestore:

  1. 如果您已关闭相关的 Web 应用标签页,请返回 Cloud Shell 页面、点击 Web 预览,然后点击在端口 8080 上预览。等待新标签页显示网页。
  2. 在浏览器中更改地址,以显示 customer-info-with-sdk.html 页面。新网址的格式如下:https://random_prefix-devshell.appspot.com/customer-info-with-sdk.html
  3. 点击使用 Google 账号登录,然后使用您的凭据登录。登录后,系统会显示一个包含您的电子邮件地址的文本框。
  4. 点击获取客户信息。系统会显示您在 Cloud Firestore 数据库中输入的姓名和公司名称。
  5. 将电子邮件地址更改为 bob@example.com,然后点击使用 Google 账号登录。这次的响应是一条错误消息:

    Error: FirebaseError: [code=permission-denied]: Missing or insufficient Permissions.

    如需详细了解如何使用 JavaScript 查询 Firestore,请参阅 Firestore 文档

排查 Web 应用中的问题

如果您在运行 Web 应用时遇到以下问题,这些问题排查步骤可能会派上用场。

浏览 Web 应用时出错

错误 备注
您会看到以下任一错误消息:

Error:Could not connect to Cloud Shell on port 8080

Error:No active Cloud Shell
确保 Cloud Shell 已打开,并且 Web 应用正在运行,如注册自定义身份验证处理程序中所述。如果您打开新的 Cloud Shell 会话,请先将工作目录更改为 Web 应用的目录,然后再运行 Web 应用:

cd "$HOME"/cloudshell_open/securing-cloud-firestore-with-identity-platform

运行 Web 应用后,请检查以下输出是否显示:

Example app listening on port 8080!

登录 Google 时出错

错误 备注
点击使用 Google 账号登录。时没有任何反应。您会看到以下错误代码:

Cannot GET /undefined
确保您已在 config.js 文件中设置 apiKeyauthDomain 变量,如配置 Web 应用中所述。
在 Google 登录页面上,您将看到以下错误消息:

Authorization Error - Error 400: redirect_uri_mismatch
发送到 Google 的重定向网址与授权给 OAuth 客户端的网址的列表不匹配。确保您已按照注册自定义身份验证处理程序中所述配置了已获授权的重定向 URI。
使用 Identity Platform SDK 登录 Google 时,您会看到以下错误消息:

This domain (***) is not authorized to run this operation. Add it to the OAuth redirect domains list in the Firebase console -> Auth section -> Sign-in method tab
如果 Cloud Shell 所用的网域不在 Identity Platform 允许的网域列表中,就可能会出现此错误。要验证网域是否已添加到网域列表中,请按照向 Web 应用网域授权中的步骤操作。

获取客户信息时出错

错误 备注
您会看到以下任一错误消息:

Error: FirebaseError: [code=permission-denied]: Missing or insufficient permissions

Error: Missing or insufficient permissions
Firestore 安全规则可能无法验证发送到 Firestore 的令牌。确保正确配置 Firestore 安全规则,如创建 Firestore 安全规则中所述。
您会看到以下任何错误消息:

Error: FirebaseError: [code=unavailable]: Failed to get document because the client is offline

Error: "The project *** does not exist or it does not contain an active Datastore or Firestore database

Error: "Project id [PROJECT_ID] is malformed: it either contains invalid characters or is too long
请务必按照配置 Web 应用中的说明在 config.js 文件中设置 projectId 属性。

清除数据

为避免因本教程中使用的资源导致您的 Google Cloud 账号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。

  1. 在 Google Cloud 控制台中,进入管理资源页面。

    转到“管理资源”

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

删除各个资源

  1. 在 Cloud Shell 中,按 Ctrl+C 停止 Web 应用。
  2. 删除克隆的代码库目录:

    rm -rf "$HOME"/cloudshell_open/securing-cloud-firestore-with-identity-platform
    
  3. 进入 Identity Platform 设置页面。

    转到 Identity Platform“设置”页面

  4. 点击安全标签页,删除之前添加的网域,然后点击保存

  5. 前往身份提供商页面。

    前往“身份提供商”页面

  6. 删除您之前添加的 Google 提供商。

  7. 前往 API 和服务,然后进入凭据页面。

    转到“凭据”页面

  8. 在“凭据”页面上,点击 Web 客户端(由 Google 服务自动创建)

  9. 已获授权的重定向 URI 页面上,删除之前粘贴的网址,然后点击保存

  10. 打开 Firebase 控制台,在控制台导航窗格中,点击您的项目。

  11. 从左侧的开发菜单中选择数据库选项。

  12. 数据库页面上,点击规则标签页。

  13. 使用您在开始本文档之前就有的安全规则覆盖当前的安全规则,然后点击发布

  14. 在 Google Cloud 控制台中转到 Firestore 页面

  15. 点击客户集合左侧的菜单,然后点击删除集合

  16. 在删除确认弹出窗口中,在集合 ID 字段中输入 customers,然后点击删除

后续步骤

探索有关 Google Cloud 的参考架构、图表和最佳做法。查看我们的 Cloud Architecture Center