Artikel 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 mendapatkan pengalaman yang lebih sederhana.
Ringkasan
Untuk membuat halaman autentikasi Anda sendiri, ikuti langkah-langkah berikut:
- Aktifkan identitas eksternal. Pilih Saya akan memberikan opsi UI sendiri selama penyiapan.
- Instal library
gcip-iap
. - Konfigurasikan UI dengan mengimplementasikan antarmuka
AuthenticationHandler
. Halaman autentikasi Anda harus menangani skenario berikut:- Pemilihan penyewa
- Otorisasi pengguna
- Login pengguna
- Penanganan error
- Opsional: Sesuaikan halaman autentikasi Anda dengan fitur tambahan, seperti status progres, halaman logout, dan pemrosesan pengguna.
- Uji UI Anda.
Menginstal library gcip-iap
Untuk menginstal library gcip-iap
, jalankan perintah berikut:
npm install gcip-iap --save
Modul NPM gcip-iap
memisahkan komunikasi antara
aplikasi, IAP, dan Identity Platform Anda. Hal ini memungkinkan Anda menyesuaikan seluruh
alur autentikasi tanpa harus mengelola pertukaran dasar 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 hingga v1.1.0
Mulai dari 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
firebase
di filepackage.json
Anda ke v9.6.0+. - Perbarui pernyataan impor
firebase
sebagai 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
memerlukan 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
firebase
di filepackage.json
Anda ke v9.8.3+. - Perbarui pernyataan impor
firebase
sebagai 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
.
Memilih tenant
Untuk memilih tenant, terapkan selectTenant()
. Anda
dapat mengimplementasikan metode ini untuk memilih tenant secara terprogram, atau
menampilkan UI sehingga pengguna dapat memilihnya sendiri.
Dalam kedua kasus tersebut, library menggunakan objek SelectedTenantInfo
yang ditampilkan
untuk menyelesaikan alur autentikasi. Isinya adalah ID tenant yang dipilih, ID penyedia apa pun, dan email yang dimasukkan pengguna.
Jika ada beberapa tenant dalam project, Anda harus memilih satu tenant sebelum dapat mengautentikasi pengguna. Jika hanya memiliki satu tenant, atau menggunakan autentikasi level project, Anda tidak perlu mengimplementasikan selectTenant()
.
IAP mendukung penyedia yang sama dengan Identity Platform, seperti:
- Email dan sandi
- OAuth (Google, Facebook, Twitter, GitHub, Microsoft, dll.)
- SAML
- OIDC
- Nomor telepon
- Khusus
- Anonim
Jenis autentikasi dengan 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 metode 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 untuk memilih tenant
Untuk memungkinkan pengguna memilih tenant, tampilkan daftar tenant dan minta pengguna memilih satu, atau minta mereka memasukkan alamat email, lalu menemukan 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 mengembalikan instance Auth, yang sesuai dengan kunci API dan ID tenant yang diberikan. Jika ID tenant tidak diberikan, gunakan penyedia identitas level project.
getAuth()
melacak tempat pengguna yang sesuai dengan
konfigurasi yang diberikan disimpan. Hal ini juga memungkinkan Anda memperbarui token ID Identity Platform milik pengguna yang telah diautentikasi sebelumnya secara diam-diam tanpa mengharuskan pengguna memasukkan kembali kredensialnya.
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 menggunakan halaman autentikasi yang sama. Tindakan ini juga memungkinkan beberapa pengguna login secara bersamaan tanpa membuat pengguna sebelumnya logout.
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 proses login, implementasikan startSignIn()
, tampilkan
UI untuk autentikasi pengguna, lalu tampilkan
UserCredential
untuk pengguna yang sudah login setelah selesai.
Dalam lingkungan multi-tenant, Anda dapat menentukan metode autentikasi yang tersedia dari SelectedTenantInfo
, jika metode tersebut disediakan. Variabel ini berisi informasi yang sama dengan yang ditampilkan oleh selectTenant()
.
Contoh berikut menunjukkan implementasi startSignIn()
untuk
pengguna yang 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 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.
});
});
}
Baca bagian Mengautentikasi dengan multi-tenancy untuk mengetahui informasi selengkapnya.
Menangani error
Untuk menampilkan pesan error kepada pengguna atau mencoba pemulihan 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 diizinkan. |
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 |
Kunjungi 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 alihan tidak valid. |
get-project-mapping-fail |
Gagal mendapatkan ID project. |
gcip-id-token-encryption-error |
Terjadi error pada enkripsi token ID GCIP. |
gcip-id-token-decryption-error |
Error dekripsi token ID GCIP. |
gcip-id-token-unescape-error |
Unescape base64 keamanan web gagal. |
resource-missing-gcip-sign-in-url |
URL autentikasi GCIP untuk resource IAP yang ditentukan tidak ada. |
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
mengeksekusi tugas jaringan yang berjalan lama,
implementasikan showProgressBar()
dan hideProgressBar()
.
Membuat pengguna logout
Dalam beberapa kasus, Anda dapat mengizinkan pengguna untuk logout dari semua sesi saat ini yang memiliki 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 sudah login sebelum beralih ke resource IAP, terapkan processUser()
.
Anda dapat menggunakan metode ini untuk melakukan hal berikut:
- Tautkan ke penyedia tambahan.
- Perbarui profil pengguna.
- Minta 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 ingin perubahan apa pun pada pengguna tercermin dalam klaim token ID yang disebarkan oleh IAP ke aplikasi, 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, lalu 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 selanjutnya
- Pelajari cara mengakses resource non-Google secara terprogram.
- Pelajari cara mengelola sesi.
- Pelajari cara kerja identitas eksternal dengan IAP.