@zautha/react
React bindings for Zautha: <ZauthaProvider>, hooks (useAuth, useUser,
useSession, …), <Show>, and the prebuilt components from @zautha/ui.
Install
Section titled “Install”npm i @zautha/react @zautha/uiQuickstart (React + Vite)
Section titled “Quickstart (React + Vite)”import { Show, SignIn, UserButton, ZauthaProvider } from "@zautha/react";import { ui } from "@zautha/ui";import "@zautha/ui/styles.css";
export function App() { return ( <ZauthaProvider publishableKey="pk_test_…" ui={ui}> <Show when="signed-out"> <SignIn /> </Show> <Show when="signed-in"> <UserButton /> </Show> </ZauthaProvider> );}Social sign-in
Section titled “Social sign-in”<SignIn /> and <SignUp /> show the providers your instance enabled (Google, GitHub, …) above their forms. The
provider sends the user back to ssoCallbackUrl (default: the same page, where the component finishes the sign-in);
for a dedicated page, render <AuthenticateWithRedirectCallback /> there and pass its URL:
<SignIn ssoCallbackUrl="/sso-callback" redirectUrl="/dashboard" />;
// app/sso-callback page:<AuthenticateWithRedirectCallback />;It finishes the sign-in or sign-up, asks on the same page for whatever is left (the second factor, a missing username,
the email code when the provider did not verify the address) and goes on to redirectUrl. A modal sign-in offers
providers only with an ssoCallbackUrl. With no target at all (neither the flow’s redirectUrl nor its own
redirectUrlComplete prop) it stays on its waiting card once signed in: pass redirectUrlComplete, or go on yourself
from onEvent (sign_in.completed, sign_up.completed). It is not the page for connecting an account: <UserProfile />
on its own page finishes a connection itself.
Account management
Section titled “Account management”<UserProfile /> is the signed-in user’s account page: name and username, email addresses, connected accounts
(connecting a provider leaves for it and comes back to the same page, which finishes it), password, passkeys,
two-step verification, active devices and (when the instance allows it) account deletion. Passkeys show only where the
browser and the page can use them: on a page under the instance’s passkey domain (production: your domain, shared with
the hosted pages; development: the page’s own host, so a passkey added on localhost does not work on the hosted pages
and the other way round). See Passkeys in @zautha/js. <UserButton />’s “Manage account” opens it in a modal, or
goes to userProfileUrl when you pass one (e.g. your instance’s hosted /user page). Connecting an account from
inside that modal does not finish: the provider sends the user back to the app’s page, not into the modal, so
nothing completes the connection there — pass userProfileUrl, or render <UserProfile /> on its own page, for
connecting accounts.
Organizations: roles and permissions
Section titled “Organizations: roles and permissions”useAuth() gives the session’s active organization from its token: orgId, orgRole (org:admin), orgSlug and
orgPermissions (the role’s custom permissions, org:invoices:read), all null without one. has({ role }) and
has({ permission }) take full keys and answer the same as your server does (@zautha/backend, @zautha/nextjs,
Zautha.AspNetCore); a system permission (org:sys_…) is never in the token, so it is false. <Show> takes the same
conditions. Switch organizations with useZautha()?.setActive({ organization }) (an id, a slug while slugs are on, or
null for the personal account); the hooks follow once the new token is in.
<Show when={{ permission: "org:invoices:read" }} fallback={<p>No access</p>}> <Invoices /></Show><Show> only hides UI: check the same permission on your server.
Sensitive actions (reverification)
Section titled “Sensitive actions (reverification)”useReverification(fetcher) wraps an action that may need a recent identity check. When fetcher throws
ReverificationRequiredError (e.g. user.updatePassword) or resolves to your backend’s 403 reverification_required
response (requireReverification in @zautha/backend, reverificationError in @zautha/nextjs,
RequireZauthaReverification in Zautha.AspNetCore), a modal asks the user to verify and the action runs once more.
Closing the modal rejects with ReverificationCancelledError.
const { getToken } = useAuth();const transfer = useReverification(async () => fetch("/api/transfer", { method: "POST", headers: { Authorization: `Bearer ${await getToken()}` } }),);Impersonation
Section titled “Impersonation”useAuth().actor (and useSession().session.actor) is { sub, iss } in an impersonation session and null
otherwise. With ui on <ZauthaProvider>, the provider shows the impersonation band at the bottom of every page of
such a session (“End session” signs out); nothing to add yourself. Without ui (headless use) there is no band, but actor is still set: show your own notice
from useAuth().actor.
const { actor } = useAuth();if (actor !== null) console.log(`support agent ${actor.sub} is signed in as this user`);