如要搭配 Identity-Aware Proxy (IAP) 使用外部身分,應用程式必須有登入頁面。IAP 會將使用者重新導向至這個頁面,以便使用者通過驗證,存取安全資源。
本文說明如何使用開放原始碼的 JavaScript 程式庫 FirebaseUI 建立驗證頁面。FirebaseUI 提供可自訂的元素,有助於減少樣板程式碼,並處理使用者透過各種識別資訊提供者登入的流程。
如要加快上手速度,請讓 IAP 代管 UI。您不必編寫任何額外程式碼,即可試用外部身分。如要處理更進階的案例,您也可以從頭開始建構自己的登入頁面。這個選項較為複雜,但可讓您全面掌控驗證流程和使用者體驗。
事前準備
啟用外部身分,並在設定期間選取「I'll provide my own UI」選項。
安裝程式庫
安裝 gcip-iap、firebase 和 firebaseui 程式庫。gcip-iap 模組會抽象化應用程式、IAP 和 Identity Platform 之間的通訊。firebase 和 firebaseui 程式庫提供驗證 UI 的建構區塊。
npm install firebase --save
npm install firebaseui --save
npm install gcip-iap --save
請注意,gcip-iap 模組無法透過 CDN 使用。
接著,您可以在來源檔案中 import 模組。請使用適用於您 SDK 版本的正確匯入項目:
gcip-iap v0.1.4 或更早版本
// Import firebase modules.
import * as firebase from "firebase/app";
import "firebase/auth";
// Import firebaseui module.
import * as firebaseui from 'firebaseui'
// Import gcip-iap module.
import * as ciap from 'gcip-iap';
gcip-iap v1.0.0 以上版本
自 v1.0.0 版起,gcip-iap 需要 firebase v9 以上的對等依附元件。如要遷移至 gcip-iap v1.0.0 以上版本,請完成下列動作:
- 將
package.json檔案中的firebase和firebaseui版本分別更新為 v9.6.0 以上和 v6.0.0 以上。 - 將
firebase匯入陳述式更新為下列內容:
// Import firebase modules.
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
// Import firebaseui module.
import * as firebaseui from 'firebaseui'
// Import gcip-iap module.
不需要變更其他程式碼。
如需其他安裝選項,包括使用程式庫的本地化版本,請參閱 GitHub 上的操作說明。
設定應用程式
FirebaseUI 會使用設定物件,指定用於驗證的房客和供應商。完整設定可能很長,看起來會像這樣:
// The project configuration.
const configs = {
// Configuration for project identified by API key API_KEY1.
API_KEY1: {
authDomain: 'project-id1.firebaseapp.com',
// Decide whether to ask user for identifier to figure out
// what tenant to select or whether to present all the tenants to select from.
displayMode: 'optionFirst', // Or identifierFirst
// The terms of service URL and privacy policy URL for the page
// where the user select tenant or enter email for tenant/provider
// matching.
tosUrl: 'http://localhost/tos',
privacyPolicyUrl: 'http://localhost/privacypolicy',
callbacks: {
// The callback to trigger when the selection tenant page
// or enter email for tenant matching page is shown.
selectTenantUiShown: () => {
// Show title and additional display info.
},
// The callback to trigger when the sign-in page
// is shown.
signInUiShown: (tenantId) => {
// Show tenant title and additional display info.
},
beforeSignInSuccess: (user) => {
// Do additional processing on user before sign-in is
// complete.
return Promise.resolve(user);
}
},
tenants: {
// Tenant configuration for tenant ID tenantId1.
tenantId1: {
// Full label, display name, button color and icon URL of the
// tenant selection button. Only needed if you are
// using the option first option.
fullLabel: 'ACME Portal',
displayName: 'ACME',
buttonColor: '#2F2F2F',
iconUrl: '<icon-url-of-sign-in-button>',
// Sign-in providers enabled for tenantId1.
signInOptions: [
// Microsoft sign-in.
{
provider: 'microsoft.com',
providerName: 'Microsoft',
buttonColor: '#2F2F2F',
iconUrl: '<icon-url-of-sign-in-button>',
loginHintKey: 'login_hint'
},
// Email/password sign-in.
{
provider: 'password',
// Do not require display name on sign up.
requireDisplayName: false,
disableSignUp: {
// Disable user from signing up with email providers.
status: true,
adminEmail: 'admin@example.com',
helpLink: 'https://www.example.com/trouble_signing_in'
}
},
// SAML provider. (multiple SAML providers can be passed)
{
provider: 'saml.my-provider1',
providerName: 'SAML provider',
fullLabel: 'Employee Login',
buttonColor: '#4666FF',
iconUrl: 'https://www.example.com/photos/my_idp/saml.png'
},
],
// If there is only one sign-in provider eligible for the user,
// whether to show the provider selection page.
immediateFederatedRedirect: true,
signInFlow: 'redirect', // Or popup
// The terms of service URL and privacy policy URL for the sign-in page
// specific to each tenant.
tosUrl: 'http://localhost/tenant1/tos',
privacyPolicyUrl: 'http://localhost/tenant1/privacypolicy'
},
// Tenant configuration for tenant ID tenantId2.
tenantId2: {
fullLabel: 'OCP Portal',
displayName: 'OCP',
buttonColor: '#2F2F2F',
iconUrl: '<icon-url-of-sign-in-button>',
// Tenant2 supports a SAML, OIDC and Email/password sign-in.
signInOptions: [
// Email/password sign-in.
{
provider: firebase.auth.EmailAuthProvider.PROVIDER_ID,
// Do not require display name on sign up.
requireDisplayName: false
},
// SAML provider. (multiple SAML providers can be passed)
{
provider: 'saml.my-provider2',
providerName: 'SAML provider',
fullLabel: 'Contractor Portal',
buttonColor: '#4666FF',
iconUrl: 'https://www.example.com/photos/my_idp/saml.png'
},
// OIDC provider. (multiple OIDC providers can be passed)
{
provider: 'oidc.my-provider1',
providerName: 'OIDC provider',
buttonColor: '#4666FF',
iconUrl: 'https://www.example.com/photos/my_idp/oidc.png'
},
],
},
// Tenant configuration for tenant ID tenantId3.
tenantId3: {
fullLabel: 'Tenant3 Portal',
displayName: 'Tenant3',
buttonColor: '#007bff',
iconUrl: '<icon-url-of-sign-in-button>',
// Tenant3 supports a Google and Email/password sign-in.
signInOptions: [
// Email/password sign-in.
{
provider: firebase.auth.EmailAuthProvider.PROVIDER_ID,
// Do not require display name on sign up.
requireDisplayName: false
},
// Google provider.
{
provider: 'google.com',
scopes: ['scope1', 'scope2', 'https://example.com/scope3'],
loginHintKey: 'login_hint',
customParameters: {
prompt: 'consent',
},
},
],
// Sets the adminRestrictedOperation configuration for providers
// including federated, email/password, email link and phone number.
adminRestrictedOperation: {
status: true,
adminEmail: 'admin@example.com',
helpLink: 'https://www.example.com/trouble_signing_in'
}
},
},
},
};
如要瞭解如何設定 IAP 專屬的某些欄位,請參閱以下各節中的指引。如需設定其他欄位的範例,請參閱上方的程式碼片段,或 GitHub 上的 FirebaseUI 說明文件。
設定 API 金鑰
一般設定會從專案的 API 金鑰開始:
// The project configuration.
const configs = {
// Configuration for API_KEY.
API_KEY: {
// Config goes here
}
}
在大多數情況下,您只需要指定一個 API 金鑰。不過,如要跨多個專案使用單一驗證網址,可以加入多個 API 金鑰:
const configs = {
API_KEY1: {
// Config goes here
},
API_KEY2: {
// Config goes here
},
}
取得驗證網域
將 authdomain 欄位設為已佈建的網域,以利進行同盟登入。您可以在 Google Cloud 控制台的「Identity Platform」頁面中擷取這個欄位。
指定租戶 ID
設定時需要提供使用者可驗證身分的租戶和供應商清單。
每個租戶都有專屬 ID。如果您使用專案層級驗證 (沒有租戶),請改用特殊 _ 識別碼做為 API 金鑰。例如:
const configs = {
// Configuration for project identified by API key API_KEY1.
API_KEY1: {
tenants: {
// Project-level IdPs flow.
_: {
// Tenant config goes here
},
// Single tenant flow.
1036546636501: {
// Tenant config goes here
}
}
}
}
您也可以使用 * 運算子指定萬用字元租戶設定。
如果找不到相符的 ID,系統會使用這個房客做為備用。
設定租戶供應商
每個房客都有自己的供應商,這些供應商會在 signInOptions 欄位中指定:
tenantId1: {
signInOptions: [
// Options go here
]
}
如要瞭解如何設定供應商,請參閱 FirebaseUI 說明文件中的「設定登入供應商」。
除了 FirebaseUI 說明文件列出的步驟,還有幾個 IAP 專屬欄位,取決於您選擇的租戶選取模式。如要進一步瞭解這些欄位,請參閱下一節。
選擇租戶選取模式
使用者可以透過兩種方式選取租戶:選項優先模式或 ID 優先模式。
在選項模式中,使用者首先要從清單中選取租戶,然後輸入使用者名稱和密碼。在 ID 模式中,使用者會先輸入電子郵件地址。系統接著會自動選取第一個身分識別提供者與電子郵件網域相符的租戶。
如要使用選項模式,請將 displayMode 設為 optionFirst。接著,您需要為每個房客的按鈕提供設定資訊,包括 displayName、buttonColor 和 iconUrl。您也可以選擇提供 fullLabel,覆寫整個按鈕標籤,而不只是顯示名稱。
以下是設定為使用選項模式的租戶範例:
tenantId1: {
fullLabel: 'ACME Portal',
displayName: 'ACME',
buttonColor: '#2F2F2F',
iconUrl: '<icon-url-of-sign-in-button>',
// ...
如要使用 ID 模式,每個登入選項都必須指定 hd 欄位,指出支援的網域。可以是規則運算式 (例如 /@example\.com$/),也可以是網域字串 (例如 example.com)。
以下程式碼顯示設定為使用 ID 模式的租戶:
tenantId1: {
signInOptions: [
// Email/password sign-in.
{
hd: 'acme.com', // using regex: /@acme\.com$/
// ...
},
啟用立即重新導向
如果應用程式僅支援單一識別資訊提供者,將 immediateFederatedRedirect 設為 true 會略過登入 UI,並將使用者直接重新導向至該提供者。
設定回呼
設定物件包含一組回呼,會在驗證流程的各個時間點叫用。此外,您也可以自訂 UI。可用的掛鉤如下:
selectTenantUiShown() |
在顯示選取租戶的 UI 時觸發。如要使用自訂標題或主題修改使用者介面,請使用這個方法。 |
signInUiShown(tenantId) |
在選取租戶並顯示使用者輸入憑證的 UI 時觸發。如要使用自訂標題或主題修改 UI,請使用這個屬性。 |
beforeSignInSuccess(user) |
在登入完成前觸發。您可以使用這個方法修改已登入的使用者,然後重新導向至 IAP 資源。 |
以下範例程式碼說明如何實作這些回呼:
callbacks: {
selectTenantUiShown: () => {
// Show info of the IAP resource.
showUiTitle(
'Select your employer to access your Health Benefits');
},
signInUiShown: (tenantId) => {
// Show tenant title and additional display info.
const tenantName = getTenantNameFromId(tenantId);
showUiTitle(`Sign in to access your ${tenantName} Health Benefits`);
},
beforeSignInSuccess: (user) => {
// Do additional processing on user before sign-in is
// complete.
// For example update the user profile.
return user.updateProfile({
photoURL: 'https://example.com/profile/1234/photo.png',
}).then(function() {
// To reflect updated photoURL in the ID token, force token
// refresh.
return user.getIdToken(true);
}).then(function() {
return user;
});
}
}
初始化程式庫
建立設定物件後,請按照下列步驟在驗證頁面上初始化程式庫:
建立 HTML 容器,用於算繪 UI。
<!DOCTYPE html> <html> <head>...</head> <body> <!-- The surrounding HTML is left untouched by FirebaseUI. Your app may use that space for branding, controls and other customizations.--> <h1>Welcome to My Awesome App</h1> <div id="firebaseui-auth-container"></div> </body> </html>建立
FirebaseUiHandler例項,以便在 HTML 容器中算繪,並將您建立的config元素傳遞至該例項。const configs = { // ... } const handler = new firebaseui.auth.FirebaseUiHandler( '#firebaseui-auth-container', configs);建立新的
Authentication例項,將處理常式傳遞至該例項,然後呼叫start()。const ciapInstance = new ciap.Authentication(handler); ciapInstance.start();
部署應用程式,然後前往驗證頁面。系統會顯示登入使用者介面,其中包含您的租戶和提供者。
後續步驟
- 瞭解如何透過程式存取非 Google 資源。
- 瞭解如何管理工作階段。
- 深入瞭解外部身分與 IAP 的搭配運作方式。