Design Principles
P1. Identity and commerce are separate concerns, one process
Section titled “P1. Identity and commerce are separate concerns, one process”Identity answers “who you are.” Commerce answers “what you bought.” What you can do is answered by each application. These three are separate concerns, but they live in the same saas-core process — not separate microservices. The module boundaries (identity/SSO/commerce) are the split points when scale demands it.
P2. Better Auth is the identity kernel, not a custom IdP
Section titled “P2. Better Auth is the identity kernel, not a custom IdP”Authentication primitives (registration, login, session, OAuth, social login) are provided by Better Auth, an embedded TypeScript library. saas-core does not reimplement password hashing, session tokens, or OAuth flows. Custom IdP deployments (Logto, Keycloak) are intentionally avoided—they add deployment complexity without benefit for a single-operator product suite.
P3. saas-core is the OAuth Server. Each SaaS is an OAuth Client.
Section titled “P3. saas-core is the OAuth Server. Each SaaS is an OAuth Client.”Every application registered in saas-core gets a unique client_id and
audience. JWTs are issued with per-application audience values:
// Acme's token{"sub":"user_abc","aud":"tobby-api","client_id":"tobby-web"}// Beta's token{"sub":"user_abc","aud":"studio-api","client_id":"studio-web"}A Acme token presented to Beta is rejected at signature verification because
the aud claim doesn’t match. This is the security baseline for subscription
isolation.
P4. SSO = session reuse, not token sharing
Section titled “P4. SSO = session reuse, not token sharing”A shared cookie domain (.example.com) means the browser sends the same
session cookie to every subdomain. When the user visits a new application in
the same SSO group, the session is immediately recognized — no redirect,
no re-authentication. Applications in different SSO groups require explicit
re-authentication at the authorize endpoint.
P5. Identity is universal. Commerce is per-application.
Section titled “P5. Identity is universal. Commerce is per-application.”The user table contains no app_id — a user who registers on any application
exists globally. The subscription table contains app_id on every row —
Acme Pro does not entitle Beta access. This dual shape enables cross-product
identity (one person, multiple products) with per-product subscription isolation.
P6. Record facts, do not interpret entitlements
Section titled “P6. Record facts, do not interpret entitlements”saas-core stores that User X has an active subscription to tobby.pro.monthly.
The mapping from SKU to feature set (1000 AI calls, 50GB storage) is the
application’s responsibility. saas-core never builds an entitlements table.