使用 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 存取權杖。如要取得這類 ID,最簡單的方法是將 Google 啟用為 Identity Platform 的供應商。如果您的應用程式已使用 Google 做為身分識別提供者,可以略過這個部分。

  1. 前往Google Cloud 控制台的「Identity Platform Providers」頁面。

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

  2. 按一下「新增提供者」。

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

  4. 設定「Web Client ID」(網頁用戶端 ID) 和「Web Client Secret」(網頁用戶端密鑰):

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

      前往「憑證」

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

  5. 按一下這兩個頁面中的「儲存」。

登入管理面板

Cloud Run 代管登入頁面的 JSON 設定,位於 LOGIN_URL/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 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. 您可以視需要指定修訂版本的進階設定,或按一下「變數與密鑰」分頁標籤,新增環境變數。

  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. 在側邊面板中,選取「登入網址」欄位旁的「編輯」圖示。

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

    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"
            ...
          }
        }
        ...
      }
    }
    

後續步驟