使用 Cloud Run 託管登入頁面

如要搭配使用外部身分和 Identity-Aware Proxy (IAP),應用程式必須有登入頁面。IAP 會將使用者重新導向至這個頁面,以便在他們存取安全資源前進行驗證。

本文說明如何使用 Cloud Run 部署及自訂預先建構的登入頁面。這是開始使用外部身分最快的方式,而且不需要編寫任何程式碼。

您也可以自行建立登入頁面。自行建構頁面難度較高,但可進一步控管驗證流程和使用者體驗。詳情請參閱「使用 FirebaseUI 建立登入頁面」和「建立自訂登入頁面」。

登入頁面限制

如果專案已啟用電子郵件列舉防護功能,就無法使用預先建構的登入頁面。

如果專案已啟用電子郵件列舉防護功能,請先停用電子郵件列舉防護功能,再繼續執行本文件中的程序。

事前準備

  • 啟用 Compute Engine API。

    啟用 Compute Engine API

  • 啟用外部身分,並在設定期間選取「Create a sign-in page for me」選項。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 保護的應用程式後,複製「Login URL」(登入網址)。

  2. 前往 Google Cloud 控制台的「Credentials」(憑證) 頁面。

    前往「憑證」

  3. 將 LOGIN_URL/__/auth/handler 新增為應用程式 OAuth 2.0 用戶端的其中一個授權重新導向 URI。選取設定 Google 供應商時使用的 OAuth 用戶端 ID。

如果是其他 SAML 和 OIDC 供應商,請同樣新增 LOGIN_URL/__/auth/handler 做為授權重新導向 URI 或 ACS 網址。

測試登入頁面

IAP 建立的初始登入頁面功能完整。測試方法如下:

  1. 前往受 IAP 保護的資源。系統應會自動將您重新導向登入頁面。

  2. 選取要用來登入的租戶和提供者。如果畫面上未列出任何租戶或供應商,請確認您已使用 Identity Platform 設定。

  3. 使用憑證登入。

系統應會將您重新導向至受保護的資源。

自訂登入頁面

您可以使用 JSON 設定檔自訂登入頁面。其中一些選項包括:

  • 在登入頁面中加入標頭和標誌。
  • 指定可用的租戶和供應商。
  • 自訂每個租戶和供應商按鈕的圖示和樣式。
  • 新增應用程式隱私權政策和服務條款的連結。

以下各節說明如何存取及更新 JSON 設定檔。

取得存取權杖

如要管理登入頁面,您需要 Google 存取權杖。最簡單的方法是將 Google 啟用為 Identity Platform 的提供者。如果應用程式已使用 Google 做為識別資訊提供者,可以略過本節。

  1. 前往Google Cloud 控制台的「Identity Platform Providers」(Identity Platform 供應商) 頁面。

    前往 Identity Platform 的「Providers」(提供者) 頁面

  2. 按一下「Add a Provider」。

  3. 從供應商清單中選取「Google」。

  4. 設定「Web Client ID」和「Web Client Secret」:

    1. 前往 Google Cloud 控制台的「Credentials」(憑證) 頁面。

      前往「憑證」

    2. 使用現有的 OAuth 2.0 用戶端,或建立新的用戶端。將 Client ID 和 Client secret 設為網頁用戶端 ID 和網頁用戶端密鑰。將 LOGIN_URL/__/auth/handler 新增為 OAuth 2.0 用戶端的其中一個已授權重新導向 URI。LOGIN_URL 是選取「建立登入頁面」選項後,由 IAP 建立的登入網址。在 Google Cloud 控制台的「IAP」頁面選取受 IAP 保護的資源,即可找到這項資訊。

  5. 在兩個頁面中都按一下「儲存」。

登入管理面板

Cloud Run 代管登入頁面的 JSON 設定,位於「LOGIN_URL/admin」面板。下列步驟說明如何存取面板。請注意,您必須具備 Storage 管理員 (roles/storage.admin) 角色。

  1. 前往 Google Cloud 控制台的「IAP」頁面。

    前往 IAP 頁面

  2. 從清單中選取資源。

  3. 啟動資訊面板中「Customize page」(自訂頁面) 下方列出的網址。看起來應該像 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 bucket)。

