使用 Cloud Run 托管登录页面

要将外部身份与 Identity-Aware Proxy (IAP) 搭配使用,您的应用需要登录页面。IAP 会将用户重定向到此页面进行身份验证,然后用户才能访问安全资源。

本文介绍如何使用 Cloud Run 部署和自定义预建登录页面。这是开始使用外部身份的最快方式,您无需编写任何代码。

您还可以自行构建登录页面。构建自己的页面比较复杂,但可以加强您对身份验证流程和体验的控制。如需了解详情,请参阅 使用 FirebaseUI 创建登录页面创建自定义登录页面

登录页面限制

如果您的项目启用了 电子邮件枚举保护,则无法使用预建登录页面。

如果您的项目启用了电子邮件枚举保护,请先停用电子邮件枚举保护,然后再继续执行本文中的步骤。

准备工作

  • 启用 Compute Engine API。

    启用 Compute Engine API

  • 启用外部身份,并选择 为我创建登录页面选项。这样,Cloud Run 和 FirebaseUI 就可以为您创建登录页面。

  • 确保 Cloud Run 使用的服务帐号 PROJECT_NUMBER-compute@developer.gserviceaccount.com具有以下 预定义角色

    • roles/identitytoolkit.viewer
    • roles/iap.settingsAdmin
    • roles/compute.networkViewer

为 Identity Platform 提供商设置已获授权的重定向 URI

如果您使用的 Identity Platform 提供商需要登录重定向(重定向到外部 IdP 登录页面),则必须在提供商配置中将托管登录页面的网址添加为已获授权的重定向网址。

例如,对于 Google 提供商,您需要执行以下操作:

  1. 选择受 IAP 保护的应用后,复制登录网址

  2. 在 Google Cloud 控制台中,前往凭据页面。

    进入“凭据”页面

  3. LOGIN_URL/__/auth/handler 添加为应用 OAuth 2.0 客户端的已获授权的重定向 URI 之一。选择与配置 Google 提供商时使用的 OAuth 客户端 ID 相同的 OAuth 客户端 ID。

对于其他 SAML 和 OIDC 提供商,请执行相同的操作,将 LOGIN_URL/__/auth/handler 添加为已获授权的重定向 URI 或 ACS 网址。

测试登录页面

IAP 创建的初始登录页面可以完全正常运行。要进行测试,请按以下步骤操作:

  1. 导航到受 IAP 保护的资源。系统应该会自动将您重定向至登录页面。

  2. 选择用于登录的租户和提供商。如果您没有看到任何租户或提供商,请确保您已使用 Identity Platform 配置了一个租户或提供商。

  3. 使用凭据登录。

系统应该会将您重定向到受保护的资源。

自定义登录页面

您可以使用 JSON 配置文件来自定义登录页面。选项包括:

  • 在登录页面中添加标头和徽标。
  • 指定可用的租户和提供商。
  • 自定义每个租户和提供商按钮的图标和样式。
  • 添加指向应用隐私权政策和服务条款的链接。

以下部分介绍了如何访问和更新 JSON 配置文件。

获取访问令牌

为了管理登录页面,您需要有 Google 访问令牌。获取 Google 访问令牌最简单的方法是让 Google 成为 Identity Platform 的提供商。如果您的应用已经将 Google 用作身份提供商,则您可以跳过本部分。

  1. 在 Google Cloud 控制台中,前往 Identity Platform 提供商 页面。

    前往“Identity Platform 提供商”页面

  2. 点击添加提供商

  3. 从提供商列表中选择 Google

  4. 配置 Web 客户端 IDWeb 客户端密钥

    1. 在 Google Cloud 控制台中,前往凭据页面。

      进入“凭据”页面

    2. 使用现有的 OAuth 2.0 客户端或创建新的客户端。将 Client IDClient secret 配置为 Web 客户端 IDWeb 客户端密钥 。将 LOGIN_URL/__/auth/handler 添加为 OAuth 2.0 客户端的已获授权的重定向 URI 之一。LOGIN_URL 是 IAP 在选择为我创建登录页面 选项后创建的登录网址 。您可以在控制台的 IAP 页面中找到它,方法是选择受 IAP 保护的资源。 Google Cloud

  5. 在这两个页面上点击保存

