Status: Accepted and implemented core architecture for Epic #1 / issue #51
Decision date: 2026-08-09
This document is normative for customer authentication, account admission, and the boundary between authentication and LemmaComputer authorization. Agents implementing Epic #1 identity work must read it before changing authentication, sessions, invitations, organization admission, enterprise SSO, or deployment profiles.
LemmaComputer will use Better Auth as the customer authentication framework. Better Auth is embedded in the existing Control API at first; it is not a separate service or a per-tenant container. Horizontally scaled Control API replicas share one logical authentication database in the hosted profile.
Better Auth owns customer authentication mechanics:
- email and password authentication, verification, reset, and recovery;
- TOTP, passkeys, backup codes, and authentication sessions;
- Google and Microsoft social OAuth;
- tenant-configured SAML and OIDC protocol handling;
- authentication-provider account links and provider token custody.
LemmaComputer remains authoritative for:
- stable product accounts;
- organizations and protected ownership;
- invitations and membership lifecycle;
- organization roles, permissions, and resource scopes;
- active-organization selection and product-session authorization context;
- tenant placement into pooled or dedicated data planes;
- platform-operator authority and tenant-support elevation;
- product authorization and audit decisions.
The defining rule is:
Better Auth proves who authenticated. LemmaComputer decides which organization and resources that account may use.
Run multiple Better Auth-bearing Control replicas for availability, not one Better Auth deployment per hosted tenant. A distinct Better Auth realm is reserved for a customer-managed installation or an explicitly contracted identity-silo deployment.
The Control API previously implemented Microsoft Entra OIDC directly. That
adapter and the hosted External ID adapter have been removed. The embedded
Better Auth runtime owns customer authentication under
/api/v1/auth/customer/* in every deployment profile. After any configured
method authenticates a person, Control maps the stable
authentication account into the existing product domain:
account_usersis the stable product account;external_identitiesrecords immutable provider identity links;organizationsandorganization_membershipsown tenant access;browser_sessionsbind product access to an active membership;- the product permission catalog and policies own authorization.
That domain foundation is valuable and remains in place. Better Auth replaces the provider-specific customer authentication mechanics; it does not replace the product organization and authorization model.
The internal tenant boundary is universal, but the customer experience is not.
A verified hosted consumer can bootstrap exactly one personal tenant without
entering an organization name. The personal tenant still owns its membership,
workspaces, credentials, policy, usage, and audit scope. A company remains an
explicit organization tenant that can admit multiple members and configure
enterprise identity. One account may belong to both kinds and explicitly
switch its active product membership. SSO configures how an organization's
members authenticate; it never changes tenant kind or compute placement.
This decision deliberately changes the earlier managed-CIAM trust boundary. The open-source Better Auth framework implements password hashing, TOTP, passkeys, OAuth, SAML, OIDC, session security, and secret encryption, while the LemmaComputer deployment operates the database, encryption keys, email delivery, availability, backup, monitoring, abuse defenses, and incident response. LemmaComputer must never replace Better Auth's cryptographic and protocol implementations with bespoke password or MFA code.
Better Auth's core repository and @better-auth/sso package are MIT licensed:
- A customer can create and recover an account without a Microsoft account.
- Email/password, TOTP, passkeys, Google, and Microsoft are compatible login methods for the same stable product account.
- A tenant can configure its own SAML or OIDC provider without changing product roles or authorization semantics.
- One account can belong to multiple organizations and explicitly select one active membership per product authorization context.
- Standard hosted tenants can use pooled data; enterprise tenants can use dedicated databases or VPCs without duplicating authentication by default.
- Hosted and customer-managed profiles use the same Better Auth integration and product authorization contracts from the same product codebase.
- Authentication, organization admission, and resource authorization fail closed and remain independently testable.
- Better Auth does not become the product policy engine.
- Identity-provider groups, roles, or email domains do not grant LemmaComputer permissions.
- A dedicated enterprise data plane does not automatically imply a dedicated identity realm.
- Platform operators do not receive authority from the customer account realm.
- Application startup does not run Better Auth database migrations.
- The first implementation does not attempt active-active, globally replicated authentication across regions.
flowchart LR
Browser["Customer browser"]
Edge["LemmaComputer web ingress"]
Control["Control API replicas<br/>Better Auth embedded"]
Providers["Google, Microsoft,<br/>customer SAML or OIDC"]
AuthDB[("Authentication database<br/>users, credentials, sessions,<br/>TOTP, passkeys, SSO providers")]
ControlDB[("Control database<br/>accounts, organizations,<br/>memberships, invitations,<br/>roles, session context, audit")]
Placement["Tenant placement resolver"]
Pool[("Pooled business database<br/>tenant_id isolation")]
EnterpriseDB[("Dedicated enterprise database")]
EnterpriseVPC["Dedicated enterprise data plane<br/>optional private VPC ingress"]
Browser --> Edge
Edge --> Control
Control <--> Providers
Control --> AuthDB
Control --> ControlDB
Control --> Placement
Placement --> Pool
Placement --> EnterpriseDB
Placement --> EnterpriseVPC
Better Auth belongs to the customer identity control plane. Pooled and dedicated customer business stores are data-plane placement choices. Changing an organization's data-plane placement must not require changing its stable account or authentication-provider links.
Better Auth is a TypeScript framework and can be mounted inside the current
Fastify-based Control API under a customer-authentication route namespace such
as /api/v1/auth/customer/*.
This is preferred initially because it:
- introduces no new app-to-auth network call or service-discovery dependency;
- requires no mTLS boundary between Control and Better Auth;
- reuses the existing authentication boundary and ingress;
- packages cleanly in both hosted and customer-managed profiles;
- lets every Control replica use the same durable authentication store;
- avoids another independently deployed service before scale justifies it.
flowchart TB
Ingress["Ingress or load balancer"]
Control1["Control API replica 1<br/>Better Auth embedded"]
Control2["Control API replica 2<br/>Better Auth embedded"]
ControlN["Control API replica N<br/>Better Auth embedded"]
AuthDB[("Shared authentication database")]
ProductDB[("Shared product control database")]
Ingress --> Control1
Ingress --> Control2
Ingress --> ControlN
Control1 --> AuthDB
Control2 --> AuthDB
ControlN --> AuthDB
Control1 --> ProductDB
Control2 --> ProductDB
ControlN --> ProductDB
A future dedicated identity service is allowed behind the same internal authentication contract if independent scaling, multiple products, regional identity planes, separate security ownership, or a contractual isolation boundary justifies the operational cost. That later service boundary requires explicit service authentication, network policy, failure behavior, deployment ownership, and potentially mTLS. It is not part of the initial decision.
Better Auth requires durable storage for users, credential accounts, provider accounts, sessions, verification records, TOTP material, passkeys, and SSO provider configurations. See the Better Auth database documentation.
Use two logical databases with separate roles in one PostgreSQL cluster at first:
PostgreSQL cluster
├── lemmacomputer_auth
│ └── Better Auth-owned schema and migrations
└── lemmacomputer_control
└── LemmaComputer product domain and migrations
| Store | Authority | Contents |
|---|---|---|
lemmacomputer_auth |
Better Auth integration | Users, credentials, provider accounts, auth sessions, verifications, TOTP, passkeys, SSO provider configuration |
lemmacomputer_control |
LemmaComputer | Product accounts, organizations, memberships, invitations, product session context, roles, permissions, placement, audit |
| Pooled business database | LemmaComputer data plane | Standard-tier customer records, all explicitly tenant-scoped |
| Dedicated business database | LemmaComputer data plane | One contracted enterprise tenant or deployment stamp |
The local Compose profile may use the existing PostgreSQL container for both logical databases. Production may initially use the same managed PostgreSQL cluster, with separate database credentials and privileges. A later dedicated authentication cluster is an operational scaling or blast-radius choice, not a tenant-model requirement.
Do not create cross-database foreign keys. Control enforces the account mapping
idempotently. Prefer UUID Better Auth user identifiers and use the same UUID as
account_users.id so the stable mapping is direct and provider-neutral.
Better Auth and product schema changes use separate migration streams and separate migration roles. Better Auth's CLI may generate candidate schema, but generated SQL must be reviewed and incorporated into a pinned, forward-only authentication migration job. Application startup checks compatibility and never migrates either database.
Better Auth user.id
-> account_users.id
-> organization membership A
-> organization membership B
-> organization membership C
Better Auth's provider-account table is authoritative for login methods and
credential-provider links. The existing external_identities table remains as
a compatibility and audit projection during migration. It must not create a
second independent provider-account authority.
Email remains mutable display/contact data. Never merge identities, choose a tenant, accept an invitation, or grant a role based only on matching email. Account linking requires authenticated proof of both identities or an explicit, audited recovery process.
The target has one Better Auth browser session cookie. LemmaComputer maintains a server-side product authorization context keyed to the validated Better Auth session identifier. It does not copy the raw authentication token.
sequenceDiagram
participant Browser
participant Control as Control API and Better Auth
participant Provider as Password or external provider
participant AuthDB as Authentication database
participant IAM as LemmaComputer IAM
participant ControlDB as Product control database
participant Data as Selected tenant data plane
Browser->>Control: Start sign-in
Control->>Provider: Authenticate when federation is selected
Provider-->>Control: Verified callback
Control->>AuthDB: Create or load user and auth session
Control-->>Browser: Secure Better Auth session cookie
Browser->>Control: Request protected resource
Control->>AuthDB: Validate session
AuthDB-->>Control: Auth user and session ID
Control->>IAM: Resolve active product session context
IAM->>ControlDB: Check account, organization and membership
ControlDB-->>IAM: Active membership and effective permissions
IAM-->>Control: AuthenticatedPrincipal
Control->>Data: Route using admitted organization placement
Data-->>Browser: Authorized tenant-scoped result
The product authorization context records at minimum:
- Better Auth session identifier;
account_user_id;- active
organization_membership_id; - creation, last-seen, and recent-step-up timestamps;
- revocation state.
Every protected request must still verify that the account, organization, and membership are active; compute permissions server-side; and prove the requested resource belongs to the active organization. Suspending a membership therefore denies product access immediately even when the underlying authentication session is still valid.
Product logout revokes the product authorization context. Full logout, account
disablement, password compromise, and "sign out all devices" also invoke Better
Auth session revocation. The current lemmacomputer_session may remain during a
bounded migration, but two independent long-term browser authentication cookies
are not the target.
The default hosted identity plane is pooled. One stable Better Auth account may hold multiple organization memberships with different roles. The explicitly selected active membership determines authorization and data-plane routing.
Better Auth user
├── Organization A: owner -> pooled data plane
├── Organization B: member -> pooled data plane
└── Organization C: auditor -> dedicated enterprise data plane
A tenant-specific business database or VPC does not require a tenant-specific Better Auth container. Authentication remains in the control plane unless the customer specifically contracts for identity-plane isolation or residency.
An optional identity-silo tier may deploy a separate Better Auth realm when a customer requires a fully private login path, customer-specific identity keys, strict identity-data residency, no shared identity database, or air-gapped operation. It is a premium exception because it fragments accounts, migrations, provider registrations, backup, recovery, patching, and multi-organization membership.
Every customer-managed installation naturally runs its own Control API with embedded Better Auth and its own authentication database. It supports exactly one product organization and has no required call to a LemmaComputer-hosted identity plane.
The customer operator may enable the methods appropriate to the environment:
- local email/password and TOTP;
- passkeys;
- Google or Microsoft OAuth where Internet access is allowed;
- internal or external SAML/OIDC;
- invitation-only admission with public signup disabled.
This preserves the same-product-codebase invariant while keeping identity, secrets, database, backup, and network custody inside the customer deployment.
The open-source @better-auth/sso package provides SAML and OIDC protocol
functionality, provider registration, callbacks, provider discovery, and
provisioning hooks. Better Auth's hosted self-service SSO product is not a
dependency: LemmaComputer builds and operates the tenant-facing configuration,
test, enforcement, recovery, and rotation UI on top of the package APIs.
sequenceDiagram
participant Admin as Tenant administrator
participant UI as People and Access UI
participant Control as Control API
participant SSO as Better Auth SSO plugin
participant AuthDB as Authentication database
participant IdP as Customer identity provider
Admin->>UI: Configure company SSO
UI->>Control: Submit SAML metadata or OIDC settings
Control->>Control: Require tenant IdP-management permission
Control->>SSO: Register provider for organization ID
SSO->>AuthDB: Store provider configuration securely
SSO-->>Control: Return callback and metadata information
Control-->>UI: Show setup and test state
Admin->>UI: Test connection
UI->>SSO: Begin test sign-in
SSO->>IdP: SAML or OIDC flow
IdP-->>SSO: Verified response
SSO-->>UI: Connection test result
Admin->>UI: Enforce SSO only after successful test
Do not expose Better Auth's provider-registration endpoints directly to the browser. A Control route guarded by LemmaComputer's tenant-local IdP-management permission invokes them server-side. Better Auth stores credential material; the product control database stores non-secret status, organization association, enforcement policy, and audit references.
The Better Auth Organization plugin is not the LemmaComputer authorization authority. LemmaComputer already has the richer organization, invitation, last-owner, session-revocation, permission-catalog, and resource-scope domain. SSO provisioning hooks may resolve a verified Better Auth account, but product membership admission remains an explicit LemmaComputer transaction. Provider groups and claims never assign product roles automatically.
SSO must be tested before enforcement and retain an audited owner recovery path to prevent tenant lockout. SAML configuration must enable production-strict timestamp, request, signature, size, and replay validation.
LemmaComputer invitations continue to bind the intended organization and role. They do not create a Better Auth credential and do not choose a password.
- An authorized tenant administrator creates an invitation.
- LemmaComputer sends one single-use, expiring activation link through the shared transactional email adapter. Explicit copy-link delivery is limited to local and customer-managed operation; hosted rejects it.
- Control exchanges the raw link token once for a short-lived opaque context
in an
HttpOnly,SameSite=Laxcookie. Only hashes and the invitation delivery generation are persisted, and provider redirects contain no raw invitation capability. - The recipient authenticates or creates a Better Auth account using any enabled method.
- Control validates a database-backed, verified-email Better Auth session
whose
createdAtis within the invitation reauthentication window, then checks the exact normalized email and current invitation generation. - LemmaComputer atomically activates the predetermined membership, binds the product context to the Better Auth session, consumes the activation context, and records invitation and login audit events.
- The role comes only from the invitation and product transaction.
An identity-provider email or role claim cannot redirect the invitation into a different organization or increase its authority. Expired, revoked, superseded, replayed, wrong-email, stale-session, and cross-organization attempts fail with the same public invitation error. Creation and resend are idempotent; delivery and activation attempts are durably rate limited.
Platform operators remain outside the customer Better Auth realm. Hosted and worktree deployments use a separate Better Auth passkey realm with its own database, signing keys, host-only cookie namespace, session audience, and role model. Customer-managed deployments do not expose the platform realm.
Platform operator
-> isolated platform Better Auth passkey
-> separate platform session
-> platform role
-> time-bound audited support elevation
Customer
-> Better Auth
-> product account
-> organization membership
-> tenant-local permission
Never add a permanent customer-account is_global_admin bypass. Platform
support access requires target tenant, reason, scope, expiry, recent step-up,
audit, and configured approval.
Enrollment is deliberately narrower than a general login provider. A one-time secret creates the configured first platform administrator through an internal-only credential route; the browser can otherwise call only the passkey surface. Hosted requires an explicit operator email and HTTPS. After the first verified passkey is registered, Control deletes the credential account and all bootstrap sessions, and the deployment secret may be removed. Subsequent access is passkey-only with required resident credentials and user verification. Customer sessions, customer cookies, and organization SSO never authenticate this realm. Worktree uses the same flow with a generated local identity and loopback origin.
The implementation must follow the Better Auth security reference and apply the following deployment controls:
- pin Better Auth and plugin versions and monitor security advisories;
- set exact HTTPS base URLs and trusted origins;
- never disable CSRF, origin, state, nonce, or PKCE protections;
- keep browser cookies secure, HttpOnly, SameSite, and host-only unless a reviewed flow requires otherwise;
- store Better Auth encryption secrets outside PostgreSQL and rotate them with versioned keys;
- enable
account.encryptOAuthTokens; token encryption is not enabled by default; - require email verification before credential-account activation and use non-enumerating signup and recovery responses;
- require verified TOTP enrollment and protected single-use backup codes;
- require MFA and recent step-up for ownership, SSO enforcement, recovery, billing ownership, tenant closure, and platform-sensitive actions;
- revoke relevant sessions after password reset, account disablement, identity unlinking, recovery, or compromise;
- use shared rate-limit storage across replicas plus edge WAF and abuse controls;
- trust forwarding headers only from explicitly trusted proxies;
- use TLS and least-privilege database roles; mTLS is required only when a separately reviewed service or cross-boundary topology needs it;
- encrypt backups, test restoration, and include the authentication database in recovery objectives;
- audit signup, login, failure, logout, account link/unlink, MFA changes, recovery, SSO configuration, invitation activation, and session revocation;
- keep passwords, OTPs, TOTP QR material, raw session tokens, provider tokens, client secrets, and private keys out of application logs and product audit payloads.
- Better Auth or authentication-database outage denies new login and session validation; it must not fall back to unauthenticated access.
- Product-control-database outage denies organization authorization even when a Better Auth session is valid.
- Tenant data-plane outage affects only tenants placed on that data plane and does not grant access to another placement.
- External provider outage leaves independent enabled methods available where policy permits; enforced enterprise SSO requires a documented owner recovery process.
- Authentication secret rotation uses Better Auth's versioned secret support so in-flight sessions and encrypted records have a bounded migration path.
- A suspected database and encryption-key compromise is treated as credential, provider-token, MFA-secret, and session compromise, with forced revocation and recovery.
The provider-neutral boundary, Better Auth foundation, universal customer login, invitations, tenant-configured enterprise SSO, and Microsoft adapter contraction are implemented. There are no direct workforce-Entra or External ID customer authentication routes or deployment inputs.
- Record this decision in issue #51 and its threat model.
- Introduce a provider-neutral
AuthenticatedPrincipaland authentication capability contract. - Remove provider-specific product authority from the authentication boundary.
- Add the logical authentication database and explicit migration job.
- Embed Better Auth in Control API.
- Add UUID account mapping and product authorization session context.
- Add email verification/reset delivery, password authentication, TOTP, passkeys, session revocation, and recovery audit.
- Add provider-neutral login UI.
- Add email/password, Google, and Microsoft methods.
- Add explicit proof-based account linking and unlinking.
- Add self-service organization creation with protected owner bootstrap.
- Preserve current invitation and membership lifecycle behavior.
- Activate the predetermined membership through any enabled Better Auth method.
- Remove the requirement to pre-create a customer in Microsoft External ID.
- Add tenant-admin SAML/OIDC configuration and test UI.
- Add domain verification, test-before-enforcement, recovery, audit, and secret or certificate rotation.
- Stop creating new External ID-only customer identities.
- Link existing Microsoft identities only after authenticated proof.
- Never attempt to migrate Microsoft passwords or MFA secrets.
- External ID and workforce-Entra routes, configuration, and runtime adapters are removed.
- Hosted and worktree platform operators use the isolated passkey realm.
| Alternative | Decision |
|---|---|
| Microsoft Entra External ID | Rejected and removed as a product authentication dependency; Microsoft remains an optional Better Auth social provider or tenant SSO provider |
| Amazon Cognito | Reject as the default because hosted and customer-managed profiles would not share the same complete authentication implementation and operational contract |
| Auth0 or WorkOS | Do not select as the default because managed convenience increases vendor and pricing dependency; they remain future adapters if justified |
| Custom password, MFA, SAML, or OIDC implementation | Reject because the cryptographic and protocol risk is unacceptable |
| Better Auth | Select for qualification because it is MIT licensed, TypeScript-native, database-backed, and supports local credentials, TOTP, passkeys, social OAuth, SAML, and OIDC across both product profiles |
Benefits:
- one authentication implementation across hosted and customer-managed profiles;
- no Microsoft or AWS account requirement for ordinary customers;
- optional Google, Microsoft, and enterprise SSO;
- no per-user customer provisioning in an external CIAM console;
- support for public, private, and customer-managed login topologies;
- no need to discard the existing product organization and RBAC foundation;
- enterprise data-plane isolation does not fragment authentication by default.
Costs and risks:
- LemmaComputer operates credential and MFA-secret custody;
- authentication availability, email delivery, abuse handling, backup, monitoring, and incident response become LemmaComputer responsibilities;
- a second reviewed database migration stream is required;
- dependency and security updates require an urgent, tested rollout path;
- Better Auth is a library dependency with schema and semantic versioning risk, even though it reduces external provider lock-in;
- compliance evidence must cover the complete LemmaComputer operation, not just Better Auth's implementation.
The following are ongoing release gates for the implemented Better Auth customer identity plane. A deployment must not claim support for a method until its applicable automated and human qualification proves:
- email signup, verification, password reset, and non-enumerating responses;
- TOTP enrollment, challenge, trusted-device policy, backup-code consumption, recovery, and session revocation;
- Google and Microsoft login plus explicit safe account linking;
- invitation activation into the predetermined organization and role;
- one account with multiple memberships and explicit active-organization switching;
- tenant suspension, account disablement, last-owner protection, and immediate product-access denial;
- SAML and OIDC registration, strict validation, provider rotation, test-before-enforcement, and tenant lockout recovery;
- no role grants from provider claims;
- pooled and dedicated data-plane routing from the same identity plane;
- customer-managed operation without a required LemmaComputer-hosted identity dependency;
- migration, backup/restore, secret rotation, rate limiting, proxy-header handling, audit redaction, and dependency upgrade rollback;
- adversarial cross-account, cross-session, cross-organization, and cross-data- plane isolation tests.
Issue #51 owns adoption of this architecture and its threat model. Downstream issues implement it in dependency order. If an issue body conflicts with this document, implementation must stop until issue #51 and this document are reconciled; agents must not silently choose a different authentication or tenancy model.