feat: add browser auth shell
CI / verify (pull_request) Successful in 38s
CI / infrastructure-smoke (pull_request) Successful in 12s
CI / migration-smoke (pull_request) Successful in 1m0s

This commit is contained in:
ekklesia
2026-08-23 22:01:26 -05:00
parent 91aa9c2930
commit a8c7c50b4d
26 changed files with 2702 additions and 194 deletions
+37 -2
View File
@@ -42,6 +42,33 @@ a module entrypoint, or starts a server. Real, working (non-throwing) module fix
production module-runtime startup/shutdown ordering and cleanup against a mocked pool/Better
Auth/server — see [Authorization](authorization.md#tests).
Phase 7B pass 1 adds browser-authentication evidence
([ADR 0034](../architecture/decisions/0034-host-owned-browser-authentication.md)). API tests pin the
two exact browser-boundary exceptions. Any body-bearing successful `2xx` from
`POST /api/auth/sign-in/email` preserves Better Auth's `Set-Cookie` and status but returns fixed
`{"ok":true}`, with no token/user body. `/api/auth/get-session`, `/api/auth/list-sessions`,
`/api/auth/update-session`, and `/api/auth/change-password` each receive the identical sanitized
`404` without invoking the identity handler; failures and every other supported auth route retain
their native semantics. The live browser transport additionally proves the host-only cookie and
that no token, session, user, or email appears in the sign-in body.
Shell protocol and React tests cover principal-first anonymous/authenticated/failure states; the
labelled email/password form and keyboard submission; autocomplete, pending state, and same-tick
duplicate suppression; password clearing; fixed credential-versus-service errors; no credential or
raw exception logging; fresh principal and navigation resolution after login; sign-out followed by
principal resolution; and retry without a full-page reload.
The same shell suites exercise session transitions under repeated timing: active module cleanup and
realtime teardown on sign-out/verified expiry; stale attempts after unmount; platform bootstrap
`401` mapping directly to unauthenticated; and deduplicated principal revalidation for both module
`401` and `403`. They prove an authenticated recheck preserves the module's original response,
session, and mounted UI; an anonymous recheck unmounts once and shows login; a revalidation failure
is a sanitized host failure; and a response from an already-unmounted frame cannot overwrite a newer
transition. Return/callback input is not part of the browser contract, and tests pin that external,
protocol-relative, backslash-prefixed, and scheme-bearing values cannot become navigation.
Modularity checks keep authentication out of the module UI SDK/context and Dashboard login code out
of the separate display bundle.
`pnpm verify` is intentionally Docker-free. It never plans or applies migrations against a live
database.
@@ -79,8 +106,9 @@ A second dedicated suite (`apps/api/tests/api-http.integration.ts`, included in
`pnpm test:migrations:integration` run) starts the real `apps/api` HTTP server against a freshly
migrated disposable database and exercises it with real HTTP requests: public sign-up (with no
automatic session, per `autoSignIn: false`), explicit sign-in producing a host-only
`HttpOnly`/`SameSite=Lax`/secure session cookie, `/api/v1/principal` for both the authenticated and
anonymous shapes, sign-out actually invalidating the session, a malformed or missing cookie
`HttpOnly`/`SameSite=Lax`/secure session cookie and fixed `{"ok":true}` body containing no token,
`/api/v1/principal` for both the authenticated and anonymous shapes, sign-out actually invalidating
the session, a malformed or missing cookie
resolving as anonymous, duplicate sign-up creating no second identity row, two distinct accounts
producing identical principal shapes with no elevation and no owner/role/permission data at the
database level, no plaintext password ever stored, the exact 8–128 password-length boundary (7
@@ -90,6 +118,13 @@ outage during principal resolution producing a sanitized `503` rather than a fal
starting the API against an unmigrated database creating no tables at all, and a clean listener/pool
shutdown.
The browser-authentication chain in that live suite also creates a development identity, invokes
Core's real single-use platform-owner bootstrap for that existing id, signs in through the real HTTP
transport, presents the returned cookie to `/api/v1/principal`, and reaches protected Dashboard
navigation with the owner authority. It proves composition across identity, authorization, and the
Dashboard resource; because production signup is prohibited, it deliberately does **not** claim to
prove a production first-user enrollment path.
A dedicated group within that suite proves session-store readiness against the real database rather
than a mock, since that is the only place a broken store looks exactly like an anonymous request: a
request carrying no cookies and one carrying only an unrelated cookie both resolve as anonymous