11 KiB
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 |
|
true | off |
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_tokenw tabeliusers - Backend: generowanie/walidacja tokena, cookie, auto-login z middleware
- Frontend: checkbox w formularzu + usunięcie zaślepki błędu
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 me2. 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
<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>