Skip to content

Commit e2a9d33

Browse files
authored
add multi-tenancy snippets (#250)
1 parent 20d5d98 commit e2a9d33

17 files changed

+1047
-0
lines changed

auth-next/multi-tenancy.js

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

0 commit comments

Comments
 (0)