登录到管理控制台面板

由 Cloud Run 托管的登录页面的 JSON 配置位于 LOGIN_URL/admin 面板中。 以下步骤显示了如何访问面板。请注意,您将需要 Storage Admin (roles/storage.admin) 角色。

  1. 在 Google Cloud 控制台中,前往 IAP 页面。

    前往 IAP 页面

  2. 从列表中选择您的资源。

  3. 启动在信息面板的自定义页面下列出的网址。其格式应为 https://servicename-xyz-uc.a.run.app/admin

  4. 使用您用于配置 IAP 的同一 Google 账号进行登录。此时将显示包含 JSON 配置文件的文本编辑器。

修改配置

登录页面的配置架构基于 FirebaseUI, 并沿用其多项属性。您可以改用 PROJECT_ID.firebaseapp.com,而不是使用 IAP 创建的 LOGIN_URL 作为默认 authDomain。

如果您想使用 PROJECT_ID.firebaseapp.com 作为 authDomain, 请将 signInFlow 更改为 popup,以避免在主要浏览器上出现第三方存储访问问题(请参阅 在阻止第三方访问存储空间的浏览器上使用 signInWithRedirect 的最佳实践)。此外,请按照为 Identity Platform 提供商设置已获授权的重定向 URI 中的说明,将PROJECT_ID.firebaseapp.com/__/auth/handler添加为用户将用于登录的 Identity Platform 提供商的已获授权的重定向 URI 或 ACS 网址之一。

以下代码展示了一个包含三个租户的配置示例:

{
  "AIzaSyC5DtmRUR...": {
    "authDomain": "awesomeco.firebaseapp.com",
    "displayMode": "optionFirst",
    "selectTenantUiTitle": "Awesome Company Portal",
    "selectTenantUiLogo": "https://awesome.com/abcd/logo.png",
    "styleUrl": "https://awesome.com/abcd/overrides/stylesheet.css",
    "tosUrl": "https://awesome.com/abcd/tos.html",
    "privacyPolicyUrl": "https://awesome.com/abcd/privacypolicy.html",
    "tenants": {
      "tenant-a-id": {
        "fullLabel": "Company A Portal",
        "displayName": "Company A",
        "iconUrl": "https://companya.com/img/icon.png",
        "logoUrl": "https://companya.com/img/logo.png",
        "buttonColor": "#007bff",
        "signInFlow": "popup",
        "signInOptions": [
          {
            "provider": "password",
            "requireDisplayName": false,
            "disableSignUp": {
              "status": true,
              "adminEmail": "admin@example.com",
              "helpLink": "https://www.example.com/trouble_signing_in"
            }
          },
          "facebook.com",
          "google.com",
          "microsoft.com",
          {
            "provider": "saml.okta-cicp-app",
            "providerName": "Corp Account",
            "fullLabel": "Employee Corporate Login",
            "buttonColor": "#ff0000",
            "iconUrl": "https://companya.com/abcd/icon-1.png"
          },
          {
            "provider": "oidc.okta-oidc",
            "providerName": "Contractor Account",
            "fullLabel": "Contractor Account Portal",
            "buttonColor": "#00ff00",
            "iconUrl": "https://companya.com/abcd/icon-2.png"
          }
        ],
        "tosUrl": "https://companya.com/abcd/tos.html",
        "privacyPolicyUrl": "https://companya.com/abcd/privacypolicy.html"
      },
      "tenant-b-id": {
        "fullLabel": "Company B Portal",
        "displayName": "Company B",
        "iconUrl": "https://companyb.com/img/icon.png",
        "logoUrl": "https://companyb.com/img/logo.png",
        "buttonColor": "#007bff",
        "immediateFederatedRedirect": true,
        "signInFlow": "popup",
        "signInOptions": [
          {
            "provider": "saml.okta-bla-app",
            "providerName": "Corp Account",
            "buttonColor": "#0000ff",
            "iconUrl": "https://companyb.com/abcd/icon.png"
          }
        ],
        "tosUrl": "https://companyb.com/abcd/tos.html",
        "privacyPolicyUrl": "https://companyb.com/abcd/privacypolicy.html"
      },
      "tenant-c-id": {
        "fullLabel": "Company C Portal",
        "displayName": "Company C",
        "iconUrl": "https://companyc.com/img/icon.png",
        "logoUrl": "https://companyc.com/img/logo.png",
        "buttonColor": "#007bff",
        "immediateFederatedRedirect": true,
        "signInFlow": "popup",
        "signInOptions": [
          {
            "provider": "password",
            "requireDisplayName": false
          },
          {
            "provider": "google.com",
            "scopes": ["scope1", "scope2", "https://example.com/scope3"],
            "loginHintKey": "login_hint",
            "customParameters": {
              "prompt": "consent",
            },
          }
        ],
        "tosUrl": "https://companyc.com/abcd/tos.html",
        "privacyPolicyUrl": "https://companyc.com/abcd/privacypolicy.html",
        "adminRestrictedOperation": {
          "status": true,
          "adminEmail": "admin@example.com",
          "helpLink": "https://www.example.com/trouble_signing_in"
        }
      },
    }
  }
}

