Authentication & Multi-Tenancy
OIDC or cookie sessions, permission registration before first render, and the :tenant URL segment.
Authentication and tenancy both come from the thunder-core plugin on the backend. Thunder UI consumes them through the generated SDK - it does not implement its own user store.
Two Strategies
Which one runs is decided by whether VITE_OAUTH_CLIENT_ID is set:
| Mode | Behaviour |
|---|---|
| OIDC | oidc-client-ts. Redirect flow on web; in-app browser with a custom-scheme callback (<appId>://auth/callback) on native, with tokens stored in Capacitor Preferences. |
| Cookie session | The default when no client id is set. Checks /auth/api/get-session; sign-in and sign-out go to /auth. |
VITE_OAUTH_SERVER_URL=https://auth.example.com
VITE_OAUTH_CLIENT_ID= # set -> OIDC, empty -> cookie session
VITE_OAUTH_SCOPE=full_access offline_accessThe gate itself lives in src/core/protected.tsx.
thunder-core provides both sides: sessions for the cookie strategy and a full OAuth2 / OIDC provider for the OIDC strategy.
Permissions Are Registered Before First Render
After authentication, ThunderSDK.plugins.essentials.registerPermissions() runs before the app renders. That is what makes ThunderSDK.isPermitted() reliable for route visibility - a navigation entry never flickers in and out while permissions load.
This is why route visibility can be written as a plain synchronous function:
products: { merge: true, display: () => ThunderSDK.isPermitted(ThunderSDK.products.update) },The permission data comes from thunder-core's access control.
The :tenant Segment
Every authenticated URL carries a tenant:
/:tenant/<group>/<module>The segment is pushed into the SDK with ThunderSDK.plugins.essentials.setTenant(), so every subsequent request is scoped to it. The flow is:
/redirects to/select-tenant.- The tenant picker lets the user choose or create a tenant.
- The app lands on
/:tenant/overview.
A 403 while loading permissions redirects back to the tenant picker - that is the signal that the selected tenant is not one the current user may access.
Tenants, members and invites are modelled by thunder-core's multi-tenancy.
Push Tokens
On native builds, push notifications are registered through @capacitor/push-notifications and the device token is posted to the backend with ThunderSDK.users.addFcmToken. See Mobile.