Skip to main content
Which UI do you use?
Custom UI
Pre built UI
Paid Feature

This is a paid feature.

For self hosted users, Sign up to get a license key and follow the instructions sent to you by email. Creation of tenants is free on the dev license key.

This feature is already enabled for managed service users. Creation of additional tenant is free on the provided development environment.

Example 2: Tenants use their sub domain to login

In this UX flow, all tenants login using their assigned sub domain (customer1.example.com, customer2.example.com and so on). The login method that's shown on the login page on each sub domain depends on that tenant's tenantId configuration.

important

Throughout this page, we will assume that the tenant ID for a tenant is equal to their sub domain - so if the sub domain assigned to a tenant is customer1.example.com, then their tenantId is customer1.

An example app for this setup with our pre built UI can be found on our github example dir. The app is setup to have three tenants:

  • tenant1.example.com: Login with emailpassword + Google sign in
  • tenant2.example.com: Login with emailPassword
  • tenant3.example.com: Login with passwordless + Github sign in

Step 1: Creating a new tenant#

Whenever you want to onboard a new customer, you should create and configure a tenantId for them in the SuperTokens core.

Step 2: Change CORS setting and websiteDomain#

CORS setup#

  • In order for the browser to be able to make requests to the backend, the CORS setting on the backend needs to reflect the right set of allowed origins. For example, if you have customer1.example.com on the frontend, then the CORS setting on the backend should allow customer1.example.com as an allowed origin. You can specifically whitelist the set of frontend sub domains on the backend, or you can use a regex like *.example.com.

websiteDomain setup#

  • On the frontend, set the websiteDomain to window.location.origin
  • On the backend, you should set the websiteDomain to be your main domain (example.com if your sub domains are sub.example.com), and then you want to override the sendEmail functions to change the domain of the link dynamically based on the tenant ID supplied to the sendEmail function. See the Email Delivery section in our docs for how to override the sendEmail function.

Step 3: Load login methods dynamically on the frontend based on the tenantId#

Modify the SuperTokens.init to do the following:

  1. Set the usesDynamicLoginMethods to true. This will tell our frontend SDK that the login page is based on the tenantId and to fetch the tenant config from the backend before showing any login UI.
  2. Initialize the Multitenancy recipe and provide getTenantId config function.
import React from 'react';

import SuperTokens, { SuperTokensWrapper } from "supertokens-auth-react";
import ThirdPartyEmailPassword from "supertokens-auth-react/recipe/thirdpartyemailpassword";
import Session from "supertokens-auth-react/recipe/session";
import Multitenancy from "supertokens-auth-react/recipe/multitenancy";

SuperTokens.init({
appInfo: {
appName: "<YOUR_APP_NAME>",
apiDomain: "<YOUR_API_DOMAIN>",
websiteDomain: "<YOUR_WEBSITE_DOMAIN>",
apiBasePath: "/auth",
websiteBasePath: "/auth"
},
usesDynamicLoginMethods: true,
recipeList: [
ThirdPartyEmailPassword.init({
//...
}),
Session.init(),
Multitenancy.init({
override: {
functions: (oI) => {
return {
...oI,
getTenantId: async () => {
// We treat the sub domain as the tenant ID
return window.location.host.split('.')[0]
}
}
}
},
})
]
});

Step 4: Tell SuperTokens about tenant's sub domains#

We want to restrict users to only be able to access their (sub)domains. SuperTokens makes it easy for you to do this. We start by telling SuperTokens which domain each tenantId has access to:

import SuperTokens from "supertokens-node";
import Multitenancy from "supertokens-node/recipe/multitenancy"

SuperTokens.init({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
recipeList: [
Multitenancy.init({
getAllowedDomainsForTenantId: async (tenantId, userContext) => {
// query your db to get the allowed domain for the input tenantId
// or you can make the tenantId equal to the sub domain itself
return [tenantId + ".myapp.com", "myapp.com", "www.myapp.com"]
}
}),
// other recipes...
]
})

The config above will tell SuperTokens to add the list of domains returned by you into the user's session claims once they login. This claim can then be read on the frontend and backend to restrict user's access to the right domain(s).

Step 5: Sharing sessions across sub domains#

You may want to allow the user's session to be shearable across sub domains. This would lead to a better UX in which even if they visit the main domain (logged in via a.example.com, and visit example.com), the frontend app there can detect if the user has a session or not.

This can be achieved by setting the sessionTokenFrontendDomain value in the Session recipe.

If the sub domain and the main website domain have different backends (on different sub domains), then you can also enable sharing of sessions across API domains.

note

This is not a security issue because we will anyway be restricting access to users based on their domain allow list as shown below.

Step 6: Limiting the user's access to their sub domain.#

We will be using session claim validators on the frontend to restrict sub domain access. Before proceeding, make sure that you have defined the GetAllowedDomainsForTenantId function mentioned above. This will add the list of allowed domains into the user's access token payload.

On the frontend, we want to check if the tenant has access to the current sub domain. If not, we want to redirect them to the right sub domain. This can be done by using the hasAccessToCurrentDomain session validator from the multi tenancy recipe:

import React from "react";
import Session from 'supertokens-auth-react/recipe/session';
import { AllowedDomainsClaim } from 'supertokens-auth-react/recipe/multitenancy';

Session.init({
override: {
functions: (oI) => ({
...oI,
getGlobalClaimValidators: ({ claimValidatorsAddedByOtherRecipes }) => [
...claimValidatorsAddedByOtherRecipes,
{
...AllowedDomainsClaim.validators.hasAccessToCurrentDomain(),
onFailureRedirection: async () => {
let claimValue = await Session.getClaimValue({
claim: AllowedDomainsClaim,
});
return "https://" + claimValue![0];
},
},
],
}),
},
})

Above, in Session.init on the frontend, we add the hasAccessToCurrentDomain claim validator to the global validators. This means, that whenever we check protect a route, it will check if hasAccessToCurrentDomain has passed, and if not, SuperTokens will redirect to the user to their right sub domain (via the values set in the AllowedDomainsClaim session claim).