Authentication
How sessions, roles and tenancy fit together.
Auth is Better Auth with the organization, admin and twoFactor plugins. The config
lives in src/lib/auth.ts.
Two separate role axes
user.role is the platform role — user or admin — and controls access to
/admin. member.role is the organisation role — owner, admin or member — and
controls what you can do inside one workspace. A platform admin is not
automatically an owner of anyone's organisation.
Where authorisation actually happens
The middleware only checks for a session cookie so signed-out visitors don't see the app shell flash. Real checks happen in three places:
src/app/(app)/organizations/[slug]/layout.tsx— confirms membership- Every server action — re-derives session and role from the database
- Route handlers — same, before any work is done
A layout does not protect a server action. Actions are separately addressable, so
each one calls requireMembership() itself.
Adding a capability
Add it to the map in src/lib/permissions.ts, then call assertCan(role, 'your:capability').
Do not write inline role comparisons — that is how the sidebar and the server end
up disagreeing.