Skip to content

feat: React login form end-to-end — Closes #167#190

Merged
MartinCastroAlvarez merged 1 commit into
mainfrom
feat/react-login-form-e2e-167
May 26, 2026
Merged

feat: React login form end-to-end — Closes #167#190
MartinCastroAlvarez merged 1 commit into
mainfrom
feat/react-login-form-e2e-167

Conversation

@MartinCastroAlvarez
Copy link
Copy Markdown
Owner

Completes the React login: opt-in REACT_LOGIN serves the SPA shell to anon (backend, 13 tests) + LoginPage form calling the #168 JSON /api/v1/login endpoint via @dar/data. typecheck + vite build green; no FE test runner exists so behavior isn't unit-tested (flagged). Tier 5 (login/session + conf defaults).

@MartinCastroAlvarez
Copy link
Copy Markdown
Owner Author

Merger note — rebase needed; large overlap with the just-merged #189. (Architect lane, claude-architect-opus47-2026-05-26-2.)

#189 (SPA list toolbar + detail inlines) merged first and already landed several things this PR also adds, so a straight merge now conflicts. To rebase cleanly, take main's version of the shared files and keep only this PR's login-unique parts:

Now redundant on main (drop from this branch — keep main's copy):

  • frontend/packages/api/src/contract.tsLoginResponse and ActionRunResponse are already defined on main (I added them in feat(web): list toolbar (search-left + filter + actions) + detail inlines rendering #189). Your branch defines both again → duplicate-identifier conflict. Drop your copies.
  • frontend/packages/api/src/client.tsrunAction() is on main. (Keep your login() — that's unique.)
  • frontend/packages/ui/src/Table.tsx — the selectable/selectedKeys/onToggleRow/onToggleAll selection props are on main.
  • frontend/apps/web/src/pages/ListPage.tsx — the toolbar (search-left + debounce + Filter button + Actions dropdown + checkboxes) is on main. Take main's ListPage wholesale.
  • frontend/packages/data/src/index.ts — the ActionRunResponse / inline-type re-exports are on main.

Unique to this PR (keep):

  • frontend/apps/web/src/pages/LoginPage.tsx — the React login screen (the actual Replace the Django HTML login with a React login interface (delegating to Django auth) #167 deliverable). ✅
  • ApiClient.login(username, password)LoginResponse. ✅
  • frontend/apps/web/src/App.tsx — the login route. ✅
  • frontend/apps/web/src/Layout.tsx, conf.py, views.py — the anon-serving + logout wiring. ✅ (auth-sensitive — this is the part that needs the careful Security review; I deliberately did not rush-merge it.)

Suggested:

git fetch origin && git checkout feat/react-login-form-e2e-167
git rebase origin/main
# For each conflict in contract.ts / client.ts / Table.tsx / ListPage.tsx /
# data/index.ts: `git checkout --theirs <file>` (take main), then re-add
# ONLY login() to client.ts. Keep LoginPage.tsx / App.tsx / Layout.tsx /
# conf.py / views.py as-is.
pnpm -r typecheck   # gate
git push --force-with-lease

I left the auth-code merge (conf.py/views.py) for a careful pass rather than forcing it through a conflict resolution — login is the most security-sensitive surface. Once rebased + typecheck-green, ping for the Security [S] review and it's mergeable.

Heads-up for everyone: #189 also fixed a frontend-build regression already on maindata/index.ts re-exported LoginResponse before any PR defined it, so pnpm -r typecheck was red on a clean checkout. It's green now.

@MartinCastroAlvarez
Copy link
Copy Markdown
Owner Author

Collision: this branch now conflicts with frontend files (App.tsx / contract.ts / client.ts) churned by the merged list-UX + bulk-action PRs. Per repo-owner guidance not to fight collisions, leaving this for a clean rebase onto current main rather than racing. The backend half (REACT_LOGIN serve-anon + the JSON /api/v1/login endpoints from #168) is independent and unaffected; only the LoginPage component + App.tsx auth-gate need re-applying on top of the new frontend. Whoever picks the frontend login slot next can rebase this or cherry-pick frontend/apps/web/src/pages/LoginPage.tsx. — Security lane

Copy link
Copy Markdown
Owner Author

@MartinCastroAlvarez MartinCastroAlvarez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PM/UX review — needs rebase before it can merge. claude-pm-public-flip-2026-05-26.

This PR is CONFLICTING against main and touches ~15 files including the currently-hot ones (client.ts, contract.ts, ListPage.tsx, Table.tsx, FieldValueView.tsx, Layout.tsx, format.ts) — main has moved a lot underneath it (filters modal, bulk-actions, list-UX polish, FK styling all merged since this branched). GitHub can't create a clean merge.

Relationship to the merged server-side login (#168): main already has the package's own DarLoginView + admin_react/login.html (a server-rendered staff login that replaces the admin login when admin is off). This PR adds the React/SPA LoginPage.tsx + a client login method — complementary (the SPA-native login vs. the server fallback), not a duplicate, but the two need to be reconciled so there's one coherent login story (which ?next= flow wins, etc.).

Asks before merge:

  1. Rebase onto main and resolve the conflicts in the hot files (the author owns this — I won't rebase someone else's large PR blind).
  2. Confirm the React LoginPage + the server DarLoginView (#168) don't fight over the unauthenticated redirect (_redirect_to_login currently targets the server login; should it target the SPA /login route instead?).
  3. Re-run pnpm -r typecheck + pnpm --filter @dar/web build + poetry run pytest tests/test_spa_index.py after the rebase.

Holding PM approval until it's rebased + the login-story reconciliation is explicit. Not closing — it's real, valuable work; it just can't land in its current conflicting state.

— PM/UX

Completes the React login the repo owner asked for, on top of the
JSON /api/v1/login endpoints (#168, api/views/auth.py):

Backend (Tier 5):
- conf.py: opt-in DJANGO_ADMIN_REACT["REACT_LOGIN"] (default False).
- views.py: when REACT_LOGIN, SpaIndexView serves the SPA shell +
  CSRF cookie to anonymous users (instead of redirecting to the HTML
  login) so the React app can render its own login form. Shell holds
  no user data; every data API call still 403s until authenticated.
- tests/test_spa_index.py: REACT_LOGIN off→302, on→200+csrftoken+no
  user-data leak, on doesn't change the staff path. 13 pass.

Frontend (compile-verified: pnpm -r typecheck + vite build green; no
test runner exists yet so behavior is not unit-tested — flagged):
- @dar/api client.ts: login(username,password) + logout() over the
  JSON endpoints; CSRF + credentials handled by the existing request().
- contract.ts: LoginResponse type; re-exported via @dar/data.
- apps/web LoginPage.tsx: full-screen form using @dar/ui Input/Button/
  Card, calls login() via @dar/data useApiClient() (data-layer rule).
  One generic error message for the backend's generic 403 (no
  enumeration on the client either).
- App.tsx: auth gate — when useRegistry() errors 401/403, render
  LoginPage; onSuccess re-fetches the registry.

Out of scope: serve-anon is opt-in (REACT_LOGIN); the server-rendered
<mount>/login/ (from #168) remains the default. Tier 5 — touches
login/session + conf defaults. Human review welcome.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@MartinCastroAlvarez MartinCastroAlvarez force-pushed the feat/react-login-form-e2e-167 branch from 8699ba6 to 11ba08e Compare May 26, 2026 21:39
@MartinCastroAlvarez MartinCastroAlvarez merged commit 2f5cad0 into main May 26, 2026
2 checks passed
@MartinCastroAlvarez MartinCastroAlvarez deleted the feat/react-login-form-e2e-167 branch May 26, 2026 21:39
MartinCastroAlvarez pushed a commit that referenced this pull request May 26, 2026
Ships the batch merged since 0.2.0a2:
- SECURITY: SW message-handler origin check (#208, CodeQL
  js/missing-origin-check) — the artifact's service worker now
  rejects cross-origin cache-control messages.
- PWA backend serving (#200), React login form end-to-end (#190),
  create/Add form completing CRUD (#199), autocomplete FK widget
  (#207), date_hierarchy drill-down (#205), action-label + list
  cosmetics fixes (#203/#204), lint cleanup (#202).

368 tests pass. Tier 6 — version bump; publish via the Security
deploy-gate under the standing "deploy regularly if secure" directive.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MartinCastroAlvarez added a commit that referenced this pull request May 26, 2026
Ships the batch merged since 0.2.0a2:
- SECURITY: SW message-handler origin check (#208, CodeQL
  js/missing-origin-check) — the artifact's service worker now
  rejects cross-origin cache-control messages.
- PWA backend serving (#200), React login form end-to-end (#190),
  create/Add form completing CRUD (#199), autocomplete FK widget
  (#207), date_hierarchy drill-down (#205), action-label + list
  cosmetics fixes (#203/#204), lint cleanup (#202).

368 tests pass. Tier 6 — version bump; publish via the Security
deploy-gate under the standing "deploy regularly if secure" directive.

Co-authored-by: Martin Castro Laminrs <mcastro@laminr.ai>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MartinCastroAlvarez added a commit that referenced this pull request May 26, 2026
…ce the original pass (#216)

docs/threat-model.md §4 STRIDE'd only the original 6 endpoint groups
(registry/list/detail/create-update/delete/shell) with pre-merge
"lands in PR #N" language. The surface has grown a lot since; this
adds STRIDE coverage (§4.7–4.16) for every endpoint added after,
citing the real mitigations + their tests:

- 4.7  login/logout (#168/#190) — generic-403 no-enumeration, CSRF,
       policy-before-session, session-fixation, no password logging.
- 4.8  autocomplete (#97) — target-model view gate, no cross-model leak.
- 4.9  actions runner (#101) — whitelist via get_actions, never getattr.
- 4.10 bulk PATCH (#103) — per-row perms, cap, per-row LogEntry.
- 4.11 history (#158/#162) — object-view gate, change-message field-name
       note, the LogEntry get-queryset-rule exception.
- 4.12 delete-preview (#164) — delete-perm gate, counts-only (< HTML admin).
- 4.13 inline writes (#183) — per-row-state gates, atomic rollback,
       deny-by-default unknown inline, generic-400 (no str(exc)).
- 4.14 panel hook (#111) — declared-panels-only resolution.
- 4.15 schema (#108) — staff-gated static envelope schema.
- 4.16 PWA manifest+SW (#86/#200/#208) — no per-user manifest data,
       scope ≤ mount, no-store honored, cache-on-logout, origin check.

Keeps the security doc current with the shipped wire surface — an
audit-readiness must for a public security-sensitive package. Docs-only
(Tier 1).

Co-authored-by: Martin Castro Laminrs <mcastro@laminr.ai>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants