Skip to main content

Subdomain login

Authenticate users across different tenants through different subdomains.

Overview

This guide shows you how to authenticate users through different subdomains. The authentication method that displayed on each page varies based on tenant configuration.

important

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

An example app for this setup with the pre-built UI is available on the GitHub example directory. 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

Before you start

The tutorial assumes that you already have a working application integrated with SuperTokens. If you have not, please check the Quickstart Guide.

You also need to create the tenants that are required by your application. View the previous tutorial for more information on how to do this.

Steps

1. Change CORS setting and websiteDomain

You have to create tenants before you can complete this step.

1.1 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.

1.2 websiteDomain setup

Set the websiteDomain to window.location.origin in the frontend SDK initialization step. On the backend, update the websiteDomain to be the main domain (example.com if your sub domains are sub.example.com). Then 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 the docs for how to override the sendEmail function.

2. 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 tells the frontend SDK that the login page relies on the tenantId and to fetch the tenant configuration from the backend before showing any login UI.
  2. Initialize the Multitenancy recipe and provide getTenantId configuration function.

App Info

Adjust these values based on the application that you are trying to configure. To learn more about what each field means check the references page.
This is the URL of your app's API server.
This is the URL of your app's API server.
SuperTokens will expose it's APIs scoped by this base API path.
This is the URL of your website.
The path where the login UI will be rendered
import React from 'react';

import SuperTokens, { SuperTokensWrapper } from "supertokens-auth-react";
import ThirdParty from "supertokens-auth-react/recipe/thirdparty";
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: [
// Other recipes..
Multitenancy.init({
override: {
functions: (oI) => {
return {
...oI,
getTenantId: async () => {
// We treat the sub domain as the tenant ID
return window.location.host.split('.')[0]
}
}
}
},
})
]
});

3. Restrict subdomain access

Restrict which sub domains the user has access to. To do this configure the SDK to know 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 configuration above tells SuperTokens to add the list of domains returned by you into the user's session claims once they login. This way the SDK will access the claim on the frontend and backend to limit a user only to the right subdomains.

4. Share sessions across sub domains Optional

If you need to allow users to have the same session across multiple subdomains you have to update the configuration. Set the sessionTokenFrontendDomain value in the Session recipe to enable this behavior. 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

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 is not a security issue because access restricts users based on their domain allow list as shown below.

5. Limit user access to their sub domain.

Use session claim validators on the frontend to restrict sub domain access. Before proceeding, ensure that you have defined the GetAllowedDomainsForTenantId function mentioned above. This adds the list of allowed domains into the user's access token payload.

On the frontend, check if the tenant has access to the current sub domain. If not, redirect them to the right sub domain. This is possible 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, add the hasAccessToCurrentDomain claim validator to the global validators. This means that whenever you check protect a route, it checks if hasAccessToCurrentDomain has passed. If not, SuperTokens redirects the user to their right sub domain (via the values set in the AllowedDomainsClaim session claim).

See also