feat(tradein/ui): login-форма + session-aware RouteGuard (#2555) #2562

Merged
lekss361 merged 2 commits from feat/tradein-login-ui into main 2026-07-30 18:10:47 +00:00
Owner

Closes #2555 (эпик #2549, шаг 5/8).

  • app/login/page.tsx — форма username+password, русские ошибки (401 / 429 / сеть), sanitizeNext() против open-redirect (только одиночный /, отбрасывает //host и /\host)
  • components/auth/RouteGuard.tsx — 401 → /login?next=<путь>; ранний bypass для /login (как у /ui-preview) против редирект-петли; ветка expired (trial-экран) и dev-режим не тронуты
  • lib/useMe.tsRole расширена до admin|manager|employee|pilot|analyst|expired
  • lib/useLogout.ts (новый) + TopNav v2 переключён на session-logout
  • basePath: next= строится из пути БЕЗ /trade-in (Next префиксит сам) — иначе задвоение префикса
  • Cookie: все запросы same-origin, дефолтный same-origin credentials уже шлёт tradein_session — правок в api.ts не потребовалось

tsc --noEmit + lint + npm run build чисто (/login — static route, 4.57 kB). Legacy UserMenu на остальных страницах намеренно оставлен на basic_auth-логауте (вне scope). Визуал — проверка на проде после мержа (RouteGuard без бекенда рендерит null).

Closes #2555 (эпик #2549, шаг 5/8). - `app/login/page.tsx` — форма username+password, русские ошибки (401 / 429 / сеть), `sanitizeNext()` против open-redirect (только одиночный `/`, отбрасывает `//host` и `/\host`) - `components/auth/RouteGuard.tsx` — 401 → `/login?next=<путь>`; ранний bypass для `/login` (как у `/ui-preview`) против редирект-петли; ветка `expired` (trial-экран) и dev-режим не тронуты - `lib/useMe.ts` — `Role` расширена до `admin|manager|employee|pilot|analyst|expired` - `lib/useLogout.ts` (новый) + TopNav v2 переключён на session-logout - basePath: `next=` строится из пути БЕЗ `/trade-in` (Next префиксит сам) — иначе задвоение префикса - Cookie: все запросы same-origin, дефолтный `same-origin` credentials уже шлёт `tradein_session` — правок в `api.ts` не потребовалось `tsc --noEmit` + lint + `npm run build` чисто (`/login` — static route, 4.57 kB). Legacy UserMenu на остальных страницах намеренно оставлен на basic_auth-логауте (вне scope). Визуал — проверка на проде после мержа (RouteGuard без бекенда рендерит null).
lekss361 added 1 commit 2026-07-30 17:47:41 +00:00
feat(tradein/ui): login-форма + session-aware RouteGuard (#2555)
All checks were successful
CI / changes (pull_request) Successful in 10s
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m4s
15d506b7dd
POST /api/v1/auth/login/logout уже в main (DB-backed session, httponly
cookie tradein_session). Фронт: /login-форма (username+password, ошибки
401/429 по-русски, next= redirect с open-redirect guard), RouteGuard
редиректит на /login при 401 вместо NoAccessScreen variant=session
(prod-only, dev-режим без Caddy не трогаем), useLogout хук чистит
/me-кэш и уходит на /login. Role расширена admin|manager|employee (новые)
+ pilot|analyst|expired (legacy dual-mode resolver на бэке).
bot-backend added 1 commit 2026-07-30 17:58:24 +00:00
fix(tradein/ui): харденинг sanitizeNext + сохранение query в next (#2555)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m1s
e6d68c349e
PR #2562 review, 3 однострочника:
1. sanitizeNext обходился: WHATWG URL-парсер (router.push) вырезает ASCII
   tab/CR/LF из ВСЕЙ строки перед парсингом, так что "/\t//evil" проходил
   regex (позиция 1 — таб, не "/"/"\\"), а после навигации резолвился в
   protocol-relative "//evil" → чужой origin. Теперь сначала strip
   [\t\r\n], потом валидация — regex видит ту же строку, что увидит парсер.
2. next=/login (или /login?...) кидал юзера обратно на форму входа
   (RouteGuard не гейтит /login) — dead-end. Фолбэк на "/".
3. RouteGuard брал next= только из usePathname(), без query — сессия,
   истёкшая на deep-link (/v2?id=<uuid>), теряла отчёт после релогина.
   Добавлен window.location.search в next (effect всегда client-side).
lekss361 merged commit acba900f21 into main 2026-07-30 18:10:47 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2562
No description provided.