<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.toriiauth.eu/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.toriiauth.eu/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 syncs one from the primary
(unless satelliteAutoSync={false}); if the primary has no session either, it
returns and renders signed-out. The sync starts on the satellite’s own Frontend
API host (/_torii/auth/session/sync/start), which ties the handoff to the
browser that started it, so the one-time link the primary sends back works in
that browser only. 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.toriiauth.euandconnect-src manifest.toriiauth.eu. 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';