Dokumen ini menunjukkan cara membuat halaman autentikasi Anda sendiri menggunakan identitas eksternal dan IAP. Membuat halaman ini sendiri memberi Anda kontrol penuh atas alur autentikasi dan pengalaman pengguna.
Jika tidak perlu menyesuaikan UI sepenuhnya, Anda dapat mengizinkan IAP menghosting halaman login untuk Anda, atau menggunakan FirebaseUI untuk pengalaman yang lebih efisien.
Ringkasan
Untuk membuat halaman autentikasi Anda sendiri, ikuti langkah-langkah berikut:
- Aktifkan identitas eksternal. Pilih Saya akan memberikan opsi UI saya sendiri selama penyiapan.
- Instal library
gcip-iap. Konfigurasikan UI dengan mengimplementasikan antarmuka
AuthenticationHandler.Halaman autentikasi Anda harus menangani skenario berikut:
Pemilihan tenant
Otorisasi pengguna
Login pengguna
Penanganan error
Opsional: Sesuaikan halaman autentikasi Anda dengan fitur tambahan, seperti status progres, halaman logout, dan pemrosesan pengguna.
Menginstal library gcip-iap
Untuk menginstal library gcip-iap, jalankan perintah berikut:
npm install gcip-iap --save
Modul NPM gcip-iap mengabstraksi komunikasi antara aplikasi Anda, IAP, dan Identity Platform. Hal ini memungkinkan Anda menyesuaikan seluruh alur autentikasi tanpa harus mengelola pertukaran yang mendasarinya antara UI dan IAP.
Gunakan impor yang benar untuk versi SDK Anda:
gcip-iap v0.1.4 atau yang lebih lama
// Import Firebase/GCIP dependencies. These are installed on npm install.
import * as firebase from 'firebase/app';
import 'firebase/auth';
// Import GCIP/IAP module.
import * as ciap from 'gcip-iap';
gcip-iap v1.0.0 ke v1.1.0
Mulai versi v1.0.0, gcip-iap memerlukan dependensi peer firebase v9 atau yang lebih baru.
Jika Anda bermigrasi ke gcip-iap v1.0.0 atau yang lebih baru, selesaikan tindakan
berikut:
- Update versi
firebasedi filepackage.jsonAnda ke v9.6.0+. - Perbarui pernyataan impor
firebasesebagai berikut:
// Import Firebase modules.
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
// Import the gcip-iap module.
import * as ciap from 'gcip-iap';
Tidak diperlukan perubahan kode tambahan.
gcip-iap v2.0.0
Mulai versi v2.0.0, gcip-iap mengharuskan penulisan ulang aplikasi UI kustom Anda menggunakan format SDK modular. Jika Anda bermigrasi ke gcip-iap v2.0.0 atau yang lebih baru, selesaikan tindakan berikut:
- Update versi
firebasedi filepackage.jsonke v9.8.3+. - Perbarui pernyataan impor
firebasesebagai berikut:
// Import Firebase modules.
import { initializeApp } from 'firebase/app';
import { getAuth, GoogleAuthProvider } 'firebase/auth';
// Import the gcip-iap module.
import * as ciap from 'gcip-iap';
Mengonfigurasi UI
Untuk mengonfigurasi UI, buat class kustom yang mengimplementasikan
antarmuka AuthenticationHandler:
interface AuthenticationHandler {
languageCode?: string | null;
getAuth(apiKey: string, tenantId: string | null): FirebaseAuth;
startSignIn(auth: FirebaseAuth, match?: SelectedTenantInfo): Promise<UserCredential>;
selectTenant?(projectConfig: ProjectConfig, tenantIds: string[]): Promise<SelectedTenantInfo>;
completeSignOut(): Promise<void>;
processUser?(user: User): Promise<User>;
showProgressBar?(): void;
hideProgressBar?(): void;
handleError?(error: Error | CIAPError): void;
}
Selama autentikasi, library akan otomatis memanggil metode AuthenticationHandler.
Pilih tenant
Untuk memilih tenant, terapkan selectTenant(). Anda dapat menerapkan metode ini untuk memilih tenant secara terprogram, atau menampilkan UI agar pengguna dapat memilihnya sendiri.
Dalam kedua kasus tersebut, library menggunakan objek SelectedTenantInfo yang ditampilkan
untuk menyelesaikan alur autentikasi. Objek ini berisi ID tenant yang dipilih, ID penyedia, dan email yang dimasukkan pengguna.
Jika memiliki beberapa tenant dalam project, Anda harus memilih salah satu sebelum dapat mengautentikasi pengguna. Jika Anda hanya memiliki satu tenant, atau menggunakan autentikasi level project, Anda tidak perlu menerapkan selectTenant().
IAP mendukung penyedia yang sama dengan Identity Platform, seperti:
- Email dan sandi
- OAuth (Google, Facebook, Twitter, GitHub, Microsoft, dll.)
- SAML
- OIDC
- Nomor telepon
- Kustom
- Anonim
Jenis autentikasi nomor telepon, kustom, dan anonim tidak didukung untuk multi-tenancy.
Memilih tenant secara terprogram
Untuk memilih tenant secara terprogram, manfaatkan konteks saat ini. Class
Authentication berisi getOriginalURL() yang menampilkan
URL yang diakses pengguna sebelum autentikasi.
Gunakan ini untuk menemukan kecocokan dari daftar tenant terkait:
// Select provider programmatically.
selectTenant(projectConfig, tenantIds) {
return new Promise((resolve, reject) => {
// Show UI to select the tenant.
auth.getOriginalURL()
.then((originalUrl) => {
resolve({
tenantId: getMatchingTenantBasedOnVisitedUrl(originalUrl),
// If associated provider IDs can also be determined,
// populate this list.
providerIds: [],
});
})
.catch(reject);
});
}
Mengizinkan pengguna memilih tenant
Untuk mengizinkan pengguna memilih tenant, tampilkan daftar tenant dan minta pengguna memilih salah satu, atau minta mereka memasukkan alamat email, lalu temukan kecocokan berdasarkan domain:
// Select provider by showing UI.
selectTenant(projectConfig, tenantIds) {
return new Promise((resolve, reject) => {
// Show UI to select the tenant.
renderSelectTenant(
tenantIds,
// On tenant selection.
(selectedTenantId) => {
resolve({
tenantId: selectedTenantId,
// If associated provider IDs can also be determined,
// populate this list.
providerIds: [],
// If email is available, populate this field too.
email: undefined,
});
});
});
}
Mengautentikasi pengguna
Setelah Anda memiliki penyedia, terapkan getAuth() untuk menampilkan instance Auth,
yang sesuai dengan kunci API dan ID tenant yang diberikan. Jika tidak ada ID tenant yang diberikan, gunakan penyedia identitas level project.
getAuth() melacak tempat penyimpanan pengguna yang sesuai dengan konfigurasi yang disediakan. Hal ini juga memungkinkan untuk memperbarui token ID Identity Platform pengguna yang telah diautentikasi sebelumnya secara otomatis tanpa mengharuskan pengguna memasukkan kembali kredensial mereka.
Jika Anda menggunakan beberapa resource IAP dengan tenant yang berbeda, sebaiknya gunakan instance autentikasi unik untuk setiap resource. Hal ini memungkinkan beberapa resource dengan konfigurasi berbeda untuk menggunakan halaman autentikasi yang sama. Fitur ini juga memungkinkan beberapa pengguna login secara bersamaan tanpa perlu logout dari pengguna sebelumnya.
Berikut adalah contoh cara menerapkan getAuth():
gcip-iap v1.0.0
getAuth(apiKey, tenantId) {
let auth = null;
// Make sure the expected API key is being used.
if (apiKey !== expectedApiKey) {
throw new Error('Invalid project!');
}
try {
auth = firebase.app(tenantId || undefined).auth();
// Tenant ID should be already set on initialization below.
} catch (e) {
// Use different App names for every tenant so that
// multiple users can be signed in at the same time (one per tenant).
const app = firebase.initializeApp(this.config, tenantId || '[DEFAULT]');
auth = app.auth();
// Set the tenant ID on the Auth instance.
auth.tenantId = tenantId || null;
}
return auth;
}
gcip-iap v2.0.0
import {initializeApp, getApp} from 'firebase/app';
import {getAuth} from 'firebase/auth';
getAuth(apiKey, tenantId) {
let auth = null;
// Make sure the expected API key is being used.
if (apiKey !== expectedApiKey) {
throw new Error('Invalid project!');
}
try {
auth = getAuth(getApp(tenantId || undefined));
// Tenant ID should be already set on initialization below.
} catch (e) {
// Use different App names for every tenant so that
// multiple users can be signed in at the same time (one per tenant).
const app = initializeApp(this.config, tenantId || '[DEFAULT]');
auth = getAuth(app);
// Set the tenant ID on the Auth instance.
auth.tenantId = tenantId || null;
}
return auth;
}
Memproses login pengguna
Untuk menangani login, terapkan startSignIn(), tampilkan
UI bagi pengguna untuk melakukan autentikasi, lalu tampilkan
UserCredential
untuk pengguna yang login setelah selesai.
Dalam lingkungan multi-tenant, Anda dapat menentukan metode autentikasi yang tersedia
dari SelectedTenantInfo, jika disediakan. Variabel ini berisi
informasi yang sama dengan yang ditampilkan oleh selectTenant().
Contoh berikut menunjukkan implementasi startSignIn() untuk
pengguna yang sudah ada dengan email dan sandi:
gcip-iap v1.0.0
startSignIn(auth, selectedTenantInfo) {
return new Promise((resolve, reject) => {
// Show the UI to sign-in or sign-up a user.
$('#sign-in-form').on('submit', (e) => {
const email = $('#email').val();
const password = $('#password').val();
// Example: Ask the user for an email and password.
// Note: The method of sign in may have already been determined from the
// selectedTenantInfo object.
auth.signInWithEmailAndPassword(email, password)
.then((userCredential) => {
resolve(userCredential);
})
.catch((error) => {
// Show the error message.
});
});
});
}
gcip-iap v2.0.0
import {signInWithEmailAndPassword} from 'firebase/auth';
startSignIn(auth, selectedTenantInfo) {
return new Promise((resolve, reject) => {
// Show the UI to sign-in or sign-up a user.
$('#sign-in-form').on('submit', (e) => {
const email = $('#email').val();
const password = $('#password').val();
// Example: Ask the user for an email and password.
// Note: The method of sign in may have already been determined from the
// selectedTenantInfo object.
signInWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
resolve(userCredential);
})
.catch((error) => {
// Show the error message.
});
});
});
}
Anda juga dapat memproses login pengguna dengan penyedia gabungan, seperti SAML atau OIDC, menggunakan jendela pop-up atau pengalihan:
gcip-iap v1.0.0
startSignIn(auth, selectedTenantInfo) {
// Show the UI to sign-in or sign-up a user.
return new Promise((resolve, reject) => {
// Provide the user multiple buttons to sign-in.
// For example sign-in with popup using a SAML provider.
// Note: The method of sign in may have already been determined from the
// selectedTenantInfo object.
const provider = new firebase.auth.SAMLAuthProvider('saml.myProvider');
auth.signInWithPopup(provider)
.then((userCredential) => {
resolve(userCredential);
})
.catch((error) => {
// Show the error message.
});
// Using redirect flow. When the page redirects back and sign-in completes,
// ciap will detect the result and complete sign-in without any additional
// action.
auth.signInWithRedirect(provider);
});
}
gcip-iap v2.0.0
import {signInWithPopup, SAMLAuthProvider} from 'firebase/auth';
startSignIn(auth, selectedTenantInfo) {
// Show the UI to sign-in or sign-up a user.
return new Promise((resolve, reject) => {
// Provide the user multiple buttons to sign-in.
// For example sign-in with popup using a SAML provider.
// Note: The method of sign in might have already been determined from the
// selectedTenantInfo object.
const provider = new SAMLAuthProvider('saml.myProvider');
signInWithPopup(auth, provider)
.then((userCredential) => {
resolve(userCredential);
})
.catch((error) => {
// Show the error message.
});
// Using redirect flow. When the page redirects back and sign-in completes,
// ciap will detect the result and complete sign-in without any additional
// action.
signInWithRedirect(auth, provider);
});
}
Beberapa penyedia OAuth mendukung penerusan petunjuk login untuk login:
gcip-iap v1.0.0
startSignIn(auth, selectedTenantInfo) {
// Show the UI to sign-in or sign-up a user.
return new Promise((resolve, reject) => {
// Use selectedTenantInfo to determine the provider and pass the login hint
// if that provider supports it and the user specified an email address.
if (selectedTenantInfo &&
selectedTenantInfo.providerIds &&
selectedTenantInfo.providerIds.indexOf('microsoft.com') !== -1) {
const provider = new firebase.auth.OAuthProvider('microsoft.com');
provider.setCustomParameters({
login_hint: selectedTenantInfo.email || undefined,
});
} else {
// Figure out the provider used...
}
auth.signInWithPopup(provider)
.then((userCredential) => {
resolve(userCredential);
})
.catch((error) => {
// Show the error message.
});
});
}
gcip-iap v2.0.0
import {signInWithPopup, OAuthProvider} from 'firebase/auth';
startSignIn(auth, selectedTenantInfo) {
// Show the UI to sign in or sign up a user.
return new Promise((resolve, reject) => {
// Use selectedTenantInfo to determine the provider and pass the login hint
// if that provider supports it and the user specified an email address.
if (selectedTenantInfo &&
selectedTenantInfo.providerIds &&
selectedTenantInfo.providerIds.indexOf('microsoft.com') !== -1) {
const provider = new OAuthProvider('microsoft.com');
provider.setCustomParameters({
login_hint: selectedTenantInfo.email || undefined,
});
} else {
// Figure out the provider used...
}
signInWithPopup(auth, provider)
.then((userCredential) => {
resolve(userCredential);
})
.catch((error) => {
// Show the error message.
});
});
}
Lihat Melakukan autentikasi dengan multi-tenancy untuk mengetahui informasi selengkapnya.
Menangani error
Untuk menampilkan pesan error kepada pengguna atau mencoba memulihkan dari error seperti waktu tunggu jaringan habis,
terapkan handleError().
Contoh berikut mengimplementasikan handleError():
handleError(error) {
showAlert({
code: error.code,
message: error.message,
// Whether to show the retry button. This is only available if the error is
// recoverable via retrial.
retry: !!error.retry,
});
// When user clicks retry, call error.retry();
$('.alert-link').on('click', (e) => {
error.retry();
e.preventDefault();
return false;
});
}
Tabel di bawah mencantumkan kode error khusus IAP yang dapat ditampilkan. Identity Platform juga dapat menampilkan error; lihat dokumentasi untuk firebase.auth.Auth.
| Kode error | Deskripsi |
|---|---|
invalid-argument |
Klien menentukan argumen yang tidak valid. |
failed-precondition |
Permintaan tidak dapat dijalankan dalam status sistem saat ini. |
out-of-range |
Klien menentukan rentang yang tidak valid. |
unauthenticated |
Permintaan tidak diautentikasi karena token OAuth tidak ada, tidak valid, atau sudah tidak berlaku. |
permission-denied |
Klien tidak memiliki izin yang memadai, atau UI dihosting di domain yang tidak sah. |
not-found . |
Resource yang ditentukan tidak ditemukan. |
aborted |
Konflik serentak, seperti konflik baca-ubah-tulis. |
already-exists |
Resource yang klien coba buat sudah ada. |
resource-exhausted |
Kuota resource telah habis atau mencapai pembatasan kapasitas. |
cancelled |
Permintaan dibatalkan oleh klien. |
data-loss |
Kehilangan data atau kerusakan data yang tidak dapat dipulihkan. |
unknown |
Terjadi error yang tidak diketahui pada server. |
internal |
Error server internal. |
not-implemented |
Metode API tidak diimplementasikan oleh server. |
unavailable |
Layanan tidak tersedia. |
restart-process |
Buka kembali URL yang mengalihkan Anda ke halaman ini untuk memulai ulang proses autentikasi. |
deadline-exceeded |
Batas waktu permintaan terlampaui. |
authentication-uri-fail |
Gagal membuat URI autentikasi. |
gcip-token-invalid |
Token ID GCIP yang diberikan tidak valid. |
gcip-redirect-invalid |
URL pengalihan tidak valid. |
get-project-mapping-fail |
Gagal mendapatkan project ID. |
gcip-id-token-encryption-error |
Error enkripsi token ID GCIP. |
gcip-id-token-decryption-error |
Error dekripsi token ID GCIP. |
gcip-id-token-unescape-error |
Unescape base64 yang aman untuk web gagal. |
resource-missing-gcip-sign-in-url |
URL autentikasi GCIP tidak ada untuk resource IAP yang ditentukan. |
Menyesuaikan UI
Anda dapat menyesuaikan halaman autentikasi dengan fitur opsional seperti status progres dan halaman logout.
Menampilkan UI progres
Untuk menampilkan UI progres kustom kepada pengguna setiap kali modul gcip-iap menjalankan tugas jaringan yang berjalan lama,
terapkan showProgressBar() dan hideProgressBar().
Membuat pengguna logout
Dalam beberapa kasus, Anda mungkin ingin mengizinkan pengguna logout dari semua sesi saat ini yang menggunakan URL autentikasi yang sama.
Setelah pengguna logout, mungkin tidak ada URL untuk mengalihkan mereka kembali.
Hal ini biasanya terjadi saat pengguna logout dari semua tenant yang terkait dengan
halaman login. Dalam hal ini, terapkan completeSignOut() untuk
menampilkan pesan yang menunjukkan bahwa pengguna berhasil logout. Jika Anda tidak menerapkan metode ini,
halaman kosong akan muncul saat pengguna logout.
Memproses pengguna
Untuk mengubah pengguna yang login sebelum mengalihkan ke resource IAP, terapkan processUser().
Anda dapat menggunakan metode ini untuk melakukan hal berikut:
- Link ke penyedia tambahan.
- Perbarui profil pengguna.
- Meminta data tambahan kepada pengguna setelah pendaftaran.
- Memproses token akses OAuth yang ditampilkan oleh
getRedirectResult()setelah memanggilsignInWithRedirect().
Berikut adalah contoh penerapan processUser():
gcip-iap v1.0.0
processUser(user) {
return lastAuthUsed.getRedirectResult().then(function(result) {
// Save additional data, or ask the user for additional profile information
// to store in database, etc.
if (result) {
// Save result.additionalUserInfo.
// Save result.credential.accessToken for OAuth provider, etc.
}
// Return the user.
return user;
});
}
gcip-iap v2.0.0
import {getRedirectResult} from 'firebase/auth';
processUser(user) {
return getRedirectResult(lastAuthUsed).then(function(result) {
// Save additional data, or ask the user for additional profile information
// to store in database, etc.
if (result) {
// Save result.additionalUserInfo.
// Save result.credential.accessToken for OAuth provider, etc.
}
// Return the user.
return user;
});
}
Jika Anda ingin perubahan apa pun pada pengguna tercermin dalam klaim token ID yang disebarkan oleh IAP ke aplikasi Anda, Anda harus memaksa token untuk dimuat ulang:
gcip-iap v1.0.0
processUser(user) {
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;
});
}
gcip-iap v2.0.0
import {updateProfile} from 'firebase/auth';
processUser(user) {
return updateProfile(user, {
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;
});
}
Menguji UI
Setelah membuat class yang mengimplementasikan AuthenticationHandler, Anda dapat
menggunakannya untuk membuat instance Authentication baru, dan memulainya:
// Implement interface AuthenticationHandler.
// const authHandlerImplementation = ....
const ciapInstance = new ciap.Authentication(authHandlerImplementation);
ciapInstance.start();
Deploy aplikasi Anda dan buka halaman autentikasi. Anda akan melihat UI login kustom.
Langkah berikutnya
- Pelajari cara mengakses resource non-Google secara terprogram.
- Pelajari cara mengelola sesi.
- Pelajari cara identitas eksternal bekerja dengan IAP.