Skip to content

Architecture

saas-core is organized into vertical layers. Each layer has a clear responsibility and communicates with the layer below through defined interfaces.

HTTP Layer (Fastify)
├── /api/auth/* Better Auth handler (identity, OAuth, JWKS)
├── /api/admin/* Admin API (application management)
├── /api/me/* User self-service API
└── /api/commerce/* Commerce API (Phase 3)
Application Layer
├── identity/ Policy engine, membership, invitation, API key
├── auth/ Better Auth configuration, OAuth client helpers
├── oauth/ Auth routes, admin routes (registration)
└── commerce/ Product, subscription, payment adapters
Persistence Layer (Drizzle ORM + PostgreSQL)
├── auth-schema.ts Better Auth tables (generated, do not edit)
└── schema.ts Business tables (application, commerce, identity)

saas-core supports two distinct access patterns. They share the same backend but differ in how the frontend communicates with the auth server.

Path A: Direct Client (default for web apps)

Section titled “Path A: Direct Client (default for web apps)”
Browser on tobby.example.com
│ better-auth/react client
│ authClient.signIn.email({...})
saas-core at auth.example.com/api/auth/*
│ Handles signup, signin, session, token
│ Sets session cookie (Domain=.example.com)
Cookie shared across *.example.com subsdomains
  • Frontend uses Better Auth’s official client library
  • Session cookie is shared across subdomains (SSO by default)
  • Login UI is rendered by each SaaS application
  • Requires frontend deployment on a shared parent domain

Path B: OAuth Redirect (iOS, isolated apps, third parties)

Section titled “Path B: OAuth Redirect (iOS, isolated apps, third parties)”
Browser / iOS App
│ Redirect to /api/auth/oauth2/authorize?client_id=...
saas-core
│ Checks session → redirects to login or consent
│ Issues authorization_code → redirects back
Client exchanges code for JWT at /api/auth/oauth2/token
  • Standard OAuth 2.1 Authorization Code + PKCE
  • Login page and consent page hosted by saas-core
  • Required for iOS apps (no shared domain cookie)
  • Required for isolated products (cross-group session check)
EndpointHandlerPurpose
/api/auth/*Better AuthIdentity, OAuth, JWKS
/api/admin/*oauth/routes.tsApplication CRUD
/api/admin/.../auth-policyidentity/routes.tsPer-app policy management
/api/admin/.../usersidentity/routes.tsUser membership management
/api/admin/.../invitationsidentity/routes.tsInvitation management
/api/admin/.../api-keysidentity/routes.tsM2M key management
/api/me/*identity/user-routes.tsUser self-service
/api/commerce/*Phase 3Subscription, checkout