Mengautentikasi dengan multi-tenancy
Dokumen ini menunjukkan cara mengautentikasi pengguna di lingkungan Identity Platform multi-tenant.
Sebelum memulai
Pastikan Anda telah mengaktifkan multi-tenancy untuk project dan mengonfigurasi tenant. Lihat Memulai multi-tenancy untuk mempelajari caranya.
Anda juga harus menambahkan Client SDK ke aplikasi:
Buka halaman Identity Platform di konsol Google Cloud.
Buka halaman pengguna Identity PlatformDi kanan atas, klik Detail penyiapan aplikasi.
Salin kode ke aplikasi web Anda. Misalnya:
Web versi 9
import { initializeApp } from "firebase/app"; const firebaseConfig = { apiKey: "...", // By default, authDomain is '[YOUR_APP].firebaseapp.com'. // You may replace it with a custom domain. authDomain: '[YOUR_CUSTOM_DOMAIN]' }; const firebaseApp = initializeApp(firebaseConfig);
Web versi 8
firebase.initializeApp({ apiKey: '...', // By default, authDomain is '[YOUR_APP].firebaseapp.com'. // You may replace it with a custom domain. authDomain: '[YOUR_CUSTOM_DOMAIN]' });
Login dengan tenant
Untuk login ke tenant, ID tenant harus diteruskan ke objek auth
.
Perhatikan bahwa tenantId
tidak dipertahankan saat halaman dimuat ulang.
Web versi 9
import { getAuth } from "firebase/auth"; const auth = getAuth(); const tenantId = "TENANT_ID1"; auth.tenantId = tenantId;
Web versi 8
const tenantId = "TENANT_ID1"; firebase.auth().tenantId = tenantId;
Setiap permintaan login di masa mendatang dari instance auth
ini akan menyertakan ID tenant (TENANT_ID1
dalam contoh sebelumnya) hingga Anda mengubah atau mereset ID tenant.
Anda dapat menggunakan beberapa tenant menggunakan satu atau beberapa instance auth
.
Untuk menggunakan satu instance auth
, ubah properti tenantId
setiap kali Anda ingin beralih antar-tenant. Untuk kembali ke IdP tingkat project, tetapkan
tenantId
ke null
:
Web versi 9
// One Auth instance // Switch to tenant1 auth.tenantId = "TENANT_ID1"; // Switch to tenant2 auth.tenantId = "TENANT_ID2"; // Switch back to project level IdPs auth.tenantId = null;
Web versi 8
// One Auth instance // Switch to tenant1 firebase.auth().tenantId = "TENANT_ID1"; // Switch to tenant2 firebase.auth().tenantId = "TENANT_ID2"; // Switch back to project level IdPs firebase.auth().tenantId = null;
Untuk menggunakan beberapa instance, buat instance auth
baru untuk setiap tenant dan
tetapkan ID yang berbeda:
Web versi 9
// Multiple Auth instances import { initializeApp } from "firebase/app"; import { getAuth } from "firebase/auth"; const firebaseApp1 = initializeApp(firebaseConfig1, 'app1_for_tenantId1'); const firebaseApp2 = initializeApp(firebaseConfig2, 'app2_for_tenantId2'); const auth1 = getAuth(firebaseApp1); const auth2 = getAuth(firebaseApp2); auth1.tenantId = "TENANT_ID1"; auth2.tenantId = "TENANT_ID2";
Web versi 8
// Multiple Auth instances firebase.initializeApp(config, 'app1_for_tenantId1'); firebase.initializeApp(config, 'app2_for_tenantId2'); const auth1 = firebase.app('app1').auth(); const auth2 = firebase.app('app2').auth(); auth1.tenantId = "TENANT_ID1"; auth2.tenantId = "TENANT_ID2";
Setelah login dengan tenant, pengguna tenant akan ditampilkan dengan
user.tenantId
ditetapkan ke tenant tersebut. Perhatikan bahwa jika Anda mengaktifkan tenantId
di instance auth
nanti, properti currentUser
tidak akan berubah; properti tersebut akan tetap mengarah ke pengguna yang sama dengan tenant sebelumnya.
Web versi 9
import { signInWithEmailAndPassword, onAuthStateChanged } from "firebase/auth"; // Switch to TENANT_ID1 auth.tenantId = 'TENANT_ID1'; // Sign in with tenant signInWithEmailAndPassword(auth, email, password) .then((userCredential) => { // User is signed in. const user = userCredential.user; // user.tenantId is set to 'TENANT_ID1'. // Switch to 'TENANT_ID2'. auth.tenantId = 'TENANT_ID2'; // auth.currentUser still points to the user. // auth.currentUser.tenantId is 'TENANT_ID1'. }); // You could also get the current user from Auth state observer. onAuthStateChanged(auth, (user) => { if (user) { // User is signed in. // user.tenantId is set to 'TENANT_ID1'. } else { // No user is signed in. } });
Web versi 8
// Switch to TENANT_ID1 firebase.auth().tenantId = 'TENANT_ID1'; // Sign in with tenant firebase.auth().signInWithEmailAndPassword(email, password) .then((result) => { const user = result.user; // user.tenantId is set to 'TENANT_ID1'. // Switch to 'TENANT_ID2'. firebase.auth().tenantId = 'TENANT_ID2'; // firebase.auth().currentUser still point to the user. // firebase.auth().currentUser.tenantId is 'TENANT_ID1'. }); // You could also get the current user from Auth state observer. firebase.auth().onAuthStateChanged((user) => { if (user) { // User is signed in. // user.tenantId is set to 'TENANT_ID1'. } else { // No user is signed in. } });
Akun email/sandi
Contoh berikut menunjukkan cara mendaftarkan pengguna baru:
Web versi 9
import { createUserWithEmailAndPassword } from "firebase/auth"; auth.tenantId = 'TENANT_ID'; createUserWithEmailAndPassword(auth, email, password) .then((userCredential) => { // User is signed in. // userCredential.user.tenantId is 'TENANT_ID'. }).catch((error) => { // Handle / display error. // ... });
Web versi 8
firebase.auth().tenantId = 'TENANT_ID'; firebase.auth().createUserWithEmailAndPassword(email, password) .then((result) => { // result.user.tenantId is 'TENANT_ID'. }).catch((error) => { // Handle error. });
Untuk login pengguna yang sudah ada:
Web versi 9
import { signInWithEmailAndPassword } from "firebase/auth"; auth.tenantId = 'TENANT_ID'; signInWithEmailAndPassword(auth, email, password) .then((userCredential) => { // User is signed in. // userCredential.user.tenantId is 'TENANT_ID'. }).catch((error) => { // Handle / display error. // ... });
Web versi 8
firebase.auth().tenantId = 'TENANT_ID'; firebase.auth().signInWithEmailAndPassword(email, password) .then((result) => { // result.user.tenantId is 'TENANT_ID'. }).catch((error) => { // Handle error. });
SAML
Untuk login dengan penyedia SAML, buat instance SAMLAuthProvider
dengan ID penyedia dari konsol Google Cloud:
Web versi 9
import { SAMLAuthProvider } from "firebase/auth"; const provider = new SAMLAuthProvider("saml.myProvider");
Web versi 8
const provider = new firebase.auth.SAMLAuthProvider('saml.myProvider');
Kemudian, Anda dapat menggunakan pop-up atau alur pengalihan untuk login ke penyedia SAML.
Pop-up
Web versi 9
import { signInWithPopup } from "firebase/auth"; // Switch to TENANT_ID1. auth.tenantId = 'TENANT_ID1'; // Sign-in with popup. signInWithPopup(auth, provider) .then((userCredential) => { // User is signed in. const user = userCredential.user; // user.tenantId is set to 'TENANT_ID1'. // Provider data available from the result.user.getIdToken() // or from result.user.providerData }) .catch((error) => { // Handle / display error. // ... });
Web versi 8
// Switch to TENANT_ID1. firebase.auth().tenantId = 'TENANT_ID1'; // Sign-in with popup. firebase.auth().signInWithPopup(provider) .then((result) => { // User is signed in. // tenant ID is available in result.user.tenantId. // Identity provider data is available in result.additionalUserInfo.profile. }) .catch((error) => { // Handle error. });
Pengalihan
Web versi 9
import { signInWithRedirect, getRedirectResult } from "firebase/auth"; // Switch to TENANT_ID1. auth.tenantId = 'TENANT_ID1'; // Sign-in with redirect. signInWithRedirect(auth, provider); // After the user completes sign-in and returns to the app, you can get // the sign-in result by calling getRedirectResult. However, if they sign out // and sign in again with an IdP, no tenant is used. getRedirectResult(auth) .then((result) => { // User is signed in. // The tenant ID available in result.user.tenantId. // Provider data available from the result.user.getIdToken() // or from result.user.providerData }) .catch((error) => { // Handle / display error. // ... });
Web versi 8
// Switch to TENANT_ID1. firebase.auth().tenantId = 'TENANT_ID1'; // Sign-in with redirect. firebase.auth().signInWithRedirect(provider); // After the user completes sign-in and returns to the app, you can get // the sign-in result by calling getRedirectResult. However, if they sign out // and sign in again with an IdP, no tenant is used. firebase.auth().getRedirectResult() .then((result) => { // User is signed in. // The tenant ID available in result.user.tenantId. // Identity provider data is available in result.additionalUserInfo.profile. }) .catch((error) => { // Handle error. });
Dalam kedua kasus tersebut, pastikan untuk menetapkan ID tenant yang benar di instance auth
.
Mengirim link melalui email
Untuk memulai alur autentikasi, tampilkan antarmuka yang meminta pengguna untuk
memberikan alamat emailnya, lalu panggil sendSignInLinkToEmail
untuk mengirim
link autentikasi kepada mereka. Pastikan untuk menetapkan ID tenant yang benar pada instance auth
sebelum mengirim email.
Web versi 9
import { sendSignInLinkToEmail } from "firebase/auth"; // Switch to TENANT_ID1 auth.tenantId = 'TENANT_ID1'; sendSignInLinkToEmail(auth, email, actionCodeSettings) .then(() => { // The link was successfully sent. Inform the user. // Save the email locally so you don't need to ask the user for it again // if they open the link on the same device. window.localStorage.setItem('emailForSignIn', email); }) .catch((error) => { // Handle / display error. // ... });
Web versi 8
// Switch to TENANT_ID1 firebase.auth().tenantId = 'TENANT_ID1'; firebase.auth().sendSignInLinkToEmail(email, actionCodeSettings) .then(() => { // The link was successfully sent. Inform the user. // Save the email locally so you don't need to ask the user for it again // if they open the link on the same device. window.localStorage.setItem('emailForSignIn', email); }) .catch((error) => { // Some error occurred, you can inspect the code: error.code });
Untuk menyelesaikan login di halaman landing, pertama-tama mengurai ID tenant dari link email dan menetapkannya di instance auth
. Kemudian, panggil signInWithEmailLink
dengan email pengguna dan link email sebenarnya yang berisi kode sekali pakai.
Web versi 9
import { isSignInWithEmailLink, parseActionCodeURL, signInWithEmailLink } from "firebase/auth"; if (isSignInWithEmailLink(auth, window.location.href)) { const actionCodeUrl = parseActionCodeURL(window.location.href); if (actionCodeUrl.tenantId) { auth.tenantId = actionCodeUrl.tenantId; } let email = window.localStorage.getItem('emailForSignIn'); if (!email) { // User opened the link on a different device. To prevent session fixation // attacks, ask the user to provide the associated email again. For example: email = window.prompt('Please provide your email for confirmation'); } // The client SDK will parse the code from the link for you. signInWithEmailLink(auth, email, window.location.href) .then((result) => { // User is signed in. // tenant ID available in result.user.tenantId. // Clear email from storage. window.localStorage.removeItem('emailForSignIn'); }); }
Web versi 8
if (firebase.auth().isSignInWithEmailLink(window.location.href)) { const actionCodeUrl = firebase.auth.ActionCodeURL.parseLink(window.location.href); if (actionCodeUrl.tenantId) { firebase.auth().tenantId = actionCodeUrl.tenantId; } let email = window.localStorage.getItem('emailForSignIn'); if (!email) { // User opened the link on a different device. To prevent session fixation // attacks, ask the user to provide the associated email again. For example: email = window.prompt('Please provide your email for confirmation'); } firebase.auth().signInWithEmailLink(email, window.location.href) .then((result) => { // User is signed in. // tenant ID available in result.user.tenantId. }); }
Membuat token kustom
Membuat token kustom multi-tenant sama dengan membuat token kustom
reguler; selama ID tenant yang benar telah ditetapkan pada instance auth
, klaim tenant_id
tingkat atas akan ditambahkan ke JWT yang dihasilkan.
Lihat Membuat token kustom
untuk mengetahui petunjuk mendetail tentang cara membuat dan menggunakan token kustom.
Contoh berikut menunjukkan cara membuat token kustom menggunakan Admin SDK:
Web versi 9
// Ensure you're using a tenant-aware auth instance const tenantManager = admin.auth().tenantManager(); const tenantAuth = tenantManager.authForTenant('TENANT_ID1'); // Create a custom token in the usual manner tenantAuth.createCustomToken(uid) .then((customToken) => { // Send token back to client }) .catch((error) => { console.log('Error creating custom token:', error); });
Web versi 8
// Ensure you're using a tenant-aware auth instance const tenantManager = admin.auth().tenantManager(); const tenantAuth = tenantManager.authForTenant('TENANT_ID1'); // Create a custom token in the usual manner tenantAuth.createCustomToken(uid) .then((customToken) => { // Send token back to client }) .catch((error) => { console.log('Error creating custom token:', error); });
Kode berikut menunjukkan cara login menggunakan token kustom:
Web versi 9
import { signInWithCustomToken } from "firebase/auth"; auth.tenantId = 'TENANT_ID1'; signInWithCustomToken(auth, token) .catch((error) => { // Handle / display error. // ... });
Web versi 8
firebase.auth().tenantId = 'TENANT_ID1'; firebase.auth().signInWithCustomToken(token) .catch((error) => { // Handle Errors here. const errorCode = error.code; const errorMessage = error.message; // ... });
Perhatikan bahwa jika ID tenant tidak cocok, metode signInWithCustomToken()
akan gagal.
Menautkan kredensial pengguna multi-tenant
Anda dapat menautkan jenis kredensial lain ke pengguna multi-tenant yang ada. Misalnya, jika pengguna sebelumnya diautentikasi dengan penyedia SAML di tenant, Anda dapat menambahkan login email/sandi ke akunnya yang sudah ada sehingga mereka dapat menggunakan salah satu metode untuk login ke tenant.
Web versi 9
import { signInWithPopup, EmailAuthProvider, linkWithCredential, SAMLAuthProvider, signInWithCredential } from "firebase/auth"; // Switch to TENANT_ID1 auth.tenantId = 'TENANT_ID1'; // Sign-in with popup signInWithPopup(auth, provider) .then((userCredential) => { // Existing user with e.g. SAML provider. const prevUser = userCredential.user; const emailCredential = EmailAuthProvider.credential(email, password); return linkWithCredential(prevUser, emailCredential) .then((linkResult) => { // Sign in with the newly linked credential const linkCredential = SAMLAuthProvider.credentialFromResult(linkResult); return signInWithCredential(auth, linkCredential); }) .then((signInResult) => { // Handle sign in of merged user // ... }); }) .catch((error) => { // Handle / display error. // ... });
Web versi 8
// Switch to TENANT_ID1 firebase.auth().tenantId = 'TENANT_ID1'; // Sign-in with popup firebase.auth().signInWithPopup(provider) .then((result) => { // Existing user with e.g. SAML provider. const user = result.user; const emailCredential = firebase.auth.EmailAuthProvider.credential(email, password); return user.linkWithCredential(emailCredential); }) .then((linkResult) => { // The user can sign in with both SAML and email/password now. });
Saat menautkan atau mengautentikasi ulang pengguna multi-tenant yang ada,
auth.tenantId
akan diabaikan; gunakan user.tenantId
untuk menentukan tenant yang akan
digunakan. Hal ini juga berlaku untuk API pengelolaan pengguna lainnya, seperti updateProfile
dan updatePassword
.
Menangani error account-exists-with-different-credential
Jika Anda mengaktifkan setelan Tautkan akun yang menggunakan email yang sama di konsol Google Cloud, saat pengguna mencoba login ke penyedia (seperti SAML) dengan email yang sudah ada untuk penyedia lain (seperti Google), error auth/account-exists-with-different-credential
akan ditampilkan (bersama dengan objek AuthCredential
).
Untuk menyelesaikan proses login dengan penyedia yang dimaksud, pengguna harus login terlebih dahulu
ke penyedia yang ada (Google), lalu menautkan akunnya ke AuthCredential
sebelumnya
(SAML).
Anda dapat menggunakan pop-up atau alur pengalihan untuk menangani error ini.
Pop-up
Web versi 9
import { signInWithPopup, fetchSignInMethodsForEmail, linkWithCredential } from "firebase/auth"; // Step 1. // User tries to sign in to the SAML provider in that tenant. auth.tenantId = 'TENANT_ID'; signInWithPopup(auth, samlProvider) .catch((error) => { // An error happened. if (error.code === 'auth/account-exists-with-different-credential') { // Step 2. // User's email already exists. // The pending SAML credential. const pendingCred = error.credential; // The credential's tenantId if needed: error.tenantId // The provider account's email address. const email = error.customData.email; // Get sign-in methods for this email. fetchSignInMethodsForEmail(email, auth) .then((methods) => { // Step 3. // Ask the user to sign in with existing Google account. if (methods[0] == 'google.com') { signInWithPopup(auth, googleProvider) .then((result) => { // Step 4 // Link the SAML AuthCredential to the existing user. linkWithCredential(result.user, pendingCred) .then((linkResult) => { // SAML account successfully linked to the existing // user. goToApp(); }); }); } }); } });
Web versi 8
// Step 1. // User tries to sign in to the SAML provider in that tenant. firebase.auth().tenantId = 'TENANT_ID'; firebase.auth().signInWithPopup(samlProvider) .catch((error) => { // An error happened. if (error.code === 'auth/account-exists-with-different-credential') { // Step 2. // User's email already exists. // The pending SAML credential. const pendingCred = error.credential; // The credential's tenantId if needed: error.tenantId // The provider account's email address. const email = error.email; // Get sign-in methods for this email. firebase.auth().fetchSignInMethodsForEmail(email) .then((methods) => { // Step 3. // Ask the user to sign in with existing Google account. if (methods[0] == 'google.com') { firebase.auth().signInWithPopup(googleProvider) .then((result) => { // Step 4 // Link the SAML AuthCredential to the existing user. result.user.linkWithCredential(pendingCred) .then((linkResult) => { // SAML account successfully linked to the existing // user. goToApp(); }); }); } }); } });
Pengalihan
Saat menggunakan
signInWithRedirect
, errorauth/account-exists-with-different-credential
akan ditampilkan digetRedirectResult
saat menyelesaikan alur pengalihan.Objek error berisi properti
error.tenantId
. Karena ID tenant di instanceauth
tidak dipertahankan setelah pengalihan, Anda harus menetapkan ID tenant dari objek error ke instanceauth
.Contoh berikut menunjukkan cara menangani error:
Web versi 9
import { signInWithRedirect, getRedirectResult, fetchSignInMethodsForEmail, linkWithCredential } from "firebase/auth"; // Step 1. // User tries to sign in to SAML provider. auth.tenantId = 'TENANT_ID'; signInWithRedirect(auth, samlProvider); var pendingCred; // Redirect back from SAML IDP. auth.tenantId is null after redirecting. getRedirectResult(auth).catch((error) => { if (error.code === 'auth/account-exists-with-different-credential') { // Step 2. // User's email already exists. const tenantId = error.tenantId; // The pending SAML credential. pendingCred = error.credential; // The provider account's email address. const email = error.customData.email; // Need to set the tenant ID again as the page was reloaded and the // previous setting was reset. auth.tenantId = tenantId; // Get sign-in methods for this email. fetchSignInMethodsForEmail(auth, email) .then((methods) => { // Step 3. // Ask the user to sign in with existing Google account. if (methods[0] == 'google.com') { signInWithRedirect(auth, googleProvider); } }); } }); // Redirect back from Google. auth.tenantId is null after redirecting. getRedirectResult(auth).then((result) => { // Step 4 // Link the SAML AuthCredential to the existing user. // result.user.tenantId is 'TENANT_ID'. linkWithCredential(result.user, pendingCred) .then((linkResult) => { // SAML account successfully linked to the existing // user. goToApp(); }); });
Web versi 8
// Step 1. // User tries to sign in to SAML provider. firebase.auth().tenantId = 'TENANT_ID'; firebase.auth().signInWithRedirect(samlProvider); var pendingCred; // Redirect back from SAML IDP. auth.tenantId is null after redirecting. firebase.auth().getRedirectResult().catch((error) => { if (error.code === 'auth/account-exists-with-different-credential') { // Step 2. // User's email already exists. const tenantId = error.tenantId; // The pending SAML credential. pendingCred = error.credential; // The provider account's email address. const email = error.email; // Need to set the tenant ID again as the page was reloaded and the // previous setting was reset. firebase.auth().tenantId = tenantId; // Get sign-in methods for this email. firebase.auth().fetchSignInMethodsForEmail(email) .then((methods) => { // Step 3. // Ask the user to sign in with existing Google account. if (methods[0] == 'google.com') { firebase.auth().signInWithRedirect(googleProvider); } }); } }); // Redirect back from Google. auth.tenantId is null after redirecting. firebase.auth().getRedirectResult().then((result) => { // Step 4 // Link the SAML AuthCredential to the existing user. // result.user.tenantId is 'TENANT_ID'. result.user.linkWithCredential(pendingCred) .then((linkResult) => { // SAML account successfully linked to the existing // user. goToApp(); }); });
Menonaktifkan pembuatan dan penghapusan akun pengguna akhir
Ada situasi saat Anda ingin administrator membuat akun pengguna, bukan akun yang dibuat melalui tindakan pengguna. Dalam kasus ini, Anda dapat menonaktifkan tindakan pengguna melalui REST API kami:
curl --location --request PATCH 'https://identitytoolkit.googleapis.com/v2/projects/PROJECT_ID/tenants/TENANT_ID?updateMask=client' \ --header 'Authorization: Bearer AUTH_TOKEN' \ --header 'Content-Type: application/json' \ --data-raw '{ "client": { "permissions": { "disabled_user_signup": true, "disabled_user_deletion": true } } }'
Ganti kode berikut:
AUTH_TOKEN
: token autentikasi.PROJECT_ID
: project ID.TENANT_ID
: ID tenant.
Langkah selanjutnya
- Membuat halaman login untuk beberapa tenant
- Memigrasikan pengguna yang ada ke tenant
- Mengelola tenant secara terprogram