Designing Auth Teams Don't Have to Think About
Authentication is the feature every SaaS rebuilds and gets subtly wrong. A look at the architecture decisions behind building it once, correctly.
Every SaaS team rebuilds authentication. Most of them get the happy path right and the edge cases wrong — token rotation, revocation, multi-tenancy, audit. The failures are invisible until they're a breach.
Here's how I think about building auth infrastructure that teams can adopt without becoming security experts.
Speed and revocation are in tension#
Stateless JWTs are fast: verify a signature at the edge and you're done, no round-trip to origin. But they can't be revoked before they expire. That's unacceptable for a compromised session.
The resolution is a hybrid: keep the common path stateless, add a compact, edge-replicated revocation set for the rare case.
async function verifySession(token: string): Promise<Session | null> {
const claims = await verifyJwt(token, publicKey); // fast path
if (!claims) return null;
// Only revoked sessions hit this set; it's tiny and edge-cached.
if (await revocationSet.has(claims.sid)) return null;
return claims;
}The result: signature-check latency in the common case, seconds-to-revoke when it matters.
Tenancy belongs in the database#
Application code has bugs. A single query that forgets a WHERE org_id = ? is a
cross-tenant data leak. So isolation shouldn't live in application code alone.
I enforce it at the database layer with row-level security tied to the authenticated organization:
- Every tenant-scoped table has an RLS policy.
- The policy references the current org from the session context.
- Application bugs fail closed instead of leaking.
The data model is the product#
In B2B auth, organizations, memberships, invitations, and roles aren't add-ons — they're the core. Getting them right early avoids painful migrations when your first enterprise customer asks for SSO and nested teams.
Design the SDK before the API#
We designed the developer experience of the SDK before finalizing the HTTP API. This kept the API honest: if something was awkward to express in the SDK, the API was wrong.
The "5-minute integration" promise is only real if you work backwards from the developer's first five minutes.
Secure defaults win#
Every unsafe option we removed increased adoption. Developers don't want a hundred knobs — they want the right thing to happen when they do nothing special. Make the secure path the easy path, and you've built auth people don't have to think about.