Skip to content
Esc
navigateopen⌘Jpreview
Dashboard
On this page

Tenant discovery

Automatically discover and route users to appropriate tenants based on their email domains

Overview

This tutorial shows you how to add tenant discovery functionality to your SuperTokens authentication flows. The guide makes use of the plugins functionality which automatically discovers and routes users to the appropriate tenants based on their email domains.

How it works

The plugin extracts the domain from user email addresses to infer the tenant ID. For example, user@company.com would be routed to the company tenant. The system includes built-in protection against popular email providers and falls back to the public tenant when appropriate.

Before you start

The tenant discovery plugin supports only the React and Node.js SDKs. Support for other platforms is under active development. Besides initializing the plugin, you also have to configure multi-tenancy in your SuperTokens setup.

Steps

1. Initialize the backend plugin

1.1 Install the plugin

npm install @supertokens-plugins/tenant-discovery-nodejs

1.2 Update your backend SDK configuration

import SuperTokens from "supertokens-node";
import TenantDiscoveryPlugin from "@supertokens-plugins/tenant-discovery-nodejs";

SuperTokens.init({
  appInfo: {
    appName: "My app",
    apiDomain: "https://api.example.com",
  },
  recipeList: [
    // your other recipes
  ],
  experimental: {
    plugins: [
      TenantDiscoveryPlugin.init({
        enableTenantListAPI: false, // Optional: defaults to false
      }),
    ],
  },
});

2. Initialize the frontend plugin

2.1 Install the plugin

npm install @supertokens-plugins/tenant-discovery-react

2.2 Update your frontend SDK configuration

import SuperTokens from "supertokens-auth-react";
import TenantDiscoveryPlugin from "@supertokens-plugins/tenant-discovery-react";

SuperTokens.init({
  appInfo: {
    appName: "My app",
    apiDomain: "https://api.example.com",
    websiteDomain: "https://example.com",
  },
  recipeList: [
    // your recipes
  ],
  experimental: {
    plugins: [
      TenantDiscoveryPlugin.init({
        showTenantSelector: true, // Optional: defaults to true
        extractTenantIdFromDomain: true, // Optional: defaults to true
      }),
    ],
  },
});

3. Use tenant discovery

With this configuration, when a user tries to sign in, the system automatically determines their tenant based on the email domain. Hence, you don’t need to change anything else to make it work.

Email authentication form

If you want to customize the user interface experience the plugin also provides other options.

Tenant selection interface

You can use the tenant selection interface accessible at /tenant-discovery/select. This page displays all available tenants and allows users to choose their organization before proceeding with authentication.

Customization

Block emails from specific tenants

You can override the default tenant assignment logic to prevent certain emails from accessing specific tenants:

import TenantDiscoveryPlugin from "@supertokens-plugins/tenant-discovery-nodejs";

TenantDiscoveryPlugin.init({
  enableTenantListAPI: false,
  override: (originalImplementation) => ({
    ...originalImplementation,
    isTenantAllowedForEmail: (email: string, tenantId: string) => {
      // Prevent routing to public tenant
      return tenantId !== "public";
    },
  }),
});

Add custom domain restrictions

Extend the list of restricted domains that should always use the public tenant:

TenantDiscoveryPlugin.init({
  override: (originalImplementation) => ({
    ...originalImplementation,
    isRestrictedEmailDomain: (emailDomain: string) => {
      return originalImplementation.isRestrictedEmailDomain(emailDomain) || emailDomain === "example.com";
    },
  }),
});

Implement a custom user interface

To create a custom tenant discovery interface, use the usePluginContext hook:

import { useState } from "react";
import { usePluginContext } from "@supertokens-plugins/tenant-discovery-react";

function CustomTenantDiscovery() {
  const { api, functions } = usePluginContext();
  const [email, setEmail] = useState("");
  const [tenants, setTenants] = useState<Array<{ tenantId: string }>>([]);

  const handleEmailSubmit = async () => {
    const result = await api.tenantIdFromEmail(email);
    if (result.status === "OK") {
      functions.setEmailId(email);
      functions.setTenantId(result.tenant);
    }
  };

  const loadTenants = async () => {
    const response = await api.fetchTenants();
    if (response.status === "OK") {
      setTenants(response.tenants);
    }
  };

  return (
    <div>
      <h2>Enter your email to find your organization</h2>
      <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="user@company.com" />
      <button onClick={handleEmailSubmit}>Continue</button>

      <h3>Or choose from available organizations:</h3>
      <button onClick={loadTenants}>Load Organizations</button>
      {tenants.map((tenant) => (
        <button key={tenant.tenantId} onClick={() => functions.setTenantId(tenant.tenantId)}>
          {tenant.tenantId === "public" ? "Personal Account" : tenant.tenantId}
        </button>
      ))}
    </div>
  );
}

Next steps

Besides tenant discovery, you can also explore other enterprise authentication features:

API reference

API schema and response details