כדי להשתמש בזהויות חיצוניות עם שרת Proxy לאימות זהויות (IAP), האפליקציה צריכה דף כניסה. שרת IAP יפנה את המשתמשים לדף הזה כדי לאמת אותם לפני שהם יוכלו לגשת למשאבים מאובטחים.
במאמר הזה נסביר איך ליצור דף אימות באמצעות FirebaseUI, ספריית JavaScript בקוד פתוח. FirebaseUI מספק רכיבים שאפשר להתאים אישית, שעוזרים לצמצם את כמות קוד ה-boilerplate, ומטפל בתהליכי הכניסה של משתמשים באמצעות מגוון רחב של ספקי זהויות.
כדי להתחיל מהר יותר, אפשר לאפשר ל-IAP לארח את ממשק המשתמש בשבילכם. כך אפשר לנסות זהויות חיצוניות בלי לכתוב קוד נוסף. במקרים מורכבים יותר, אפשר גם לבנות דף כניסה משלכם מאפס. האפשרות הזו מורכבת יותר, אבל היא מאפשרת לכם שליטה מלאה בתהליך האימות ובחוויית המשתמש.
לפני שמתחילים
במהלך ההגדרה, מפעילים זהויות חיצוניות ובוחרים באפשרות I'll provide my own UI (אני אשתמש בממשק משתמש משלי).
התקנת הספריות
מתקינים את הספריות gcip-iap, firebase ו-firebaseui. מודול gcip-iap מבצע הפשטה של התקשורת בין האפליקציה, IAP ו-Identity Platform. הספריות firebase ו-firebaseui מספקות את אבני הבניין לממשק המשתמש של האימות.
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 ומעלה, אתם צריכים לבצע את הפעולות הבאות:
- מעדכנים את הגרסאות של
firebaseו-firebaseuiבקובץpackage.jsonלגרסה 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. דוגמאות להגדרת שדות אחרים מופיעות בקטע הקוד שלמעלה או במסמכי התיעוד של FirebaseUI ב-GitHub.
הגדרת מפתח ה-API
הגדרה טיפוסית מתחילה עם מפתח API לפרויקט:
// The project configuration.
const configs = {
// Configuration for API_KEY.
API_KEY: {
// Config goes here
}
}
ברוב המקרים, צריך לציין רק מפתח API אחד. עם זאת, אם רוצים להשתמש בכתובת URL אחת לאימות בכמה פרויקטים, אפשר לכלול כמה מפתחות API:
const configs = {
API_KEY1: {
// Config goes here
},
API_KEY2: {
// Config goes here
},
}
קבלת דומיין האימות
מגדירים את השדה authdomain לדומיין שהוקצה כדי לאפשר כניסה מאוחדת. אפשר לאחזר את השדה הזה מדף Identity Platform במסוף Google Cloud .
ציון מזהים של דיירים
ההגדרה מחייבת רשימה של דיירים וספקים שהמשתמשים יכולים לבצע אימות מולם.
כל דייר מזוהה באמצעות המזהה שלו. אם אתם משתמשים באימות ברמת הפרויקט (ללא דיירים), אתם יכולים להשתמש במזהה המיוחד _ כמפתח 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
}
}
}
}
אפשר גם לציין הגדרת דייר עם תו כללי לחיפוש באמצעות האופרטור *.
הדייר הזה משמש כברירת מחדל אם לא נמצא מזהה תואם.
הגדרת ספקי דיירים
לכל דייר יש ספקים משלו, שמצוינים בשדה signInOptions:
tenantId1: {
signInOptions: [
// Options go here
]
}
במאמר הגדרת ספקי כניסה בתיעוד של FirebaseUI מוסבר איך להגדיר ספקים.
בנוסף לשלבים שמפורטים במסמכי התיעוד של FirebaseUI, יש כמה שדות שספציפיים לרכישות מתוך האפליקציה, והם תלויים במצב הבחירה של הדייר שתבחרו. מידע נוסף על השדות האלה מופיע בקטע הבא.
בחירת מצב לבחירת דייר (tenant)
המשתמשים יכולים לבחור דייר בשתי דרכים: מצב בחירה קודמת או מצב זיהוי קודם.
במצב אפשרויות, המשתמש מתחיל בבחירת דייר מתוך רשימה, ואז מזין את שם המשתמש והסיסמה שלו. במצב מזהה, המשתמש מזין קודם את כתובת האימייל שלו. לאחר מכן המערכת בוחרת אוטומטית את הדייר הראשון עם ספק זהויות שתואם לדומיין של כתובת האימייל.
כדי להשתמש במצב אפשרויות, מגדירים את displayMode ל-optionFirst. לאחר מכן תצטרכו לספק פרטי הגדרה לכל לחצן של דייר, כולל displayName, buttonColor ו-iconUrl. אפשר גם לספק fullLabel אופציונלי כדי לשנות את כל התווית של הלחצן במקום רק את השם המוצג.
הנה דוגמה לדייר שהוגדר לשימוש במצב אפשרויות:
tenantId1: {
fullLabel: 'ACME Portal',
displayName: 'ACME',
buttonColor: '#2F2F2F',
iconUrl: '<icon-url-of-sign-in-button>',
// ...
כדי להשתמש במצב מזהה, כל אפשרות כניסה צריכה לציין שדה hd שמציין את הדומיין שהיא תומכת בו. הערך יכול להיות ביטוי רגולרי (למשל /@example\.com$/) או מחרוזת הדומיין (למשל example.com).
הקוד הבא מראה דייר שהוגדר לשימוש במצב מזהה:
tenantId1: {
signInOptions: [
// Email/password sign-in.
{
hd: 'acme.com', // using regex: /@acme\.com$/
// ...
},
הפעלת הפניה אוטומטית מיידית
אם האפליקציה תומכת רק בספק זהויות אחד, הגדרת immediateFederatedRedirect ל-true תדלג על ממשק המשתמש של הכניסה ותפנה את המשתמש ישירות לספק.
הגדרת קריאות חוזרות (callback)
אובייקט ההגדרה מכיל קבוצה של קריאות חוזרות (callback) שמופעלות בנקודות שונות במהלך תהליך האימות. בנוסף, תוכלו להתאים אישית את ממשק המשתמש. אלה ה-hooks שזמינים:
selectTenantUiShown() |
מופעל כשמוצג ממשק המשתמש לבחירת דייר. משתמשים בזה אם רוצים לשנות את ממשק המשתמש עם כותרת או עיצוב בהתאמה אישית. |
signInUiShown(tenantId) |
מופעל כשבוחרים דייר ומוצג ממשק המשתמש שבו המשתמש מזין את פרטי הכניסה שלו. משתמשים בזה אם רוצים לשנות את ממשק המשתמש עם כותרת או עיצוב מותאמים אישית. |
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 כדי לעבד את ממשק המשתמש.
<!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חדשה, מעבירים אליה את ה-handler וקוראים ל-start().const ciapInstance = new ciap.Authentication(handler); ciapInstance.start();
פורסים את האפליקציה ועוברים לדף האימות. יופיע ממשק משתמש לכניסה לחשבון, עם הדיירים והספקים שלכם.
המאמרים הבאים
- איך ניגשים למשאבים שלא שייכים ל-Google באמצעות תוכנה
- מידע נוסף על ניהול סשנים
- איך זה עובד עם זהויות חיצוניות ב-IAP