
Research
Shai-Hulud Descends to Hades: Miasma Worm Campaign Spreads with New PyPI Wave
Socket found 37 malicious PyPI wheels that abuse Python startup hooks to launch a Bun-powered credential stealer tied to Mini Shai-Hulud/Miasma.
@supertokens-plugins/tenants-react
Advanced tools
Add multi-tenancy management UI to your SuperTokens React application. This plugin provides comprehensive tenant management interfaces including tenant switching, user management, invitations, and join requests.
npm install @supertokens-plugins/tenants-react
Initialize the plugin in your SuperTokens frontend configuration:
import SuperTokens from "supertokens-auth-react";
import ProfileBasePlugin from "@supertokens-plugins/profile-base-react";
import TenantsPlugin from "@supertokens-plugins/tenants-react";
SuperTokens.init({
appInfo: {
// your app info
},
recipeList: [
// your recipes (Session recipe is required)
],
experimental: {
plugins: [
// Profile base plugin is required
ProfileBasePlugin.init(),
TenantsPlugin.init({
requireTenantCreation: false, // Optional: defaults to false
redirectToUrlOnJoiningTenant: "/", // Optional: defaults to "/"
}),
],
},
});
[!IMPORTANT] You also have to install and configure the backend plugin
@supertokens-plugins/tenants-nodejsand the profile base plugin@supertokens-plugins/profile-base-react.
| Option | Type | Default | Description |
|---|---|---|---|
requireTenantCreation | boolean | false | Whether users must create a tenant before accessing the app |
redirectToUrlOnJoiningTenant | string or (() => void) | "/" | URL to redirect to or function that does the redirection after successfully joining tenants |
The plugin automatically integrates with the profile base plugin to provide:
The plugin provides the following pre-built pages:
Comprehensive tenant management interface including:
Admin interface for managing tenant creation requests.
Access plugin functionality and API methods:
import { usePluginContext } from "@supertokens-plugins/tenants-react";
function MyTenantComponent() {
const { api, pluginConfig, t } = usePluginContext();
const handleFetchTenants = async () => {
try {
const response = await api.fetchTenants();
if (response.status === "OK") {
console.log("Tenants:", response.tenants);
}
} catch (error) {
console.error("Failed to fetch tenants:", error);
}
};
return (
<div>
<h2>{t("PL_TD_SELECT_TENANT_TITLE")}</h2>
<button onClick={handleFetchTenants}>Load Tenants</button>
</div>
);
}
The plugin provides these API methods through the usePluginContext hook:
Retrieve all available tenants for the current user:
const { api } = usePluginContext();
const result = await api.fetchTenants();
if (result.status === "OK") {
console.log("Tenants:", result.tenants);
// [{ id: "tenant1", name: "My Tenant", role: "admin" }]
}
Create a new tenant:
const { api } = usePluginContext();
const result = await api.createTenant({
name: "My New Tenant",
firstFactors: ["emailpassword", "thirdparty"], // optional
});
if (result.status === "OK") {
console.log("Tenant created:", result.pendingApproval);
}
Switch to a different tenant:
const { api } = usePluginContext();
const result = await api.switchTenant("tenant1");
if (result.status === "OK") {
console.log("Switched to tenant successfully");
// Session is now in the context of tenant1
}
Join a tenant:
const { api } = usePluginContext();
const result = await api.joinTenant({
tenantId: "tenant1",
});
if (result.status === "OK") {
console.log("Joined tenant successfully");
}
Get all users in the current tenant:
const { api } = usePluginContext();
const result = await api.getUsers();
if (result.status === "OK") {
console.log("Users:", result.users);
// [{ id: "user1", emails: ["user@example.com"], roles: ["admin"] }]
}
Change a user's role in the current tenant:
const { api } = usePluginContext();
const result = await api.changeRole("user123", "tenant-member");
if (result.status === "OK") {
console.log("Role changed successfully");
}
Remove a user from the current tenant:
const { api } = usePluginContext();
const result = await api.removeUserFromTenant("user123");
if (result.status === "OK") {
console.log("User removed successfully");
}
Invite a user to the tenant:
const { api } = usePluginContext();
const result = await api.addInvitation("user@example.com", "tenant-member");
if (result.status === "OK") {
console.log("Invitation code:", result.code);
}
Get all pending invitations:
const { api } = usePluginContext();
const result = await api.getInvitations();
if (result.status === "OK") {
console.log("Invitations:", result.invitees);
}
Remove a pending invitation:
const { api } = usePluginContext();
const result = await api.removeInvitation("user@example.com");
if (result.status === "OK") {
console.log("Invitation removed");
}
Accept an invitation to join a tenant:
const { api } = usePluginContext();
const result = await api.acceptInvitation("invitation-code", "tenant1");
if (result.status === "OK") {
console.log("Invitation accepted");
}
Get all pending join requests for the current tenant:
const { api } = usePluginContext();
const result = await api.getOnboardingRequests();
if (result.status === "OK") {
console.log("Join requests:", result.users);
}
Approve a join request:
const { api } = usePluginContext();
const result = await api.acceptOnboardingRequest("user123");
if (result.status === "OK") {
console.log("Request approved");
}
Reject a join request:
const { api } = usePluginContext();
const result = await api.declineOnboardingRequest("user123");
if (result.status === "OK") {
console.log("Request declined");
}
Get all pending tenant creation requests (admin only):
const { api } = usePluginContext();
const result = await api.getCreationRequests();
if (result.status === "OK") {
console.log("Creation requests:", result.requests);
}
Approve a tenant creation request (admin only):
const { api } = usePluginContext();
const result = await api.acceptCreationRequest("request123");
if (result.status === "OK") {
console.log("Request approved");
}
Reject a tenant creation request (admin only):
const { api } = usePluginContext();
const result = await api.declineCreationRequest("request123");
if (result.status === "OK") {
console.log("Request declined");
}
You can customize the default pages by providing your own components:
import TenantsPlugin from "@supertokens-plugins/tenants-react";
import { CustomSelectTenant, CustomTenantManagement } from "./your-custom-components";
SuperTokens.init({
// ... other config
experimental: {
plugins: [
TenantsPlugin.init({
override: (oI) => ({
...oI,
pages: (originalPages) => ({
...originalPages,
selectTenant: CustomSelectTenant,
tenantManagement: CustomTenantManagement,
}),
}),
}),
],
},
});
This plugin automatically integrates with the profile base plugin by:
The plugin comes with built-in translations for English. Translation keys are available through the t function in usePluginContext:
const { t } = usePluginContext();
console.log(t("PL_TD_SELECT_TENANT_TITLE")); // "Select Tenant"
console.log(t("PL_TD_CREATE_TENANT_BUTTON")); // "Create Tenant"
The plugin automatically registers the following routes:
/user/tenants/create - Tenant creation page/user/invite/accept - Invite accept pageDifferent features require different permissions:
| Feature | Permission Required |
|---|---|
| List tenants | tenant-access |
| Switch tenant | tenant-access |
| Create tenant | None (configurable) |
| List users | list-users |
| Change user roles | change-user-roles |
| Remove users | remove-users |
| Manage invitations | manage-invitations |
| Manage join requests | manage-join-requests |
| Manage creation requests | manage-create-requests |
The plugin automatically handles session context switching when users switch tenants. After switching tenants:
redirectToUrlOnJoiningTenantAll API methods return a standardized response format:
// Success response
{ status: "OK", ...data }
// Error response
{ status: "ERROR", message: "Error description" }
Always check the status field before accessing response data:
const result = await api.fetchTenants();
if (result.status === "OK") {
// Handle success
console.log(result.tenants);
} else {
// Handle error
console.error(result.message);
}
See the main repository for license information.
FAQs
Tenants Base Plugin for SuperTokens
We found that @supertokens-plugins/tenants-react demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
Socket found 37 malicious PyPI wheels that abuse Python startup hooks to launch a Bun-powered credential stealer tied to Mini Shai-Hulud/Miasma.

Security News
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.

Security News
pnpm 11.5 now recognizes npm staged publish approvals in release metadata, preventing those releases from being mistaken for lower-trust package publishes.