Organizations & RBAC
Organizations
Multi-tenant workspaces with members, roles, and invitations — the foundation for B2B applications.
Organizations give your application multi-tenancy — the ability for users to create and belong to multiple workspaces. Whether you call them teams, companies, or projects, organizations are the building block for any B2B SaaS where users collaborate within shared workspaces.
Banata Auth uses an explicit organization creation model:
- Organizations are created when you or your users call the create organization endpoint.
- The creator is automatically assigned a configurable role (defaults to
super_admin). - Role assignment is driven by your project's role catalog.
Enabling Organizations
1. Turn it on in the dashboard
Open your project in the Banata dashboard and navigate to Authentication > Methods. Toggle Organization to enabled.
2. Configure options in your SDK client
Once organizations are enabled in the dashboard, you can fine-tune behavior through the SDK configuration:
import { createAuthClient } from "@banata-auth/react";
const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_AUTH_URL!,
// Organization options are configured in the dashboard,
// but you can override them in the SDK if needed:
plugins: [],
});Configuration Options
These options are available in the dashboard under Authentication > Methods > Organization:
| Option | Type | Default | Description |
|---|---|---|---|
allowUserToCreateOrg | boolean | true | Whether users can create organizations |
creatorRole | string | "super_admin" | Role assigned to the organization creator |
maxOrganizations | number | (unset) | Maximum number of organizations a user can create |
Data Model
Organization
interface Organization {
id: string; // e.g., "org_01HXYZ..."
name: string; // Display name
slug: string; // URL-friendly identifier (unique)
logo?: string; // Logo URL
metadata?: Record<string, unknown>; // Custom data (max 16KB)
createdAt: string; // ISO 8601 timestamp
updatedAt: string;
}Member
interface Member {
id: string;
userId: string;
organizationId: string;
role: string; // "super_admin" by default, then custom roles
createdAt: string;
}Invitation
Invitations track pending membership offers. For the full invitation lifecycle, see the Invitations guide.
Client-Side API
All client-side operations use the authClient you set up during installation.
Create an Organization
import { authClient } from "@/lib/auth-client";
const { data: org, error } = await authClient.organization.create({
name: "Acme Corp",
slug: "acme-corp", // Optional — auto-generated from name if omitted
logo: "https://example.com/logo.png", // Optional
metadata: { plan: "pro", industry: "tech" }, // Optional
});List User's Organizations
const { data: orgs } = await authClient.organization.list();
// orgs = [{ id, name, slug, logo, ... }, ...]Set Active Organization
When a user belongs to multiple organizations, they switch between them. The active organization is stored in the session:
await authClient.organization.setActive({
organizationId: "org_01HXYZ...",
});Update an Organization
await authClient.organization.update({
organizationId: "org_01HXYZ...",
data: {
name: "Acme Corporation",
logo: "https://example.com/new-logo.png",
metadata: { plan: "enterprise" },
},
});Delete an Organization
await authClient.organization.delete({
organizationId: "org_01HXYZ...",
});
// All members and invitations are also deletedReact Hooks
useOrganization()
Returns the current active organization and loading state:
import { useOrganization } from "@banata-auth/react";
const {
organization, // Organization | null — the active org
isLoading, // boolean — true while fetching
} = useOrganization();useBanataAuth()
The full auth context includes organization data alongside user and session:
import { useBanataAuth } from "@banata-auth/react";
const {
user,
session,
organization, // Active organization
isLoading,
setActiveOrganization, // Function to switch orgs
} = useBanataAuth();Member Management
List Members
const { data: members } = await authClient.organization.listMembers({
organizationId: "org_01HXYZ...",
});
// members = [{ id, userId, role, user: { name, email, image } }, ...]Add a Member
await authClient.organization.addMember({
organizationId: "org_01HXYZ...",
userId: "usr_01HXYZ...",
role: "super_admin", // or any custom role slug
});Update Member Role
await authClient.organization.updateMemberRole({
organizationId: "org_01HXYZ...",
memberId: "mem_01HXYZ...",
role: "sandbox_operator",
});Remove a Member
await authClient.organization.removeMember({
organizationId: "org_01HXYZ...",
memberId: "mem_01HXYZ...",
});Organization Switcher Component
A common UI pattern is an organization switcher in your navigation. Here is a minimal example:
"use client";
import { useBanataAuth } from "@banata-auth/react";
import { authClient } from "@/lib/auth-client";
import { useState, useEffect } from "react";
export function OrgSwitcher() {
const { organization, user } = useBanataAuth();
const [orgs, setOrgs] = useState([]);
const [open, setOpen] = useState(false);
useEffect(() => {
authClient.organization.list().then(({ data }) => {
setOrgs(data ?? []);
});
}, []);
async function switchOrg(orgId: string) {
await authClient.organization.setActive({ organizationId: orgId });
setOpen(false);
window.location.reload(); // Refresh to load new org context
}
return (
<div style={{ position: "relative" }}>
<button onClick={() => setOpen(!open)}>
{organization?.name ?? "Select Organization"}
</button>
{open && (
<div style={{
position: "absolute",
top: "100%",
left: 0,
background: "#fff",
border: "1px solid #ddd",
borderRadius: "8px",
padding: "4px",
minWidth: "200px",
}}>
{orgs.map((org) => (
<button
key={org.id}
onClick={() => switchOrg(org.id)}
style={{
display: "block",
width: "100%",
textAlign: "left",
padding: "8px 12px",
background: org.id === organization?.id ? "#f0f0f0" : "transparent",
}}
>
{org.name}
</button>
))}
</div>
)}
</div>
);
}Server-Side Management (Admin SDK)
You can manage organizations from your backend using the admin SDK. This is useful for admin panels, automated provisioning, or server-side workflows:
import { BanataAuth } from "@banata-auth/sdk";
const banata = new BanataAuth({
apiKey: "your-api-key",
baseUrl: "https://auth.banata.dev",
});
// Create an organization
const org = await banata.organizations.createOrganization({
name: "Enterprise Corp",
slug: "enterprise-corp",
});
// List all organizations (paginated)
const { data, listMetadata } = await banata.organizations.listOrganizations({
limit: 20,
});
// Get a specific organization
const details = await banata.organizations.getOrganization({
organizationId: "org_01HXYZ...",
});
// Update an organization
await banata.organizations.updateOrganization({
organizationId: "org_01HXYZ...",
name: "Updated Name",
});
// Manage members
const members = await banata.organizations.listMembers({
organizationId: "org_01HXYZ...",
});
await banata.organizations.addMember({
organizationId: "org_01HXYZ...",
userId: "usr_01HXYZ...",
role: "super_admin",
});
await banata.organizations.removeMember({
organizationId: "org_01HXYZ...",
memberId: "mem_01HXYZ...",
});Invitation Management
You can send, accept, and revoke invitations from the client SDK. For the complete invitation lifecycle, email template configuration, and advanced flows, see the dedicated Invitations guide.
Send an Invitation
await authClient.organization.inviteMember({
organizationId: "org_01HXYZ...",
email: "newuser@example.com",
role: "sandbox_viewer",
});
// Banata sends the invitation email automatically using the provider and template
// configured in your dashboard under Emails > Providers and Email TemplatesAccept an Invitation
await authClient.organization.acceptInvitation({
invitationId: "inv_01HXYZ...",
});Revoke an Invitation
await authClient.organization.revokeInvitation({
invitationId: "inv_01HXYZ...",
});Next Steps
- Invitations — Complete invitation lifecycle and email configuration
- Roles & Permissions — Fine-grained access control within organizations
- Webhooks — Get notified about organization events