Skip to content

Commit f2e74be

Browse files
committed
add multi-tenancy snippets
1 parent 20d5d98 commit f2e74be

17 files changed

+1049
-0
lines changed

auth-next/multi-tenancy.js

Lines changed: 349 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,349 @@
1+
// [SNIPPET_REGISTRY disabled]
2+
// [SNIPPETS_SEPARATION enabled]
3+
4+
const { SAMLAuthProvider } = require(".pnpm/@[email protected]_@[email protected]/node_modules/@firebase/auth/dist/cordova/src");
5+
6+
function signInWithTenants() {
7+
// [START multitenant_set_tenant]
8+
const { getAuth } = require("firebase/auth");
9+
const auth = getAuth();
10+
const tenantId = "TENANT_ID1";
11+
auth.tenantId = tenantId;
12+
// [END multitenant_set_tenant]
13+
}
14+
15+
function switchTenantSingleAuthInstance(auth) {
16+
// [START multitenant_switch_tenant]
17+
// One Auth instance
18+
// Switch to tenant1
19+
auth.tenantId = "TENANT_ID1";
20+
// Switch to tenant2
21+
auth.tenantId = "TENANT_ID2";
22+
// Switch back to project level IdPs
23+
auth.tenantId = null;
24+
// [END multitenant_switch_tenant]
25+
}
26+
27+
function switchTenantMultiAuthInstance(firebaseConfig1, firebaseConfig2) {
28+
// [START multitenant_switch_tenant_multiinstance]
29+
// Multiple Auth instances
30+
const { initializeApp } = require("firebase/app");
31+
const { getAuth } = require("firebase/auth");
32+
const firebaseApp1 = initializeApp(firebaseConfig1, 'app1_for_tenantId1');
33+
const firebaseApp2 = initializeApp(firebaseConfig2, 'app2_for_tenantId2');
34+
35+
const auth1 = getAuth(firebaseApp1);
36+
const auth2 = getAuth(firebaseApp2);
37+
38+
auth1.tenantId = "TENANT_ID1";
39+
auth2.tenantId = "TENANT_ID2";
40+
// [END multitenant_switch_tenant_multiinstance]
41+
}
42+
43+
function passwordSignInWithTenantDemo(auth, email, password) {
44+
// [START multitenant_signin_password_demo]
45+
const { signInWithEmailAndPassword, onAuthStateChanged } = require("firebase/auth");
46+
// Switch to TENANT_ID1
47+
auth.tenantId = 'TENANT_ID1';
48+
49+
// Sign in with tenant
50+
signInWithEmailAndPassword(auth, email, password)
51+
.then((userCredential) => {
52+
// User is signed in.
53+
const user = userCredential.user;
54+
// user.tenantId is set to 'TENANT_ID1'.
55+
// Switch to 'TENANT_ID2'.
56+
auth.tenantId = 'TENANT_ID2';
57+
// auth.currentUser still points to the user.
58+
// auth.currentUser.tenantId is 'TENANT_ID1'.
59+
});
60+
61+
// You could also get the current user from Auth state observer.
62+
onAuthStateChanged(auth, (user) => {
63+
if (user) {
64+
// User is signed in.
65+
// user.tenantId is set to 'TENANT_ID1'.
66+
} else {
67+
// No user is signed in.
68+
}
69+
});
70+
// [END multitenant_signin_password_demo]
71+
}
72+
73+
function signUpWithTenant(auth, email, password) {
74+
// [START multitenant_signup_password]
75+
const { createUserWithEmailAndPassword } = require("firebase/auth");
76+
auth.tenantId = 'TENANT_ID';
77+
78+
createUserWithEmailAndPassword(auth, email, password)
79+
.then((userCredential) => {
80+
// User is signed in.
81+
// userCredential.user.tenantId is 'TENANT_ID'.
82+
}).catch((error) => {
83+
// Handle / display error.
84+
// ...
85+
});
86+
// [END multitenant_signup_password]
87+
}
88+
89+
90+
function passwordSignInWithTenant(auth, email, password) {
91+
// [START multitenant_signin_password]
92+
const { signInWithEmailAndPassword } = require("firebase/auth");
93+
auth.tenantId = 'TENANT_ID';
94+
95+
signInWithEmailAndPassword(auth, email, password)
96+
.then((userCredential) => {
97+
// User is signed in.
98+
// userCredential.user.tenantId is 'TENANT_ID'.
99+
}).catch((error) => {
100+
// Handle / display error.
101+
// ...
102+
});
103+
// [END multitenant_signin_password]
104+
}
105+
106+
function samlSignInPopupTenant(auth, provider) {
107+
// [START multitenant_signin_saml_popup]
108+
const { signInWithPopup } = require("firebase/auth");
109+
// Switch to TENANT_ID1.
110+
auth.tenantId = 'TENANT_ID1';
111+
112+
// Sign-in with popup.
113+
signInWithPopup(auth, provider)
114+
.then((userCredential) => {
115+
// User is signed in.
116+
const user = userCredential.user;
117+
// user.tenantId is set to 'TENANT_ID1'.
118+
// Provider data available from the result.user.getIdToken()
119+
// or from result.user.providerData
120+
})
121+
.catch((error) => {
122+
// Handle / display error.
123+
// ...
124+
});
125+
// [END multitenant_signin_saml_popup]
126+
}
127+
128+
function samlSignInRedirectTenant(auth, provider) {
129+
// [START multitenant_signin_saml_redirect]
130+
const { signInWithRedirect, getRedirectResult } = require("firebase/auth");
131+
// Switch to TENANT_ID1.
132+
auth.tenantId = 'TENANT_ID1';
133+
134+
// Sign-in with redirect.
135+
signInWithRedirect(auth, provider);
136+
137+
// After the user completes sign-in and returns to the app, you can get
138+
// the sign-in result by calling getRedirectResult. However, if they sign out
139+
// and sign in again with an IdP, no tenant is used.
140+
getRedirectResult(auth)
141+
.then((result) => {
142+
// User is signed in.
143+
// The tenant ID available in result.user.tenantId.
144+
// Provider data available from the result.user.getIdToken()
145+
// or from result.user.providerData
146+
})
147+
.catch((error) => {
148+
// Handle / display error.
149+
// ...
150+
});
151+
// [END multitenant_signin_saml_redirect]
152+
}
153+
154+
function sendSignInLinkToEmailTenant(auth, email, actionCodeSettings) {
155+
// [START multitenant_send_emaillink]
156+
const { sendSignInLinkToEmail } = require("firebase/auth");
157+
// Switch to TENANT_ID1
158+
auth.tenantId = 'TENANT_ID1';
159+
160+
sendSignInLinkToEmail(auth, email, actionCodeSettings)
161+
.then(() => {
162+
// The link was successfully sent. Inform the user.
163+
// Save the email locally so you don't need to ask the user for it again
164+
// if they open the link on the same device.
165+
window.localStorage.setItem('emailForSignIn', email);
166+
})
167+
.catch((error) => {
168+
// Handle / display error.
169+
// ...
170+
});
171+
// [END multitenant_send_emaillink]
172+
}
173+
174+
function signInWithEmailLinkTenant(auth) {
175+
// [START multitenant_signin_emaillink]
176+
const { isSignInWithEmailLink, parseActionCodeURL, signInWithEmailLink } = require("firebase/auth");
177+
if (isSignInWithEmailLink(auth, window.location.href)) {
178+
const actionCodeUrl = parseActionCodeURL(window.location.href);
179+
if (actionCodeUrl.tenantId) {
180+
auth.tenantId = actionCodeUrl.tenantId;
181+
}
182+
let email = window.localStorage.getItem('emailForSignIn');
183+
if (!email) {
184+
// User opened the link on a different device. To prevent session fixation
185+
// attacks, ask the user to provide the associated email again. For example:
186+
email = window.prompt('Please provide your email for confirmation');
187+
}
188+
// The client SDK will parse the code from the link for you.
189+
signInWithEmailLink(auth, email, window.location.href)
190+
.then((result) => {
191+
// User is signed in.
192+
// tenant ID available in result.user.tenantId.
193+
// Clear email from storage.
194+
window.localStorage.removeItem('emailForSignIn');
195+
});
196+
}
197+
// [END multitenant_signin_emaillink]
198+
}
199+
200+
// Same as the code in auth/ since this is the admin SDK.
201+
function createCustomTokenTenant(admin, uid) {
202+
// [START multitenant_create_custom_token]
203+
// Ensure you're using a tenant-aware auth instance
204+
const tenantManager = admin.auth().tenantManager();
205+
const tenantAuth = tenantManager.authForTenant('TENANT_ID1');
206+
207+
// Create a custom token in the usual manner
208+
tenantAuth.createCustomToken(uid)
209+
.then((customToken) => {
210+
// Send token back to client
211+
})
212+
.catch((error) => {
213+
console.log('Error creating custom token:', error);
214+
});
215+
// [END multitenant_create_custom_token]
216+
}
217+
218+
function signInWithCustomTokenTenant(auth, token) {
219+
// [START multitenant_signin_custom_token]
220+
const { signInWithCustomToken } = require("firebase/auth");
221+
auth.tenantId = 'TENANT_ID1';
222+
223+
signInWithCustomToken(auth, token)
224+
.catch((error) => {
225+
// Handle / display error.
226+
// ...
227+
});
228+
// [END multitenant_signin_custom_token]
229+
}
230+
231+
function linkAccountTenant(auth, provider, email, password) {
232+
// [START multitenant_account_linking]
233+
const { signInWithPopup, EmailAuthProvider, linkWithCredential, SAMLAuthProvider, signInWithCredential } = require("firebase/auth");
234+
// Switch to TENANT_ID1
235+
auth.tenantId = 'TENANT_ID1';
236+
237+
// Sign-in with popup
238+
signInWithPopup(auth, provider)
239+
.then((userCredential) => {
240+
// Existing user with e.g. SAML provider.
241+
const prevUser = userCredential.user;
242+
const emailCredential =
243+
EmailAuthProvider.credential(email, password);
244+
return linkWithCredential(prevUser, emailCredential)
245+
.then((linkResult) => {
246+
// Sign in with the newly linked credential
247+
const linkCredential = SAMLAuthProvider.credentialFromResult(linkResult);
248+
return signInWithCredential(auth, linkCredential);
249+
})
250+
.then((signInResult) => {
251+
// Handle sign in of merged user
252+
// ...
253+
});
254+
})
255+
.catch((error) => {
256+
// Handle / display error.
257+
// ...
258+
});
259+
// [END multitenant_account_linking]
260+
}
261+
262+
function accountExistsPopupTenant(auth, samlProvider, googleProvider, goToApp) {
263+
// [START multitenant_account_exists_popup]
264+
const { signInWithPopup, fetchSignInMethodsForEmail, linkWithCredential } = require("firebase/auth");
265+
// Step 1.
266+
// User tries to sign in to the SAML provider in that tenant.
267+
auth.tenantId = 'TENANT_ID';
268+
signInWithPopup(auth, samlProvider)
269+
.catch((error) => {
270+
// An error happened.
271+
if (error.code === 'auth/account-exists-with-different-credential') {
272+
// Step 2.
273+
// User's email already exists.
274+
// The pending SAML credential.
275+
const pendingCred = error.credential;
276+
// The credential's tenantId if needed: error.tenantId
277+
// The provider account's email address.
278+
const email = error.email;
279+
// Get sign-in methods for this email.
280+
fetchSignInMethodsForEmail(email, auth)
281+
.then((methods) => {
282+
// Step 3.
283+
// Ask the user to sign in with existing Google account.
284+
if (methods[0] == 'google.com') {
285+
signInWithPopup(auth, googleProvider)
286+
.then((result) => {
287+
// Step 4
288+
// Link the SAML AuthCredential to the existing user.
289+
linkWithCredential(result.user, pendingCred)
290+
.then((linkResult) => {
291+
// SAML account successfully linked to the existing
292+
// user.
293+
goToApp();
294+
});
295+
});
296+
}
297+
});
298+
}
299+
});
300+
// [END multitenant_account_exists_popup]
301+
}
302+
303+
function accountExistsRedirectTenant(auth, samlProvider, googleProvider, goToApp) {
304+
// [START multitenant_account_exists_redirect]
305+
const { signInWithRedirect, getRedirectResult, fetchSignInMethodsForEmail, linkWithCredential } = require("firebase/auth");
306+
// Step 1.
307+
// User tries to sign in to SAML provider.
308+
auth.tenantId = 'TENANT_ID';
309+
signInWithRedirect(auth, samlProvider);
310+
var pendingCred;
311+
// Redirect back from SAML IDP. auth.tenantId is null after redirecting.
312+
getRedirectResult(auth).catch((error) => {
313+
if (error.code === 'auth/account-exists-with-different-credential') {
314+
// Step 2.
315+
// User's email already exists.
316+
const tenantId = error.tenantId;
317+
// The pending SAML credential.
318+
pendingCred = error.credential;
319+
// The provider account's email address.
320+
const email = error.email;
321+
// Need to set the tenant ID again as the page was reloaded and the
322+
// previous setting was reset.
323+
auth.tenantId = tenantId;
324+
// Get sign-in methods for this email.
325+
fetchSignInMethodsForEmail(auth, email)
326+
.then((methods) => {
327+
// Step 3.
328+
// Ask the user to sign in with existing Google account.
329+
if (methods[0] == 'google.com') {
330+
signInWithRedirect(auth, googleProvider);
331+
}
332+
});
333+
}
334+
});
335+
336+
// Redirect back from Google. auth.tenantId is null after redirecting.
337+
getRedirectResult(auth).then((result) => {
338+
// Step 4
339+
// Link the SAML AuthCredential to the existing user.
340+
// result.user.tenantId is 'TENANT_ID'.
341+
linkWithCredential(result.user, pendingCred)
342+
.then((linkResult) => {
343+
// SAML account successfully linked to the existing
344+
// user.
345+
goToApp();
346+
});
347+
});
348+
// [END multitenant_account_exists_redirect]
349+
}

0 commit comments

Comments
 (0)