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 pengalaman yang lebih sederhana.
Ringkasan
Untuk membuat halaman autentikasi Anda sendiri, ikuti langkah-langkah berikut:
- Aktifkan identitas eksternal. Pilih Saya akan menyediakan opsi UI saya sendiri selama penyiapan.
- Instal library
gcip-iap
. - Konfigurasi UI dengan menerapkan 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.
- 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 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 hingga 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
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 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. URL ini berisi ID tenant yang dipilih, ID penyedia apa pun, dan email yang dimasukkan pengguna.
Jika memiliki beberapa tenant dalam project, Anda harus memilih salah satunya sebelum dapat
melakukan autentikasi pengguna. Jika hanya memiliki satu tenant, atau menggunakan autentikasi tingkat 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 untuk memilih salah satunya, 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 tingkat project.
getAuth()
melacak tempat pengguna yang sesuai dengan
konfigurasi yang diberikan disimpan. Hal ini juga memungkinkan untuk memuat ulang token ID Identity Platform pengguna yang diautentikasi sebelumnya secara otomatis 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 yang berbeda untuk menggunakan halaman autentikasi yang sama. Fitur ini juga memungkinkan beberapa pengguna login secara bersamaan tanpa logout 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 untuk diautentikasi pengguna, 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 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 membuat pengguna login 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.
});
});
}
Lihat Mengautentikasi dengan multi-tenancy untuk informasi selengkapnya.
Menangani error
Untuk menampilkan pesan error kepada pengguna atau mencoba pemulihan dari error seperti waktu tunggu jaringan habis,
terapkan handleError()
.
Contoh berikut menerapkan 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 alihan 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 |
Penghapusan enkode base64 yang aman bagi 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 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 login sebelum mengalihkan ke resource IAP,
terapkan processUser()
.
Anda dapat menggunakan metode ini untuk melakukan hal berikut:
- Link 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 Anda ingin perubahan pada pengguna ditampilkan 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 menerapkan 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 selanjutnya
- Pelajari cara mengakses resource non-Google secara terprogram.
- Pelajari cara mengelola sesi.
- Pelajari cara kerja identitas eksternal dengan IAP.