B2B SaaS tenancy: where should organizations, memberships, invites and base roles live?

The decision is about authority, not where a screen lives
When a B2B app adds organizations, invitations, and roles, the first architectural question is not which admin screen to build. It is which system is allowed to make the final decision about a user's membership and access.
That distinction matters because “the user is authenticated” and “the user may read this organization's records” are different facts. An identity provider can prove that a session belongs to a particular subject. Your application still needs a trustworthy answer to which tenant that subject belongs to, what role applies, and whether that role permits this action on this record.
A useful starting rule is to choose one authority for each class of data. For example, an identity provider can own authentication and external identity while your application database owns organizations, memberships, invitations, and product permissions. A B2B identity provider can instead own generic organizations and memberships, while your database owns product-specific assignments. Either can work. A half-synchronized model, where both systems can independently change membership, is the risky one.
The question surfaced in a developer's pre-launch discussion about Entra External ID, PostgreSQL, and a multi-tenant app. The useful point is the boundary they were trying to choose: who answers “who is this person?” and who answers “which organization can they act for?” One developer's experience is a prompt for a design review, not proof that one vendor or model is universally correct.
Separate identity from application membership
Treat the provider's stable subject identifier as an external identity, not as your application's user primary key. Create an internal user record and map the provider and subject to it. This keeps domain tables from depending on a vendor-specific identifier and gives the application a stable join point if authentication changes later.
An illustrative model might look like this:
CREATE TABLE app_user (
id uuid PRIMARY KEY,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE external_identity (
id uuid PRIMARY KEY,
user_id uuid NOT NULL REFERENCES app_user(id),
issuer text NOT NULL,
subject text NOT NULL,
UNIQUE (issuer, subject)
);
CREATE TABLE organization (
id uuid PRIMARY KEY,
name text NOT NULL
);
CREATE TABLE organization_membership (
organization_id uuid NOT NULL REFERENCES organization(id),
user_id uuid NOT NULL REFERENCES app_user(id),
base_role text NOT NULL CHECK (base_role IN ('owner', 'admin', 'member')),
created_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (organization_id, user_id)
);This is a starting shape, not a complete authorization system. Production choices still need to account for role changes, audit history, account linking, suspension, deletion, and any provider-specific subject semantics. Do not trust a role sent by the browser, and do not accept an organization ID from a request as proof that the caller belongs to that organization.

12 production screens. Login and database wired.
The SaaS Dashboard Kit includes login, a database, and a working dashboard. Live demo at saas.otf-kit.dev.
Choose one source of truth for each decision
Before building invitations, write down the owner of every state transition. A small table makes hidden duplication visible:
| Decision or record | App database owns it | Identity provider owns it |
|---|---|---|
| Login, MFA, session and external subject | No | Yes |
| Organization membership and base role | Yes | No |
| Product-specific project assignment | Yes | No |
| Invitation acceptance state | Yes | No |
| Enterprise SSO policy, when adopted | No | Yes |
The table is an example of one coherent split, not a required vendor configuration. If the provider owns organizations and memberships, make that choice explicit too: record the provider organization identifier, define how updates reach the application, and decide what happens when synchronization is delayed or fails. Never let one endpoint make an access decision from a local membership row while another endpoint trusts a stale provider claim without a defined consistency rule.
For each field, ask: who can create it, who can change it, which system is authoritative, how does the other system learn about the change, and how quickly must revocation take effect? If the answer is “both,” specify conflict resolution and failure behavior before users depend on it.
Keep authorization close to the protected data
Whichever system owns membership, enforce the decision on the server for every request that reads or changes tenant data. A common path is: validate the session, map its issuer and subject to an internal user, resolve the active organization membership, then authorize the requested operation against that membership and the target record.
Avoid relying only on a role embedded in a long-lived token when administrators can change roles during a session. Define how quickly changes take effect: for example, short-lived claims plus a server-side membership check for sensitive operations, or a versioned authorization cache with explicit invalidation. The right choice depends on the application's risk and latency requirements; write it down instead of assuming token refresh will happen at the right moment.
Then test tenant boundaries directly. Create two organizations with distinct users and records. Verify that a member can reach only their own organization's records, that changing an organization ID in a URL or request body does not cross the boundary, and that a removed member loses access on the expected timeline. Include background jobs and exports in the same review; they often bypass the request path where the first authorization check was added.

Treat invitations as a state machine
An invitation is not just an email with a button. It represents a pending grant of access, so define its lifecycle: created, delivered or delivery-failed, accepted, expired, revoked, and possibly replaced. Bind acceptance to a verified identity and a single organization. Make the token unguessable, store only what the design needs, expire it, and make acceptance idempotent so a repeated click cannot create duplicate memberships.
Decide who may invite, which roles they may assign, and whether an invitation can outlive the inviter's own membership. If an organization admin is removed while an invitation is pending, the system should have a deliberate answer. Audit who invited whom, to which organization, with what role, and when the invitation changed state. Do not expose whether an email address has an account unless that disclosure is intentional.
When a provider-owned organization model fits
Provider-owned organizations can reduce the amount of generic identity administration your team must build, particularly when customer requirements already include organization switching, enterprise SSO, or provisioning. The tradeoff is operational: your application needs a clear way to consume membership changes and make product authorization consistent with them. A local projection may be useful, but it is a projection, not a second writer.
An application-owned model can keep authorization and domain data in one database and may make provider changes less entangled with product tables. The tradeoff is that your team now owns the generic B2B lifecycle: invitations, membership administration, role changes, and the operational work around them. Keeping the data locally does not make SSO or provisioning free; it only leaves room to integrate those later behind a defined mapping.
Choose based on the capability you need now, the work you are prepared to own, and the failure mode you can operate. Do not adopt a provider's organization primitives just because they exist, and do not build them yourself just to avoid a future migration that has not been scoped.
Make the choice reversible where it matters
You can reduce lock-in without pretending every migration will be easy. Keep internal user IDs separate from external subjects. Keep product-specific permissions attached to product records rather than copying every permission into identity claims. Put provider calls behind a small adapter, record the issuer with each external identity, and define a migration test that maps one user and one organization without changing domain ownership.
Before launch, write a short decision record with the selected authority, the data each system owns, the synchronization path, the revocation target, and the test that proves tenant isolation. Revisit it when a real requirement arrives—such as a customer's SSO or provisioning requirement—not merely because another provider has a feature page for it.
If you are assembling the surrounding dashboard and account surfaces, the SaaS Dashboard Kit is an editable starter reference. It does not provide enterprise SSO/SCIM or a complete B2B tenant-membership and role system; those boundaries still need to be designed and implemented for your application.
Sources
- B2B SaaS: do you keep orgs, memberships and base roles in your own DB, or let the auth provider own them?, r/SaaS. The post describes one developer's pre-launch design question and is used as an example of the decision, not as a universal recommendation.
- Related OTF context: SaaS dashboard starter dependency audit.
Ship the product, not the setup.
- 12 production screens — auth, team, analytics, and settings
- Login and a real database are wired in
- AI configs pre-tuned so your agent extends instead of regenerates
Ship your first screen tonight — free starter in your inbox
The quickstart, the SDK repo and the live kit demos, on this screen and in your inbox.
No spam. Unsubscribe any time.