App authentication and sessions
App authentication and sessions
Embedded apps authenticate within the Thor dashboard context and use the resulting Admin client for project operations. Keep authentication, session persistence, and webhook verification in their purpose-specific server handlers.
Authenticate each protected handler once
Call authenticate.admin(request) in every protected route loader or action before reading project data or calling Admin.
React Router can run matched loaders in parallel.
Authenticating in both the root and a child loader can cause competing session exchanges or refreshes.
Keep the root responsible for the app shell and provider configuration.
Do not assume a parent loader is an authentication gate for a child action or resource route.
Each protected entrypoint must establish its own authenticated context.
Use the returned admin client rather than taking a project ID and credential from an untrusted form.
Preserve boundary behavior
Retain the template’s boundary.error and boundary.headers integration and its document response headers.
Authentication responses may need to reach the embedded app bridge.
Do not catch every thrown response and replace it with a generic product-loading error.
If you add diagnostic logging, record only a sanitized cause and available correlation ID.
Never log tokens, cookies, authorization headers, or the client secret.
Use durable production sessions
The default memory session store is for development and tests. It is lost on restart and is not shared across processes or isolates. Production needs a persistent session-storage adapter accessible to every instance serving the app. For Cloudflare, the template documents the D1 adapter and provisioning workflow.
Provision the storage resource, apply the adapter’s documented schema, and pass the configured adapter as sessionStorage to thorApp.
A storage adapter does not provision a cloud database just because it is installed.
Session writes must tolerate concurrent attempts for the same session.
Keep THOR_APP_SCOPES aligned with the active app version.
Test reauthorization when permissions change instead of continuing with a stored session that lacks the new scopes.
Use purpose-specific webhook authentication
The template’s webhook action calls authenticate.webhook(request) and receives payload, session, topic, and project.
Use that helper before dispatching event work.
Do not parse the body first or replace verification with a check that a topic header exists.
After verification, persist and deduplicate the event before acknowledging it.
See Webhooks for the durable processing contract.
Verify production behavior
Test a fresh session, an existing session after restart, token refresh, changed scopes, and requests handled by different app instances. A permission failure should give the operator a useful recovery path, not an endless redirect or an empty success table. Keep the runtime app URL consistent with the registration and deployment origin.