Authaz logoAuthaz
DocumentationAPI Reference
  • Get Started

    • Authaz
    • Core Concepts
    • Set up your app
    • Quickstart — cURL
  • Authentication

    • Authentication Settings
    • Signup
    • Invitations
    • Password Authentication
    • Multi-Factor Auth
    • Magic Link
    • OAuth / Social Login
    • Passkey (WebAuthn)
    • SAML SSO
    • Machine-to-Machine (M2M)
    • API Keys
  • Authorization

    • Authorization
    • Resources
    • Policies
    • Roles
    • Access Explorer
  • Tenancy

    • Multi-tenancy
    • Tenancy Customization
  • Brand & Host

    • Branding
    • Custom Domains
    • Communications & Email Templates
  • Operate

    • Users
    • Analytics
    • Audit Logs
    • Application Settings
  • SDK Quickstarts

    • Quickstart — Next.js
    • Quickstart — React SPA
    • Quickstart — Hono
    • Quickstart — .NET (Authaz.Sdk)
  • Recipes

    • Recipes & Cookbook
    • Next.js — first integration
    • Next.js — B2B SaaS (multi-tenant)
    • Hono — first integration
    • Hono — B2B SaaS (multi-tenant)
    • React SPA — first integration
    • React SPA — B2B SaaS (multi-tenant)
    • .NET — first integration
    • .NET — B2B SaaS (multi-tenant)
  • Reference

    • Tokens
    • API Reference
    • Errors & Troubleshooting
  • Documentation

    • How Authaz is Built
  1. Authaz
  2. Docs
  3. Recipes
  4. React SPA — B2B SaaS (multi-tenant)

Recipes

React SPA — B2B SaaS (multi-tenant)

2 min read·Updated Jun 19, 2026

← All recipes · First time? Set up your app (60 seconds — keys + redirect URI)

A React SPA paired with a multi-tenant Authaz application. Builds on the single-tenant React recipe — read that first; this page only shows the deltas.

1. What you'll build#

A B2B SaaS React SPA where the signed-in user's tenantId comes from useUser() and gates every API call to the backend.

2. Application setup#

Pair this with the multi-tenant Hono recipe on the backend (or any backend with multi-tenant Authaz wiring). The frontend doesn't need any application changes — multi-tenant context is set on the backend.

3. Install#

Identical to the single-tenant React recipe.

4. Configure

Previous
React SPA — first integration
Next
.NET — first integration
#

Identical. The tenant picker is rendered by Authaz Sign-In during login; your SPA never renders a tenant chooser.

5. Wire it up#

Identical to single-tenant — <AuthazProvider basePath="/api/auth">, the same callback page, the same router setup.

6. Read the user and tenant#

useUser() returns an AuthazUser with tenantId and organizationId populated when the access token has those claims:

import { useRequireUser } from "@authaz/react";
 
export default function Dashboard() {
  const user = useRequireUser({ redirectTo: "/" });
  if (!user) return null;
 
  if (!user.tenantId) {
    return <p>No tenant selected — sign in again to pick one.</p>;
  }
 
  return (
    <main>
      <h1>Hello, {user.email}</h1>
      <p>Tenant: <code>{user.tenantId}</code></p>
      <p>Organization: <code>{user.organizationId}</code></p>
    </main>
  );
}

7. Tenant-scoped API calls#

Your fetches go to the backend over the same origin (cookie attached automatically). The backend reads tenant_id from the JWT — you don't need to send it from the SPA at all:

import { useUser } from "@authaz/react";
import { useEffect, useState } from "react";
 
export function Invoices() {
  const user = useUser();
  const [invoices, setInvoices] = useState<unknown[]>([]);
 
  useEffect(() => {
    if (!user?.tenantId) return;
    // No need to send tenantId — backend reads it from the JWT
    fetch("/api/invoices")
      .then((r) => r.json())
      .then(setInvoices);
  }, [user?.tenantId]);
 
  return <ul>{/* render invoices */}</ul>;
}

If you ever feel tempted to put tenantId in a header or query string from the SPA — don't. The backend must read it from the JWT exclusively. The frontend's user.tenantId is for display, not for authorization.

8. Common gotchas#

  • user.tenantId is for UI only. Never trust it server-side. The backend pulls tenant_id from the JWT, which the user can't tamper with.
  • Tenant switching is a re-login. If your UX needs a "switch tenant" button, it must call the logout endpoint, then redirect back to /api/auth/login. There's no in-place swap.
  • Re-fetch SPA state when the user changes tenants. Cached queries keyed only by user id will leak cross-tenant data. Key by (user.id, user.tenantId).
  • user.tenantId is undefined on first paint. The hook resolves async. Always handle the null/undefined window.
  • The login redirect doesn't auto-pick a tenant. If your user belongs to multiple tenants, Authaz Sign-In shows the picker. There's no way to skip it from the SPA — the OAuth flow doesn't expose that surface.

9. Next steps#

  • Multi-tenancy guide — shared vs isolated pools, tenant lifecycle
  • Architecture — tenant isolation — JWT binding, RLS, cryptographic isolation per organization
  • Multi-tenant Hono recipe — the recommended backend pairing