<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. |
Satellite domains (multi-domain SSO)
Section titled “Satellite domains (multi-domain SSO)”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.
Native (Capacitor) props
Section titled “Native (Capacitor) props”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. |
Navigation: OAuth vs. password
Section titled “Navigation: OAuth vs. password”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.
Events
Section titled “Events”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 auth context
Section titled “The auth context”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>;}Behaviour notes
Section titled “Behaviour notes”- 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, allowscript-src cdn.torii.soandconnect-src manifest.torii.so. To self-host or air-gap, pointcdnUrl/manifestUrlat 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.
TypeScript
Section titled “TypeScript”import type { ToriiProviderProps, ToriiProviderEvents, AuthContextValue,} from '@torii-js/torii-react';