使用 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。

如果是其他 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. 按一下「新增供應商」

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

  4. 設定「網頁用戶端 ID」和「網頁用戶端密鑰」

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

      前往「憑證」

    2. 使用現有的 OAuth 2.0 用戶端或建立新的用戶端。將 Client IDClient secret 設定為「Web Client ID」(網頁用戶端 ID) 和「Web Client Secret」(網頁用戶端密鑰)。將 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頁面。

    前往 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 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: #f8f9fa;
}

重新部署 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. 視需要指定修訂版本的進階設定,或按一下「變數與密鑰」分頁標籤,新增環境變數。

  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 布林值 (01),表示是否要記錄所有網路要求錯誤和詳細資料。系統不會記錄私密資料。預設為停用 (0)。
UI_CONFIG 包含登入頁面 JSON 設定的字串。使用這個變數而非 /admin 面板,可避免在存取設定時讀取及寫入 Cloud Storage。系統會忽略無效的設定。在設定這個變數前,使用 /admin 面板驗證 JSON,有助於減少語法錯誤。
GCS_BUCKET_NAME 這個字串會覆寫預設的 Cloud Storage 值區,用來儲存 JSON 設定。檔案名稱為 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頁面。

      前往 IAP 頁面

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

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

    4. 選取「使用現有的託管登入頁面」,然後從下拉式選單中選擇網域。

    5. 按一下 [儲存]

為多個 IAP 資源使用一個登入頁面

您可以使用同一個登入頁面保護多個 IAP 資源。這樣一來,您就不必費心管理多個設定。

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

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

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

  3. 重新部署 Cloud Run 服務。

疑難排解

瀏覽器中的第三方 Cookie 和儲存空間分區

如果瀏覽器停用第三方 Cookie 或實作儲存空間分割,登入頁面或管理頁面可能無法正常運作。如要解決這個問題,請按照下列步驟操作:

  1. 重新部署登入頁面,使用最新版本 1.0.1。

  2. 如果您使用「自訂登入頁面」功能,請務必將 authDomain 設為 IAP 建立的 LOGIN_URL。或者,如果 signInFlow 設為 popup,您可以將 authDomain 設為 PROJECT_ID.firebaseapp.com

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

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

後續步驟