以下範例示範如何覆寫預設 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: #e6eaf0;
}

重新部署 Cloud Run 服務

在某些情況下,您可能需要重新部署託管登入頁面的 Cloud Run 服務。情境範例包括:

  • 新增、修改或移除識別資訊提供者
  • 修改租戶設定
  • 設定環境變數
  • 將容器映像檔更新至最新版本

定期更新及重新部署容器映像檔,可確保您取得最新的錯誤修正和安全性修補程式。您可以在 GitHub 查看版本之間的變更清單。

您可以使用 /versionz 端點,取得已部署容器的目前版本。例如:

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

如要重新部署 Cloud Run 服務,請按照下列步驟操作:

  1. 前往 Google Cloud 控制台的「Cloud Run」頁面。

    前往「Cloud Run」頁面

  2. 選取託管登入頁面的服務。

  3. 點選「編輯及部署新的修訂版本」。

  4. 您也可以指定修訂版本的進階設定,或按一下「Variables & Secrets」(變數與密鑰) 分頁標籤來新增環境變數。

  5. 點選「Deploy」(部署)。

進階選項

透過程式輔助方式自訂登入頁面

除了使用 /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 布林值 (0 或 1),表示是否要記錄所有網路要求錯誤和詳細資料。系統不會記錄機密資料。預設為停用 (0)。
UI_CONFIG 包含登入頁面 JSON 設定的字串。使用這個變數而非 /admin 面板,可避免在存取設定時讀取及寫入 Cloud Storage。系統會忽略無效的設定。設定這個變數前,請先使用 /admin 面板驗證 JSON,盡可能減少語法錯誤。
GCS_BUCKET_NAME 覆寫預設 Cloud Storage bucket 的字串,用於儲存 JSON 設定。檔案名稱為 config.json,預設位置為 gcip-iap-bucket-[CLOUD-RUN-SERVICE-NAME]-[PROJECT-NUMBER]。
ALLOW_ADMIN 布林值 (0 或 1),指出是否允許存取 /admin 設定面板。預設為啟用 (1)。

更新變數後,您必須部署新版 Cloud Run 服務,變更才會生效。如要進一步瞭解環境變數,請參閱 Cloud Run 說明文件。

自訂網域

根據預設,使用者登入時會看到 Cloud Run 服務的網址。如要改為指定自訂網域,請按照下列步驟操作:

  1. 按照「對應自訂網域」中的步驟,為 Cloud Run 服務設定自訂網域。

  2. 設定 IAP 使用新的驗證網址:

    1. 前往 Google Cloud 控制台的「IAP」頁面。

      前往 IAP 頁面

    2. 選取受 IAP 保護的資源。

    3. 在側邊面板中,選取「Login URL」(登入網址) 欄位旁的「Edit」(編輯) 圖示。

    4. 選取「Use an existing hosted sign-in page」,然後從下拉式選單中選擇網域。

    5. 按一下 [儲存]。

多項 IAP 資源共用一個登入頁面

您可以使用同一個登入頁面保護多項 IAP 資源。這可減少管理多項設定的工作。

如要重複使用登入頁面,請按照下列步驟操作:

  1. 請按照本文中的步驟,為第一個受 IAP 保護的資源部署驗證頁面。

  2. 為第二項資源啟用 IAP。系統提示您指定登入頁面時,請選取「I'll provide my own」(我會自行提供),然後輸入 Cloud Run 服務的網址做為「URL」(網址)。

  3. 重新部署 Cloud Run 服務。

疑難排解

託管登入頁面無法使用服務

使用託管登入頁面時,您會收到下列錯誤訊息:

Service unavailable

因此無法存取登入頁面。

如要解決這項錯誤,請執行下列操作:

  1. 確認預設 Cloud Storage bucket 的格式為 gcip-iap-bucket-CLOUD_RUN_SERVICE_NAME-PROJECT-NUMBER。

  2. 確認預設 Cloud Storage bucket 存在,且包含名為 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"
            ...
          }
        }
        ...
      }
    }
    

後續步驟