如需查看可用属性的完整列表,请参阅参考文档

替换 CSS

您可以使用 styleUrl 属性指定自定义 CSS 文件。此文件中的样式将替换默认的 CSS。该文件必须可使用 HTTPS 进行公开访问(例如,托管在 Cloud Storage 存储分区中)。

以下示例演示了如何替换默认 CSS:

/** Change header title style. */
.heading-center {
  color: #7181a5;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 20px;
  font-weight: bold;
}

/** Use round edged borders for container. */
.main-container {
  border-radius: 5px;
}

/** Change page background color. */
body {
  background-color: #f8f9fa;
}

重新部署 Cloud Run 实例

在某些情况下,您可能希望重新部署托管登录页面的 Cloud Run 实例。示例场景包括:

  • 添加、修改或移除身份提供商
  • 修改租户配置
  • 设置环境变量
  • 将容器映像更新为最新版本

定期更新和重新部署容器映像可确保您获得最新的 Bug 修复和安全补丁程序。您可以在 GitHub 上查看版本之间的更改的列表。

您可以使用 /versionz 端点获取已部署容器的当前版本。例如:

curl 'https://servicename-xyz-uc.a.run.app/versionz'

如需重新部署 Cloud Run 实例,请执行以下操作:

  1. 在 Google Cloud 控制台中,前往 Cloud Run 页面。

    前往 Cloud Run 页面

  2. 选择托管登录页面的实例。

  3. 点击修改和部署新修订版本

  4. (可选)指定修订版本的高级设置,或点击变量和密钥标签页来添加环境变量。

  5. 点击部署

高级选项

以编程方式自定义登录页面

除了使用 /admin 控制台之外,您还可以通过编程方式更新 JSON 配置。

要获取当前配置,请使用 /get_admin_config 端点。例如:

curl -H 'Authorization: Bearer [TOKEN]'
  'https://servicename-xyz-uc.a.run.app/get_admin_config'

要更新配置,请使用 /set_admin_config。例如:

curl -XPOST -H 'Authorization: Bearer [TOKEN]' -H "Content-type: application/json"
  -d '[UPDATED-CONFIG]' 'https://servicename-xyz-uc.a.run.app/set_admin_config'

这两种 REST 调用都需要 https://www.googleapis.com/auth/devstorage.read_write 范围,并且必须将有效的 OAuth 令牌附加到 Authorization 标头。

