Skip to content

<ToriiProvider>

<ToriiProvider> wraps your app, owns session state, refreshes the access token in the background, and exposes everything to the tree via React context. Every Torii component and hook (<SignIn>, <UserButton>, useAuth(), etc.) must render inside it.

Session management uses HttpOnly cookies, so the session token is never exposed to JavaScript. Only the short-lived JWT is held in memory and re-minted from the cookie on page load.

import { ToriiProvider } from '@torii-js/torii-react';
createRoot(document.getElementById('root')!).render(
<ToriiProvider publishableKey={import.meta.env.VITE_TORII_PUBLISHABLE_KEY}>
<App />
</ToriiProvider>,
);

The publishable key is the only required prop. Everything else is opt-in.

Name Type Default Description
publishableKey string - Required. Publishable key (pk_live_… or pk_test_…) issued for your environment. The SDK decodes its API origin from this key.
children ReactNode - Required. Your application tree.
proxyOrigin string - Absolute origin (e.g. "https://auth.example.com") to route SDK requests through, for first-party session cookies. Required unless your environment uses a verified CNAME custom domain; without one, cookies are third-party and blocked by Safari. Must be http/https; throws synchronously at mount otherwise. See first-party cookies.
navigate (path: string) => void full-page redirect SPA-router navigation callback. When set, every internal SDK redirect (<RedirectToSignIn>, <RedirectToGates>, post-OAuth landing) calls this instead of setting window.location.href, preserving router state. OAuth provider start (bouncing to the IdP) always does a real navigation regardless.
appearance AppearanceConfig { theme: 'shadcn' } Visual config: a theme preset ('shadcn' | 'mui') or full Theme, token variables, and per-slot elements. See theming.
languages LanguageInput[] your environment’s languages Available languages. Each entry is a built-in code ('en' | 'da') or a full LanguageConfig. Omit it and the languages configured for your environment in the dashboard (Settings → Languages) apply, so you can change what you offer without a deploy. Passing it overrides the dashboard for this app. Either way the SDK auto-detects the browser locale within the set, renders the language picker, and exposes it via useAuth().setLanguage. Unknown bare codes throw at mount. See labels & i18n.
cdnUrl string https://cdn.torii.so/sdk/v0/latest/runtime.js Override the origin the signed UI runtime (runtime.js) is fetched from. Defaults to the baked-in production runtime origin. Point it at a self-hosted mirror for a strict CSP, an air-gapped deployment, or a native WebView. Must be an absolute http(s) URL; validated at mount (throws synchronously if malformed).
manifestUrl string https://manifest.torii.so/manifest.json Override the origin the signed integrity manifest (manifest.json) is fetched from. Defaults to the baked-in production manifest origin. Set it alongside cdnUrl when serving from a self-hosted mirror (the manifest’s hashes must match the runtime served). Must be an absolute http(s) URL; validated at mount (throws synchronously if malformed).
events ToriiProviderEvents - Session-lifecycle telemetry hooks. All optional. See Events.
afterOauthSignInPath string "/" Where the server sends the browser after an OAuth sign-in callback. OAuth-only: password sign-in is in-page (flip render gates or route via onLoginSuccess).
afterOauthSignUpPath string afterOauthSignInPath Where the server lands the browser after an OAuth sign-up callback. Inherits afterOauthSignInPath, so setting only that covers both.
afterOauthLinkPath string "/" Where the server lands the browser after an OAuth identity-link callback. Set this when <ConnectedAccounts> lives on a non-root route.
gateUrls Partial<Record<GateKey, string>> {} Per-gate-key URL map consumed by <RedirectToGates> to route a user with a pending session gate. See session gates.
isSatellite boolean false Marks this app as a satellite, served from a different registrable domain than your primary, sharing the primary’s session. Requires domain. See Satellite domains.
domain string - Required for a satellite. The satellite’s own domain (e.g. "app.acme.io" or a full origin); it can’t be derived from the publishable key, which encodes your primary. This is the host the satellite’s session cookie is set on. Must resolve to http/https.
satelliteAutoSync boolean true Whether a satellite automatically syncs with the primary on initial load when it has no local session. Set false to trigger the sync yourself.
signInUrl string - URL of your sign-in page. For a satellite this must point to your primary: sign-in is hosted there, not on satellites. Exposed on the auth context.
signUpUrl string - URL of your sign-up page. For a satellite this must point to your primary: sign-up is hosted there, not on satellites. Exposed on the auth context.
onActiveSessionChange (info: ActiveSessionChangeInfo) => void full-page reload Called when the account switcher swaps the signed-in identity in place (switching accounts, adding one, or falling back to a remaining account after signing out the active one). By default the SDK reloads to signInUrl (or "/") on a swap, so a fresh page rebuilds every cache for the new user. Set this to keep the swap in-page instead: the SDK skips the reload and hands you { userId, previousUserId, sessionId?, reason } so you invalidate your own caches (query client, store) and navigate yourself. Initial sign-in and sign-out are unaffected either way.

