SLICE-2: identity — email + one-time-code sign-up, sessions, Landing/Sign-in (SD-0001 §7.2) #6

Merged
ben.stull merged 10 commits from slice-2-identity into main 2026-06-10 17:51:19 +00:00
Owner

SLICE-2 — Identity (SD-0001 §7.2)

Open, passwordless identity on top of the SLICE-1 walking skeleton. Completes PUC-1, PUC-2 (+a/b/c), PUC-3, PUC-9; realizes BUC-1/BUC-2.

What changed

  • accounts domainrequest_code / verify / get_account, enforcing INV-2 (email canonical key, no duplicate accounts; ON CONFLICT race-safe) and INV-3 (one-time codes handled like secrets: HMAC-peppered hash only, 10-min TTL, single-use, ≤5 attempts, 60s resend cooldown). All rules in the domain once (INV-6); the BFF owns no logic.
  • platform/mailer — one-method send() port + LogMailer (dev/test): the code lands in an in-memory outbox (tests) and the backend log (PUC-10 dev channel). SmtpMailer is SLICE-4.
  • platform/session — stdlib-HMAC signed, stateless cookie sessions (no session table, §6.2); constant-time verify, tamper-proof.
  • BFF /api/auth/*request-code (uniform 204, no enumeration, §6.6), verify (200 + session cookie + {account, storefront, created}), me, logout (PUC-9). §6.4 error envelope; storefront hard-wired null behind a _storefront_for seam SLICE-3 fills.
  • Frontend — Landing + two-step Sign-in screens, typed API client, the exhaustive entry-routing rule (§6.5) unit-tested with Vitest (§6.8), and a signed-in placeholder making sign-out reachable.
  • Dev-log fix — surface ecomm.* INFO so uvicorn shows the code (PUC-10).

Tests / gate

scripts/check.sh green: import-linter contract kept, 43 backend tests (scenario + INV-2/INV-3 invariants + no-enumeration), frontend build + 3 Vitest routing tests. Verified end-to-end on localhost over HTTP (request-code → code-in-log → verify+cookie → me → logout).

Deferred to SLICE-4 (final review I-1/I-2; spec scopes delivery_failed there)

When SmtpMailer lands: send-before-commit (don't orphan a code / trip the cooldown on delivery failure) and add the 502 delivery_failed handler (INV-9). E2E browser tests deferred per spec §6.8.

🤖 Generated with Claude Code

## SLICE-2 — Identity (SD-0001 §7.2) Open, passwordless identity on top of the SLICE-1 walking skeleton. Completes PUC-1, PUC-2 (+a/b/c), PUC-3, PUC-9; realizes BUC-1/BUC-2. ### What changed - **`accounts` domain** — `request_code` / `verify` / `get_account`, enforcing **INV-2** (email canonical key, no duplicate accounts; `ON CONFLICT` race-safe) and **INV-3** (one-time codes handled like secrets: HMAC-peppered hash only, 10-min TTL, single-use, ≤5 attempts, 60s resend cooldown). All rules in the domain once (**INV-6**); the BFF owns no logic. - **`platform/mailer`** — one-method `send()` port + `LogMailer` (dev/test): the code lands in an in-memory outbox (tests) and the backend log (PUC-10 dev channel). `SmtpMailer` is SLICE-4. - **`platform/session`** — stdlib-HMAC signed, stateless cookie sessions (no session table, §6.2); constant-time verify, tamper-proof. - **BFF `/api/auth/*`** — `request-code` (uniform 204, no enumeration, §6.6), `verify` (200 + session cookie + `{account, storefront, created}`), `me`, `logout` (PUC-9). §6.4 error envelope; `storefront` hard-wired `null` behind a `_storefront_for` seam SLICE-3 fills. - **Frontend** — Landing + two-step Sign-in screens, typed API client, the exhaustive entry-routing rule (§6.5) unit-tested with Vitest (§6.8), and a signed-in placeholder making sign-out reachable. - **Dev-log fix** — surface `ecomm.*` INFO so uvicorn shows the code (PUC-10). ### Tests / gate `scripts/check.sh` green: import-linter contract kept, **43 backend tests** (scenario + INV-2/INV-3 invariants + no-enumeration), frontend build + **3 Vitest** routing tests. Verified end-to-end on localhost over HTTP (request-code → code-in-log → verify+cookie → me → logout). ### Deferred to SLICE-4 (final review I-1/I-2; spec scopes `delivery_failed` there) When `SmtpMailer` lands: send-before-commit (don't orphan a code / trip the cooldown on delivery failure) and add the `502 delivery_failed` handler (INV-9). E2E browser tests deferred per spec §6.8. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
ben.stull added 10 commits 2026-06-10 17:51:06 +00:00
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Email-canonical get-or-create, peppered-hash one-time codes with 10-min TTL, single-use,
5-attempt cap, and 60s resend cooldown; uniform new-or-known (no enumeration, §6.6). All
identity rules in the domain layer once (INV-6); scenario + invariant tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Screen-shaped JSON endpoints; signed-cookie session on verify; §6.4 error envelope;
storefront hard-wired null behind the _storefront_for seam (SLICE-3 fills it). No
enumeration: uniform 204. PUC-9 logout clears the cookie.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two-step email + one-time-code sign-in behind both doors; pure entry-routing rule unit-
tested with Vitest (§6.8); storefront routing reaches a SLICE-2 signed-in placeholder with
sign-out (PUC-9). check.sh now runs the frontend unit tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
uvicorn configures only its own loggers, so the LogMailer INFO line — PUC-10's local dev
channel (the code in the backend log) — was swallowed. Add an idempotent dedicated handler
for the 'ecomm' logger (propagate=False) in create_app so dev.sh/uvicorn runs show the code.
Caught by the SLICE-2 end-to-end walk; unit tests read the in-memory outbox and didn't.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
docs(slice-2): implementation plan for the identity slice
ci / check (push) Has been cancelled
ci / check (pull_request) Has been cancelled
725877c5c6
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ben.stull merged commit 1b40bde4a0 into main 2026-06-10 17:51:19 +00:00
Sign in to join this conversation.