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:

ModeBehaviour
OIDCoidc-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 sessionThe default when no client id is set. Checks /auth/api/get-session; sign-in and sign-out go to /auth.
.env
VITE_OAUTH_SERVER_URL=https://auth.example.com
VITE_OAUTH_CLIENT_ID=            # set -> OIDC, empty -> cookie session
VITE_OAUTH_SCOPE=full_access offline_access

The 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:

  1. / redirects to /select-tenant.
  2. The tenant picker lets the user choose or create a tenant.
  3. 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.


On this page