Files
orderPRO/.paul/phases/93-remember-me-login/93-01-PLAN.md
2026-04-12 01:35:19 +02:00

11 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, delegation
phase plan type wave depends_on files_modified autonomous delegation
93-remember-me-login 01 execute 1
database/migrations/20260410_000081_add_remember_token_to_users.sql
src/Modules/Auth/AuthService.php
src/Modules/Auth/AuthController.php
src/Modules/Auth/AuthMiddleware.php
src/Modules/Users/UserRepository.php
resources/views/auth/login.php
resources/scss/login.scss
resources/lang/pl.php
true off
## Goal Dodanie checkboxa "Zapamiętaj mnie" na stronie logowania z persistent cookie (30 dni) oraz uruchomienie działającego komunikatu błędu logowania (zamiast zaślepki placeholder).

Purpose

Użytkownicy muszą logować się przy każdej sesji przeglądarki. "Zapamiętaj mnie" pozwala na trwałe logowanie na danym urządzeniu przez 30 dni. Jednocześnie placeholder błędu logowania staje się funkcjonalny — wyświetla rzeczywiste komunikaty.

Output

  • Migracja: kolumna remember_token w tabeli users
  • Backend: generowanie/walidacja tokena, cookie, auto-login z middleware
  • Frontend: checkbox w formularzu + usunięcie zaślepki błędu
## Project Context @.paul/PROJECT.md @.paul/ROADMAP.md @.paul/STATE.md

Source Files

@src/Modules/Auth/AuthService.php @src/Modules/Auth/AuthController.php @src/Modules/Auth/AuthMiddleware.php @src/Modules/Users/UserRepository.php @resources/views/auth/login.php @resources/scss/login.scss @resources/lang/pl.php @database/migrations/20260221_000001_create_users_table.sql

<acceptance_criteria>

AC-1: Checkbox "Zapamiętaj mnie" widoczny na formularzu logowania

Given strona logowania /login jest wyświetlona
When użytkownik widzi formularz logowania
Then pomiędzy polem hasła a przyciskiem "Zaloguj" widoczny jest checkbox "Zapamiętaj mnie"

AC-2: Persistent login przez 30 dni po zaznaczeniu checkboxa

Given użytkownik zaznaczył checkbox "Zapamiętaj mnie"
When loguje się poprawnymi danymi
Then przeglądarka otrzymuje cookie `remember_token` z max-age 30 dni (httponly, secure, samesite=lax)
And token jest zapisany w bazie danych (users.remember_token jako hash)
And po zamknięciu i otwarciu przeglądarki użytkownik jest nadal zalogowany

AC-3: Brak persistent login bez zaznaczenia checkboxa

Given użytkownik NIE zaznaczył checkboxa "Zapamiętaj mnie"
When loguje się poprawnymi danymi
Then cookie `remember_token` NIE jest ustawiane
And sesja wygasa po zamknięciu przeglądarki (standardowe zachowanie)

AC-4: Komunikat błędu logowania działa prawidłowo

Given strona logowania /login jest wyświetlona
When nie ma błędu logowania
Then placeholder błędu jest ukryty (display:none, nie opacity)
When użytkownik podaje złe dane i submittuje formularz
Then wyświetla się rzeczywisty komunikat błędu (np. "Nieprawidłowy email lub hasło")
And placeholder nie jest widoczny

AC-5: Wylogowanie czyści remember token

Given użytkownik jest zalogowany z "Zapamiętaj mnie"
When klika "Wyloguj"
Then cookie `remember_token` jest usuwane
And token w bazie danych jest kasowany (NULL)
And użytkownik musi zalogować się ponownie

AC-6: Wielourządzeniowe logowanie działa niezależnie

Given użytkownik zalogował się z "Zapamiętaj mnie" na urządzeniu A
When loguje się z "Zapamiętaj mnie" na urządzeniu B
Then oba urządzenia mają niezależne tokeny
And wylogowanie na urządzeniu A nie wylogowuje z urządzenia B

</acceptance_criteria>

Task 1: Migracja DB + UserRepository + remember token backend database/migrations/20260410_000081_add_remember_token_to_users.sql, src/Modules/Users/UserRepository.php 1. Utworzyć migrację dodającą kolumnę `remember_token VARCHAR(255) NULL` do tabeli `users`. - Kolumna przechowuje HASH tokena (nie plaintext) — `hash('sha256', $token)` - NULL = brak aktywnego remember me
2. W `UserRepository` dodać metody:
   - `updateRememberToken(int $userId, ?string $tokenHash): void` — UPDATE users SET remember_token = :token WHERE id = :id
   - `findByRememberToken(string $tokenHash): ?array` — SELECT id, name, email FROM users WHERE remember_token = :token LIMIT 1

