Banata

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:

typescript
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:

OptionTypeDefaultDescription
allowUserToCreateOrgbooleantrueWhether users can create organizations
creatorRolestring"super_admin"Role assigned to the organization creator
maxOrganizationsnumber(unset)Maximum number of organizations a user can create

Data Model

Organization

typescript
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

typescript
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

typescript
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

typescript
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:

typescript
await authClient.organization.setActive({
  organizationId: "org_01HXYZ...",
});

Update an Organization

typescript
await authClient.organization.update({
  organizationId: "org_01HXYZ...",
  data: {
    name: "Acme Corporation",
    logo: "https://example.com/new-logo.png",
    metadata: { plan: "enterprise" },
  },
});

Delete an Organization

typescript
await authClient.organization.delete({
  organizationId: "org_01HXYZ...",
});
// All members and invitations are also deleted

React Hooks

useOrganization()

Returns the current active organization and loading state:

typescript
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:

typescript
import { useBanataAuth } from "@banata-auth/react";
 
const {
  user,
  session,
  organization,             // Active organization
  isLoading,
  setActiveOrganization,    // Function to switch orgs
} = useBanataAuth();

Member Management

List Members

typescript
const { data: members } = await authClient.organization.listMembers({
  organizationId: "org_01HXYZ...",
});
// members = [{ id, userId, role, user: { name, email, image } }, ...]

Add a Member

typescript
await authClient.organization.addMember({
  organizationId: "org_01HXYZ...",
  userId: "usr_01HXYZ...",
  role: "super_admin",  // or any custom role slug
});

Update Member Role

typescript
await authClient.organization.updateMemberRole({
  organizationId: "org_01HXYZ...",
  memberId: "mem_01HXYZ...",
  role: "sandbox_operator",
});

Remove a Member

typescript
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:

tsx
"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:

typescript
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

typescript
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 Templates

Accept an Invitation

typescript
await authClient.organization.acceptInvitation({
  invitationId: "inv_01HXYZ...",
});

Revoke an Invitation

typescript
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