A satellite is an app on a different registrable domain than your primary (e.g. acme.com and acme.io) that shares the same session. Because a cookie can’t span different registrable domains, a satellite syncs the session from the primary via a redirect handshake.

Set the primary’s publishable key on every app (the key encodes the primary FAPI). On the satellite, also set isSatellite, its own domain, and the primary’s signInUrl / signUpUrl:

// On the primary (acme.com), nothing extra needed.
<ToriiProvider publishableKey="pk_live_…">{children}</ToriiProvider>
// On a satellite (acme.io)
<ToriiProvider
publishableKey="pk_live_…" // the PRIMARY's key
isSatellite
domain="acme.io" // the satellite's own domain
signInUrl="https://acme.com/sign-in" // sign-in lives on the primary
>
{children}
</ToriiProvider>

On a cold load with no local session, the satellite bounces to the primary to sync one (unless satelliteAutoSync={false}); if the primary has no session either, it returns and renders signed-out. Each satellite domain must be registered and verified for the environment in the dashboard.

For a Capacitor / hybrid-native app, two more props switch the SDK into a cookie-less mode (session token in secure storage, system-browser OAuth). They have no effect in a normal web build. See Native (Capacitor) auth for the full setup.

Name Type Description
tokenCache ToriiTokenCache Secure-storage adapter ({ getToken, saveToken, clearToken }). Supplying it turns on cookie-less native mode.
nativeOAuth { browser: { openOAuth: (url) => Promise<string | null> }; redirect: string } Native OAuth config: the system-browser bridge plus its deep-link target (which must be in the environment’s allowed native redirects). Required for native OAuth.

The three afterOauth* paths drive OAuth callback landing only: OAuth can’t resolve in-page, so the server redirects the browser to a configured path. Password sign-in/up never navigate: they flip session state in-place, so <SignedIn> / <SignedOut> swap immediately. Route password flows explicitly from onLoginSuccess / onSignupSuccess, preferring your router over window.location.href so query/hash state survives.

The events prop is a bag of optional session-lifecycle callbacks, pure telemetry. They are not navigation hooks: OAuth landing is owned by the afterOauth* paths, and password flows route via the per-form onLoginSuccess / onSignupSuccess.

Field Signature Fired when
onSessionExpired () => void The session ends server-side (expired or revoked).
onError (error: ToriiError) => void A non-fatal SDK error, such as a failed token refresh or a failed server-side sign-out. Read error.code (a stable, machine-branchable code) and error.message. Telemetry only; the SDK still settles to a coherent state (signed-out on expiry; local sign-out proceeds regardless).
<ToriiProvider
publishableKey="pk_live_…"
events={{
onSessionExpired: () => router.navigate({ to: '/sign-in', search: { expired: true } }),
onError: (err) => reportError(err),
}}
>
<App />
</ToriiProvider>

When a callback is unwired, the corresponding event is silent (no SDK-level log): telemetry is strictly opt-in.

The provider feeds everything to the tree through React context. Read it with the useAuth() hook, which returns the curated public AuthContextValue:

  • State: isLoaded, isLoading, isSignedIn, user, userProfile, organizations, currentLanguage, runtimeIncompatible.
  • Actions: require (role/permission authorization check), getToken (async, refreshes first if the current token is near expiry), getAccessToken (synchronous, no refresh), signIn, signOut, refreshSession, setLanguage.

useAuth().organizations is the user’s membership list; use useOrganizations() to switch the active org or manage members.

The provider’s context carries additional fields the SDK’s own components read internally; those are deliberately not part of the public useAuth() type.

import { useAuth } from '@torii-js/torii-react';
function Greeting() {
const { isLoaded, isSignedIn, user } = useAuth();
if (!isLoaded) return <Spinner />;
if (!isSignedIn) return null;
return <span>Hi, {user?.id}</span>;
}
  • Runtime loading. UI and auth logic load from the CDN at first render. Until the runtime resolves the provider reports isLoading: true; on a hard load failure it settles signed-out and surfaces the failure via <ToriiFailed> / useRuntimeStatus(). For a strict CSP, allow script-src cdn.torii.so and connect-src manifest.torii.so. To self-host or air-gap, point cdnUrl / manifestUrl at your own mirror and allow those origins instead.
  • Cross-tab sync. Sign-in / sign-out broadcast to other tabs of the same origin (no token crosses the channel; the receiving tab re-mints from its own cookie).
  • Background refresh. The JWT is refreshed automatically (and on tab refocus). A failed refresh falls through to events.onSessionExpired.
import type {
ToriiProviderProps,
ToriiProviderEvents,
AuthContextValue,
} from '@torii-js/torii-react';