设置环境变量

您可以在 Cloud Run 实例上设置环境变量,以自定义高级设置。下表列出了可用的变量:

变量 说明
DEBUG_CONSOLE 指示是否记录所有网络请求错误和详细信息的布尔值(01)。系统将不会记录敏感数据。默认情况下,处于停用状态 (0)。
UI_CONFIG 包含登录页面的 JSON 配置的字符串。使用此变量而不是 /admin 面板可以避免在访问配置时对 Cloud Storage 执行读写操作。系统会忽略无效配置。在设置此变量之前使用 /admin 面板验证您的 JSON,有助于最大限度地减少语法错误。
GCS_BUCKET_NAME 字符串,替换用于存储 JSON 配置的默认 Cloud Storage 存储分区。该文件名为 config.json,默认位置为 gcip-iap-bucket-[CLOUD-RUN-SERVICE-NAME]-[PROJECT-NUMBER]
ALLOW_ADMIN 布尔值(01),指示是否允许访问 /admin 配置面板。默认情况下,处于启用状态 (1)。

更新变量后,您需要部署新版 Cloud Run 实例以使更改生效。如需详细了解环境变量,请参阅 Cloud Run 文档

自定义网域

默认情况下,用户会在登录时查看 Cloud Run 实例的网址。要改为指定自定义网域,请执行以下操作:

  1. 按照映射自定义网域中的步骤为 Cloud Run 实例设置自定义网域。

  2. 将 IAP 配置为使用新的身份验证网址:

    1. 在 Google Cloud 控制台中,前往 IAP 页面。

      前往 IAP 页面

    2. 选择受 IAP 保护的资源。

    3. 在侧边栏中,选择登录网址字段旁边的修改图标。

    4. 选择使用现有托管的登录页面,然后从下拉菜单中选择网域。

    5. 点击保存

使用一个登录页面来保护多个 IAP 资源

您可以使用相同的登录页面保护多个 IAP 资源。这样做可以减少与管理多个配置相关的工作。

要重新使用登录页面,请执行以下操作:

  1. 按照本文中的步骤为受 IAP 保护的第一个资源部署身份验证页面。

  2. 为第二个资源启用 IAP。当系统提示您指定登录页面时,请选择我将提供自己的登录页面,然后输入 Cloud Run 服务的网址作为网址

  3. 重新部署 Cloud Run 服务。

问题排查

使用托管登录页面时服务不可用

使用托管登录页面时,您会收到以下错误:

Service unavailable

然后,您将无法访问登录页面。

要解决此错误,请执行以下操作:

  1. 验证默认 Cloud Storage 存储桶的格式是否为 gcip-iap-bucket-CLOUD_RUN_SERVICE_NAME-PROJECT-NUMBER

  2. 验证默认 Cloud Storage 存储桶是否存在,并且是否包含名为 config.json 的文件。

浏览器中的第三方 Cookie 和存储分区

对于停用第三方 Cookie 或实现存储分区的浏览器,登录页面或 admin/ 页面可能无法正常运行。如需解决此问题,请执行以下操作:

  1. 重新部署登录页面以使用 最新版本 1.0.1。

  2. 如果您自定义登录页面, 请确保将 authDomain 设置为 IAP 创建的 LOGIN_URL。或者,如果 signInFlow 设置 为 popup,您可以将 authDomain 设置 PROJECT_ID.firebaseapp.com

    以下代码段显示了自定义登录页面的 authDomain 配置:

    {
      "AIzaSyC5DtmRUR...": {
        "authDomain": "LOGIN_URL",
        ...
      }
    }
    

    以下代码段介绍了 popup 登录页面的 authDomain

    {
      "AIzaSyC5DtmRUR...": {
        "authDomain": "PROJECT_ID.firebaseapp.com",
        "tenants": {
          "tenant-a-id": {
            ...
            "signInFlow": "popup"
            ...
          }
        }
        ...
      }
    }
    

后续步骤