Avoid: NIE przechowywać plaintext tokena w DB — zawsze hash('sha256', $token)
Migracja wykonuje się bez błędów; metody repozytorium istnieją i mają prepared statements AC-2 (baza), AC-6 (wielotoken) — infrastruktura DB gotowa Task 2: AuthService + AuthController + AuthMiddleware — logika remember me + error fix src/Modules/Auth/AuthService.php, src/Modules/Auth/AuthController.php, src/Modules/Auth/AuthMiddleware.php **AuthService:** 1. Dodać stałą `REMEMBER_COOKIE = 'remember_token'` i `REMEMBER_DAYS = 30` 2. Metoda `createRememberToken(int $userId): string`: - Generuje losowy token: `bin2hex(random_bytes(32))` - Zapisuje hash w DB: `$this->users->updateRememberToken($userId, hash('sha256', $token))` - Ustawia cookie: `setcookie('remember_token', $token, [opcje 30 dni, httponly, secure, samesite=lax, path=/])` - Zwraca token (do ewentualnego użycia) 3. Metoda `loginFromRememberToken(): bool`: - Odczytuje `$_COOKIE['remember_token']` - Jeśli brak — return false - Hashuje: `hash('sha256', $cookieToken)` - Szuka usera: `$this->users->findByRememberToken($hash)` - Jeśli znaleziony — regeneruje sesję, ustawia $_SESSION['auth_user'], return true - Jeśli nie — usuwa cookie, return false 4. Metoda `clearRememberToken(int $userId): void`: - `$this->users->updateRememberToken($userId, null)` - Usuwa cookie (setcookie z max-age 0) 5. W istniejącej `logout()`: wywołać `clearRememberToken` dla aktualnego usera przed unset sesji
**AuthController:**
1. W `login()`: po udanym `$this->auth->attempt()`:
   - Sprawdzić `$request->input('remember')` 
   - Jeśli truthy → `$this->auth->createRememberToken($userId)`
   - Pobrać userId z `$this->auth->user()['id']` po attempt
2. Przekazywać `remember` checkbox state z powrotem do formularza w razie błędu (Flash::set('old_remember'))
3. W `showLogin()`: przekazać `oldRemember` z Flash do widoku

**AuthMiddleware:**
1. W `__invoke()`: jeśli `$this->auth->check()` zwraca false:
   - Przed redirect na /login, spróbować `$this->auth->loginFromRememberToken()`
   - Jeśli sukces → kontynuować normalnie ($next)
   - Jeśli porażka → redirect /login jak dotychczas

Avoid:
- NIE przechowywać plaintext tokena w DB
- NIE ustawiać cookie bez httponly i samesite
- W logout() NAJPIERW pobrać user ID, POTEM czyścić sesję
1. Login z remember=on → cookie `remember_token` w przeglądarce (30 dni) 2. Login bez remember → brak cookie 3. Po zamknięciu przeglądarki i otwarciu → auto-login z cookie 4. Logout → cookie usunięte + token NULL w DB 5. Błąd logowania → wyświetla komunikat (nie placeholder) AC-2, AC-3, AC-5, AC-6 satisfied Task 3: Frontend — checkbox, error placeholder fix, style, tłumaczenia resources/views/auth/login.php, resources/scss/login.scss, resources/lang/pl.php **login.php:** 1. Usunąć blok placeholder błędu (linie 13-16: `<?php else: ?>...<?php endif; ?>`). Zamienić na: jeśli `$errorMessage` nie pusty → wyświetl alert; w przeciwnym razie — NIC (brak zaślepki). 2. Dodać checkbox "Zapamiętaj mnie" między polem hasła a przyciskiem submit: ```php > ```
**login.scss:**
1. Usunąć regułę `.login-alert-placeholder` (opacity: 0.56 — już niepotrzebna)
2. Dodać style dla `.remember-field`:
   ```scss
   .remember-field {
     display: flex;
     align-items: center;
     gap: 8px;
     
     input[type="checkbox"] {
       width: 16px;
       height: 16px;
       accent-color: var(--c-primary, #4f6ef7);
       cursor: pointer;
     }
     
     .field-label {
       font-weight: 400;
       cursor: pointer;
       user-select: none;
     }
   }
   ```

**pl.php:**
1. Dodać klucz `'remember_me' => 'Zapamiętaj mnie'` w sekcji `auth.login`
2. Usunąć lub zostawić klucz `error_placeholder` (nie jest już używany w widoku)

Po zmianach SCSS: zbudować CSS komendą projektu (jeśli build pipeline istnieje) lub skopiować do public/assets/css/

Avoid: NIE dodawać nowych natywnych alert()/confirm() — formularz działa przez POST redirect
1. Strona /login wyświetla checkbox "Zapamiętaj mnie" 2. Bez błędu — brak żadnego komunikatu (nie ma zaślepki) 3. Po błędnym logowaniu — wyświetla się czerwony alert z treścią błędu 4. Checkbox zachowuje stan po błędnym logowaniu (old_remember) AC-1, AC-4 satisfied

DO NOT CHANGE

  • src/Core/Support/Session.php (zarządzanie sesją — stabilne)
  • src/Core/Security/Csrf.php (token CSRF — stabilne)
  • resources/views/layouts/auth.php (layout auth — bez zmian)
  • Inne moduły (Orders, Settings, Accounting itp.)

SCOPE LIMITS

  • Nie implementujemy resetowania hasła
  • Nie implementujemy "wyloguj ze wszystkich urządzeń" (poza scope)
  • Nie zmieniamy struktury sesji (SESSION_USER_KEY format)
  • Nie dodajemy nowych zależności npm/composer
Before declaring plan complete: - [ ] Migracja dodaje kolumnę `remember_token` do `users` - [ ] Login z checkbox → cookie 30-dniowe + hash w DB - [ ] Login bez checkbox → brak cookie - [ ] Zamknięcie/otwarcie przeglądarki → auto-login działa - [ ] Logout → cookie usunięte + DB token NULL - [ ] Błąd logowania → widoczny komunikat - [ ] Brak błędu → brak zaślepki/placeholdera - [ ] SCSS zbudowane do CSS - [ ] Tłumaczenie pl.php zaktualizowane - [ ] All acceptance criteria met

<success_criteria>

  • All tasks completed
  • All verification checks pass
  • No errors or warnings introduced
  • Użytkownik może logować się z persistent cookie na wielu urządzeniach niezależnie
  • Komunikaty błędów logowania działają poprawnie </success_criteria>
After completion, create `.paul/phases/93-remember-me-login/93-01-SUMMARY.md`