Skip to content
← All posts
Authentication 6 min read

The Auth and SSO Questions Every B2B Buyer Will Ask

Enterprise buyers ask the same three identity questions before they sign; build the answers into the schema, not the sales deck.

Every B2B security questionnaire asks the same three questions before a contract gets signed: can we log in with our own identity provider, can our IT team provision and remove users automatically, and can we prove who did what inside your product. If the answer to any of them is 'we'll add that later,' the deal slips a quarter or dies.

The mistake we see most often is a users table with a single organization_id column, built for a demo where every user belongs to exactly one company. That model survives until the first customer asks for a contractor who works across three of their client accounts, or the first enterprise buyer whose IT department wants to control login instead of your app. Retrofitting organization boundaries into an auth system that was never designed to have them means touching every query that assumes one user has one tenant.

This is one of the places where the unglamorous plumbing of a SaaS product decides whether you close enterprise deals or stay stuck selling to teams small enough not to have a security review. We build this layer the same way regardless of the product: organizations, memberships, and invitations as first-class tables, with SSO and SCIM as connectors that plug into that model rather than replace it, the same pattern behind the multi-location tenancy in Retailer OS.

The three questions in every security questionnaire

  • SSO: can our employees log in with Okta, Azure AD, or Google Workspace instead of a password you control?
  • Provisioning: when we hire or fire someone, does access to your product update automatically, or does someone have to remember to email you?
  • Audit: if someone changes a price, deletes a record, or exports customer data, can you tell us who did it and when?

The third question gets answered by logging the events an auditor will actually ask for, not a generic activity feed. The first two are answered by the schema underneath your login page, not by which SSO SDK you install.

Model organizations and memberships before you model auth

Build three tables before you wire up any SSO integration: organizations, users, and memberships. A user is a person with credentials. An organization is a billing and permission boundary. A membership joins the two with a role, and it's the membership — not the user — that SSO, invitations, and entitlement checks all hang off.

create table organizations (
  id uuid primary key,
  name text not null,
  sso_domain text unique
);

create table users (
  id uuid primary key,
  email citext unique not null
);

create table memberships (
  user_id uuid references users(id),
  organization_id uuid references organizations(id),
  role text not null,
  status text not null default 'active',
  primary key (user_id, organization_id)
);

This shape answers the question that breaks most retrofits: what happens when one person needs access to two organizations, like an agency managing several client accounts or a contractor logging into three general contractors' project sites. With memberships as a join table, that's a second row, not a schema migration.

SAML vs OIDC: pick based on who's asking, not what's newer

OIDC is JSON over OAuth2, easier to debug, and what most identity providers default to now. SAML is XML assertions over a browser redirect, older, and still what a meaningful share of enterprise IT departments run through Okta, Azure AD, or ADFS because it's what they've had configured for a decade. Support OIDC first because it covers Google Workspace and most modern IdPs with one code path. Add SAML per connection when a specific deal requires it, because SAML configuration — entity ID, assertion consumer service URL, signing certificate — is per-tenant data, not a global setting.

Store that configuration on the organization row, not in environment variables. Each enterprise customer's IT team supplies its own metadata, and you need to support more than one live SAML connection without a deploy. When a user authenticates through a tenant's IdP for the first time, provision the membership automatically if their email domain matches sso_domain on the organization. That's just-in-time provisioning, and it removes the step where someone has to pre-create every account by hand before day one.

Invitations are a race condition waiting to happen

An invitation is a promise that a specific email address will get a specific role in a specific organization. The bugs live in the gaps between that promise and reality: the invited person already has an account under a different email, two admins invite the same address twice, or the organization turns on SSO enforcement after invitations already went out.

  • Tie the invitation row to (email, organization_id, role), not to a user id — the user might not exist yet.
  • Expire invitations after seven days and make re-sending create a new token, not resurrect the old one.
  • If an organization has an sso_domain set and enforces SSO, block password-based invitation acceptance for that domain and redirect straight to the IdP flow.
  • On acceptance, look up the email against existing users first. If it matches, attach a new membership to the existing account instead of creating a duplicate user with a duplicate history.

