Skip to main content

Multiple Tenant Guide

In the previous guide, we saw how to integrate SuperTokens with SAML Jackson for a single tenant.

1) Add multiple tenants to SAML Jackson#

We must first add each tenant to SAML Jackson. This can be done via the addTenant.sh script in the example repo:

addTenant.sh tenant1
addTenant.sh tenant2
important

Make sure to change the values of defaultRedirectUrl, redirectUrl, product, name and description in the above script to match your app's details.

Each tenant will have its own unique client_id and client_secret which you must store in your database mapped to the tenant's ID (tenant1 or tenant2) in the above example.

You can either add them yourself, or let end users add their own tenant. If you let end users add their own tenant, then you have to allow them to upload their SAML provider's XML file via a UI and then relay that to SAML Jackson.

2) Allow end-users to select their tentant during login#

There are two ways of doing this:

Asking users to type in their tenant ID during login.#

In our example app, you can see that we use the React component override feature to insert an input box above the "login with SAML" button which allows users to type in their tenant ID during sign in / up.

note

The example app only accepts tenant IDs that you have pre configured with SAML Jackson.

Assigning a sub domain to each user and associating a sub domain to their tenant.#

In this approach, you can map a sub domain to a tenant ID in your database, and then use the API override feature to fetch the request's origin, extract the sub domain from it and query your database to get the associated tenant ID.

3) Fetch the client_id and client_secret for the tenant ID#

Once you have the tenantID on the backend API, you can query your database to fetch the associated client_id and client_secret. These values are the same as what were obtained from Step (1) on this page.

You should fetch these values in the API overrides associated with signing in / up the user.

4) Pass the client_id and client_secret to the custom auth provider logic via userContext#

Using the user context feature, you can save the client_id and client_secret in the userContext object in the sign in / up API before calling the original implementation. These values will then be available in the userContext object passed to your custom OAuth provider implementation:

import axios from "axios";
import SuperTokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword";

SuperTokens.init({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
supertokens: {
connectionURI: "...",
},
recipeList: [
ThirdPartyEmailPassword.init({
providers: [
{
id: "saml-jackson",
get: (redirectURI, authCodeFromRequest, userContext) => {
/*
userContext.client_id and userContext.client_secret needs to be
set in the API override for sign in up based on the user's input
*/
let client_id = userContext.client_id;
let client_secret = userContext.client_secret;
return {
accessTokenAPI: {
url: `http://localhost:5225/api/oauth/token`,
params: {
client_id,
client_secret,
grant_type: "authorization_code",
redirect_uri: redirectURI || "",
code: authCodeFromRequest || "",
}
},
authorisationRedirect: {
url: `http://localhost:5225/api/oauth/authorize`,
params: {
client_id
}
},
getClientId: () => {
return client_id;
},
getProfileInfo: async (accessTokenAPIResponse) => {
const profile = await axios({
method: 'get',
url: `http://localhost:5225/api/oauth/userinfo`,
headers: {
Authorization: `Bearer ${accessTokenAPIResponse.access_token}`,
},
});
return {
id: profile.data.id,
email: {
id: profile.data.email,
isVerified: true
}
};
}
}
}
}
]
}),
Session.init({})
]
});