API Reference
@shoojs/react
React hook and Convex adapter for Shoo
Install
npm install @shoojs/reactPeer dependency: react >= 18. Both exports are safe to import and render on the server. They create the browser client after mount.
useShooAuth(options?)
import { useShooAuth } from "@shoojs/react";
export function AuthStatus() {
const { status } = useShooAuth();
return <p>{status}</p>;
}Options
Accepts all ShooAuthOptions, plus:
| Option | Type | Default | Description |
|---|---|---|---|
autoHandleCallback | boolean | true | Finish a pending sign-in on mount |
autoSessionMonitor | boolean | true | Check the token with Shoo on an interval |
sessionMonitorIntervalMs | number | 60000 | Check interval |
Options are read once on mount. The monitor options can change later.
Return value
| Property | Type | Description |
|---|---|---|
status | "loading" | "signed_in" | "signed_out" | "expired" | Current state. Use this to choose what to render. |
identity | ShooIdentity | userId (the pairwise_sub) and token |
claims | IdentityClaims | null | Decoded claims for display. Not verified. |
sessionState | "unknown" | "active" | "login_required" | Last result from Shoo's session check |
loading | boolean | Same as status === "loading" |
error | string | null | Last sign-in or callback error |
signIn | (params?) => Promise<void> | Navigate to Shoo. Accepts StartSignInOptions. |
clearIdentity | () => void | App sign-out in this browser |
checkSession | () => Promise<SessionCheckResult> | Check with Shoo now |
handleCallback | (params?) => Promise<TokenResponse | null> | Use when autoHandleCallback is false |
refreshIdentity | () => void | Re-read stored identity |
authClient | ShooAuthClient | null | The underlying client, after mount |
Behavior
- One callback makes one exchange, even under Strict Mode or with several hook consumers.
- All consumers in this tab and other tabs update when the identity changes.
- When the token expires,
statusbecomes"expired". The hook never navigates by itself. CallsignIn()from a user action. signIn()keeps the profile scope of the previous token unless you passrequestPii. If the previous token had no profile data, the next sign-in does not ask for it.signIn()rejects when it cannot start, for example whensessionStorageis not writable. It also setserror, so you can render the message. If you do not catch the rejection, the browser logs it as unhandled.- Each mounted
useShooAuthruns its own session monitor and its own requests to Shoo. Call the hook once in a shared provider and pass the result down with context. Or setautoSessionMonitor: falseon the other calls. - If a session check returns
login_required, the hook clears the stored identity. checkSession()always returns the result. If the token changed or the component unmounted before the result arrived, the hook does not apply it to its state.- The known limits of
@shoojs/authalso apply: device clock, one SDK copy per page, and sign-in state insessionStorage.
import { useShooAuth } from "@shoojs/react";
export function Profile() {
const { status, identity, claims, error, signIn, clearIdentity } = useShooAuth();
if (status === "loading") return <p>Loading…</p>;
if (status !== "signed_in") {
return (
<div>
{error && <p role="alert">{error}</p>}
<button
onClick={() => {
void signIn().catch(() => {
// The hook sets error, which is rendered above.
});
}}
>
{status === "expired" ? "Sign in again" : "Sign in"}
</button>
</div>
);
}
return (
<div>
<p>ID: {identity.userId}</p>
{claims?.email && <p>Email: {claims.email}</p>}
<button onClick={clearIdentity}>Sign out</button>
</div>
);
}createShooConvexAuth(options)
Call once at module scope. Pass useAuth to ConvexProviderWithAuth. See the Convex guide.
import { createShooConvexAuth } from "@shoojs/react";
export const { useAuth, signIn, signOut } = createShooConvexAuth({
callbackPath: "/shoo/callback"
});Return value
| Property | Type | Description |
|---|---|---|
useAuth | () => ShooConvexAuthState | Hook for ConvexProviderWithAuth |
signIn | (opts?: StartSignInOptions) => Promise<void> | Navigate to Shoo |
signOut | () => void | Clear the stored identity. All consumers update. |
useAuth()
| Property | Type | Description |
|---|---|---|
isLoading | boolean | true until the client is ready |
isAuthenticated | boolean | true when status === "signed_in" |
fetchAccessToken | (args) => Promise<string | null> | The stored token, or null. Never navigates. |
status | ShooAuthStatus | Same values as useShooAuth |
error | string | null | Last callback error |
fetchAccessToken gets a new reference when the signed-in account changes, so Convex sets the new token. It never starts a sign-in.
Re-exports
import { createShooAuth, decodeIdentityClaims } from "@shoojs/react";
import type {
HandleCallbackOptions,
SessionCheckResult,
SessionMonitorOptions,
ShooAuthClient,
ShooAuthOptions,
ShooAuthStatus,
ShooIdentity,
StartSignInOptions,
TokenResponse
} from "@shoojs/react";