Source: ddx-documentation/adr/0002-auth-better-auth.md
## Status Accepted 2026-10-23 (owner Q1, confirmed by Q13). ## Context The operator asked for "advanced auth V5". Read as feature parity with the HMS Auth.js v5 beta: credentials, WebAuthn passkey and the org in the session (Q1). The workspace also needs teams, access control, API keys (service and user scope) and a JWT that ddx-api issues and verifies. Evidence: [../research/rq3-auth-matrix.md](/en/docs/research/rq3-auth-matrix). Harness canon: better-auth 1.7 for new projects, Auth.js v5 legacy only (`~/.claude/ddx-topic-flows/VERSIONS.md` `betterauth1`). ## Decision Use **better-auth 1.7** inside ddx-api, with the `organization` plugin (teams + access control), `apiKey`, `passkey` and `jwt` plugins. ddx-api issues the JWT; ddx-web reaches auth only through the BFF `api/v1`. Auth data lives in `ddx_auth` ([ADR 0004](/en/docs/adr/0004-database-split)). In ddx-web, `proxy.ts` does the optimistic cookie check and the DAL does the real check in every page and route handler. ## Rejected alternative **Auth.js v5.** Rejected: no built-in organizations, teams, access control or API keys; WebAuthn is experimental; it does not issue a JWKS-backed JWT for a separate backend. Each gap would be custom code. It is legacy in the harness canon. ## Consequences - One credential stack, three credential types (session, user API key, service API key) in one ddx-api guard chain — RQ9 designs the chain. - Plugin option names are checked against the installed types at scaffold time. - `nextCookies()` goes LAST if any better-auth client runs in ddx-web.
Accepted 2026-10-23 (owner Q1, confirmed by Q13).
The operator asked for "advanced auth V5". Read as feature parity with the HMS Auth.js v5
beta: credentials, WebAuthn passkey and the org in the session (Q1). The workspace also
needs teams, access control, API keys (service and user scope) and a JWT that ddx-api
issues and verifies. Evidence: .
Harness canon: better-auth 1.7 for new projects, Auth.js v5 legacy only
(~/.claude/ddx-topic-flows/VERSIONS.md betterauth1).
Use better-auth 1.7 inside ddx-api, with the organization plugin (teams + access
control), apiKey, passkey and jwt plugins. ddx-api issues the JWT; ddx-web reaches
auth only through the BFF api/v1. Auth data lives in ddx_auth
(). In ddx-web, proxy.ts does the optimistic
cookie check and the DAL does the real check in every page and route handler.
Auth.js v5. Rejected: no built-in organizations, teams, access control or API keys; WebAuthn is experimental; it does not issue a JWKS-backed JWT for a separate backend. Each gap would be custom code. It is legacy in the harness canon.
nextCookies() goes LAST if any better-auth client runs in ddx-web.adr/
deploy/
research/