docs: design for multi-device remember-me tokens
This commit is contained in:
57
docs/plans/2026-02-28-remember-me-multi-device-design.md
Normal file
57
docs/plans/2026-02-28-remember-me-multi-device-design.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Remember-Me Multi-Device Tokens Design (2026-02-28)
|
||||
|
||||
## Context
|
||||
After the security changes on 2026-02-26, remember-me uses a single `users.remember_token` value. This breaks multi-browser usage because each login overwrites the previous token. The goal is to allow unlimited concurrent remember-me sessions across devices and browsers, with retention cleanup for tokens older than 6 months.
|
||||
|
||||
## Goals
|
||||
- Allow unlimited concurrent remember-me sessions per user.
|
||||
- Store only hashed tokens in the database.
|
||||
- Keep existing cookie security attributes (`Secure`, `HttpOnly`, `SameSite=Lax`).
|
||||
- Clean up tokens older than 6 months.
|
||||
|
||||
## Non-Goals
|
||||
- Reworking the entire authentication system.
|
||||
- Introducing refresh-token or JWT flows.
|
||||
|
||||
## Data Model
|
||||
Add a new table `users_remember_tokens`:
|
||||
- `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY
|
||||
- `user_id` INT UNSIGNED NOT NULL, indexed
|
||||
- `token_hash` CHAR(64) NOT NULL, unique
|
||||
- `created_at` DATETIME NOT NULL
|
||||
- `last_used_at` DATETIME NULL
|
||||
- `user_agent` VARCHAR(255) NULL
|
||||
- `ip` VARCHAR(45) NULL
|
||||
|
||||
Rationale:
|
||||
- Store only `token_hash` (SHA-256 of raw token) to avoid storing bearer tokens in plain text.
|
||||
- Use `last_used_at` for retention and auditing. If null, fall back to `created_at`.
|
||||
|
||||
## Login Flow
|
||||
1. When user logs in with "Zapamiêtaj mnie":
|
||||
- Generate random token (64 hex).
|
||||
- Store `hash = sha256(token)` in `users_remember_tokens` with metadata and timestamps.
|
||||
- Set cookie to the raw token with expiry +1 year and existing security attributes.
|
||||
2. When user logs in without "Zapamiêtaj mnie":
|
||||
- Do not create a token.
|
||||
- Existing cookie is cleared.
|
||||
|
||||
## Auto-Login Flow
|
||||
1. If no session and cookie exists:
|
||||
- Hash cookie value with SHA-256.
|
||||
- Look up `users_remember_tokens` by `token_hash`.
|
||||
- If found, load user, set session, update `last_used_at`.
|
||||
- If not found, clear cookie.
|
||||
|
||||
## Retention
|
||||
- Delete tokens older than 6 months based on `last_used_at` (or `created_at` if `last_used_at` is null).
|
||||
- Trigger cleanup during login and auto-login paths to avoid extra jobs.
|
||||
|
||||
## Error Handling
|
||||
- Missing or invalid token in DB results in clearing the cookie and continuing to login screen.
|
||||
- No user row for token is treated as invalid and cleaned.
|
||||
|
||||
## Testing
|
||||
- Integration test: login with remember-me sets cookie and inserts token row.
|
||||
- Integration test: auto-login succeeds from cookie after session is cleared.
|
||||
- Retention test: tokens older than 6 months are removed on login.
|
||||
Reference in New Issue
Block a user