That last rule matters more than it looks. Nothing erodes trust in a new platform faster than a user discovering they have two accounts with two different histories because they accepted an invite with the email their IT department assigned instead of the one they usually use.

Deprovisioning is the feature nobody demos but everyone requires

SCIM (System for Cross-domain Identity Management) is the protocol identity providers use to push user changes into your product instead of making you poll for them. Most vendors treat SCIM as a nice-to-have until an enterprise buyer makes it a contract line item, because the failure mode it prevents is invisible until it isn't: an employee gets fired, IT deactivates them in Okta, and they still have an active session in your product three weeks later because nothing told you to revoke it.

Two things have to happen when a SCIM deactivation event or a manual removal comes in: the membership row flips to disabled immediately, and any active session or token tied to that membership gets revoked, not just blocked at next login. If your auth checks happen at the edge — verifying a signed token in a Cloudflare Worker before a request ever reaches your origin — you need a revocation list the edge can check cheaply, because the whole point of edge auth is skipping the database round trip, and revocation is the one case where you can't skip it. Log the removal itself, because 'we deprovisioned this person on the date IT asked us to' is exactly the kind of thing an audit trail needs to answer without someone digging through support tickets.

When to build this and when to buy it

Building SAML, OIDC, and SCIM support from scratch is real engineering time: certificate handling, clock skew tolerance, attribute mapping per IdP, and a SCIM endpoint that behaves the way Okta's connector actually expects, which is not always what the spec implies. If you're pre-revenue or your buyers are small teams without a security review, a hosted provider is the faster path and the honest recommendation — build the organization and membership model correctly from day one, then point a third-party auth service at it, rather than spending your first few months of runway on SAML XML parsing.

The calculus changes once identity has to drive billing and entitlements at the same time — once which features an organization can see depends on their plan, and enterprise contracts start asking for usage reports segmented by SSO-provisioned seats versus manually invited ones. At that point the auth layer isn't a bolt-on anymore, it's load-bearing for revenue, and that's usually when it's worth owning outright. We've shipped that model across our portfolio on tenant boundaries ranging from retail chains to construction project teams. If you're figuring out which side of that line your product is on, that's worth working through before the schema gets written, not after — see how we scope this kind of work.

Frequently asked

What's the difference between SAML and OIDC for B2B SSO?

SAML sends signed XML assertions through a browser redirect and is what most enterprise IT departments already have configured through Okta or Azure AD. OIDC is JSON over OAuth2, simpler to implement and debug, and what most modern identity providers default to. Support OIDC as your baseline, then add SAML per tenant only when a specific enterprise deal requires it.

Do I need SCIM if I already support SSO?

SSO controls how someone logs in; SCIM controls whether they should be able to log in at all. Without SCIM, deactivating a user in your customer's identity provider doesn't remove their access to your product until someone remembers to do it manually inside your admin console. Enterprise buyers increasingly require automatic deprovisioning as a contract term, not an optional feature.

How should invitations work when someone already has an account under a different email?

Tie the invitation to the email address and organization, not to a user record. When the invite is accepted, check whether that email already belongs to an existing user before creating a new one. If it does, attach a new membership to the existing account instead of splitting one person's history across two accounts.

Should a small SaaS startup build SSO and SCIM in-house?

Not usually. Building certificate handling, IdP attribute mapping, and a spec-compliant SCIM endpoint costs real engineering weeks that early-stage teams rarely have to spare. Model organizations and memberships correctly from day one, then point a hosted auth provider at that model. Bring it in-house once identity starts driving billing and entitlements directly.

Can one user belong to more than one organization?

Yes, and the schema should assume it from the start. Use a membership table joining users to organizations with a role and status, rather than an organization_id column on the user record. That lets a contractor, agency, or auditor hold separate memberships across multiple tenants without duplicate accounts or awkward workarounds later.