HelloJohn / docs
Organizations

Switching Organizations

How users switch between organizations and how the active organization is reflected in JWT tokens.

Switching Organizations

Users who belong to multiple organizations can switch between them. The active organization determines the JWT claims (org_id, role) sent in API requests.

How Organization Switching Works

Switching organizations updates the JWT claims. HelloJohn issues a new access token scoped to the selected organization.

User selects Org B
→ SDK requests new token for Org B
→ New access token contains org_id: "org_B", role: "admin"
→ All subsequent requests use this token

Switching via SDK

import { useHelloJohn } from "@hellojohn/react";

function OrgSwitcher() {
  const { organizations, activeOrganization, setActiveOrganization } =
    useHelloJohn();

  return (
    <select
      value={activeOrganization?.id}
      onChange={(e) => setActiveOrganization(e.target.value)}
    >
      {organizations.map((org) => (
        <option key={org.id} value={org.id}>
          {org.name}
        </option>
      ))}
    </select>
  );
}

setActiveOrganization handles token refresh automatically.


Reading the Active Organization

import { useOrganization } from "@hellojohn/react";

function Dashboard() {
  const { organization, isLoaded } = useOrganization();

  if (!isLoaded) return <p>Loading...</p>;

  if (!organization) {
    return <p>No active organization. Please select one.</p>;
  }

  return (
    <div>
      <h1>{organization.name}</h1>
      <p>Your role: {organization.membership.role}</p>
    </div>
  );
}

JWT Claims After Switching

Before switching:

{
  "sub": "usr_01HABCDEF123456",
  "org_id": null,
  "role": null
}

After switching to an organization where the user is an admin:

{
  "sub": "usr_01HABCDEF123456",
  "org_id": "org_01HABCDEF777666",
  "role": "admin"
}

Backend: Extracting Active Organization

// Middleware
function extractOrg(req: Request, res: Response, next: NextFunction) {
  const { org_id, role } = req.token;

  if (!org_id) {
    req.org = null;
    return next();
  }

  req.org = { id: org_id, role };
  next();
}

// Route that requires an active org
app.get("/org/data", extractOrg, (req, res) => {
  if (!req.org) {
    return res.status(400).json({ error: "No active organization selected" });
  }

  // Scoped to req.org.id
  const data = await getOrgData(req.org.id);
  res.json(data);
});

Persisting the Active Organization

The SDK stores the active organization selection in memory. On page reload, you can restore it:

// On app load, restore last-used org
const lastOrgId = localStorage.getItem("lastOrgId");

if (lastOrgId && organizations.find((o) => o.id === lastOrgId)) {
  await setActiveOrganization(lastOrgId);
}

// On switch, persist the choice
const handleSwitch = async (orgId: string) => {
  await setActiveOrganization(orgId);
  localStorage.setItem("lastOrgId", orgId);
};

Don't store sensitive data in localStorage. The org ID is not sensitive.


No Active Organization

Some apps allow users to operate without an active organization (e.g., personal dashboard). In this state, org_id and role are null in the JWT.

// Deselect the active organization
await hj.setActiveOrganization(null);

Building a Full Org Switcher

import { useHelloJohn, useOrganization } from "@hellojohn/react";

function OrgSwitcherMenu() {
  const { organizations, setActiveOrganization } = useHelloJohn();
  const { organization: activeOrg } = useOrganization();

  return (
    <div className="org-switcher">
      <div className="current-org">
        <img src={activeOrg?.logo_url} alt="" />
        <span>{activeOrg?.name ?? "Select organization"}</span>
      </div>
      <ul className="org-list">
        {organizations.map((org) => (
          <li key={org.id}>
            <button
              onClick={() => setActiveOrganization(org.id)}
              className={org.id === activeOrg?.id ? "active" : ""}
            >
              {org.name}
              {org.is_personal && " (Personal)"}
            </button>
          </li>
        ))}
        <li>
          <button onClick={() => window.location.href = "/orgs/new"}>
            + Create organization
          </button>
        </li>
      </ul>
    </div>
  );
}

On this page