Skip to content
Torii docs

<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.

NameTypeDefaultDescription
publishableKeystring-Required. Publishable key (pk_live_… or pk_test_…) issued for your environment. The SDK decodes its API origin from this key.
childrenReactNode-Required. Your application tree.
proxyOriginstring-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) => voidfull-page redirectSPA-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.
appearanceAppearanceConfig{ theme: 'shadcn' }Visual config: a theme preset ('shadcn' | 'mui') or full Theme, token variables, and per-slot elements. See theming.
languagesLanguageInput[]['da', 'en']Available languages. Each entry is a built-in code ('en' | 'da') or a full LanguageConfig. The SDK always auto-detects the browser locale against the configured (or default ['da', 'en']) languages, renders the language picker, and exposes it via useAuth().setLanguage. Pass ['en'] to ship English-only. Unknown bare codes throw at mount. See labels & i18n.
cdnUrlstringhttps://cdn.torii.so/sdk/v0/latest/runtime.jsOverride 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).
manifestUrlstringhttps://manifest.torii.so/manifest.jsonOverride 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).
eventsToriiProviderEvents-Session-lifecycle telemetry hooks. All optional. See Events.
afterOauthSignInPathstring"/"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).
afterOauthSignUpPathstringafterOauthSignInPathWhere the server lands the browser after an OAuth sign-up callback. Inherits afterOauthSignInPath, so setting only that covers both.
afterOauthLinkPathstring"/"Where the server lands the browser after an OAuth identity-link callback. Set this when <ConnectedAccounts> lives on a non-root route.
gateUrlsPartial<Record<GateKey, string>>{}Per-gate-key URL map consumed by <RedirectToGates> to route a user with a pending session gate. See session gates.
isSatellitebooleanfalseMarks this app as a satellite, served from a different registrable domain than your primary, sharing the primary’s session. Requires domain. See Satellite domains.
domainstring-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.
satelliteAutoSyncbooleantrueWhether a satellite automatically syncs with the primary on initial load when it has no local session. Set false to trigger the sync yourself.
signInUrlstring-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.
signUpUrlstring-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.

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.

NameTypeDescription
tokenCacheToriiTokenCacheSecure-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.

FieldSignatureFired when
onSessionExpired() => voidThe session ends server-side (expired or revoked).
onError(error: ToriiError) => voidA 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';