Files
2026-06-01 15:17:29 +02:00

109 lines
8.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 02
slug: users-authentication
status: validated
nyquist_compliant: true
wave_0_complete: true
created: 2026-05-31
---
# Phase 02 — Validation Strategy
> Nyquist validation audit — reconstructed from PLAN/SUMMARY artifacts (State B).
---
## Test Infrastructure
| Property | Value |
|----------|-------|
| **Backend framework** | pytest (pytest-asyncio, httpx.AsyncClient) |
| **Backend config** | `backend/pytest.ini` |
| **Backend quick run** | `cd backend && python -m pytest tests/test_auth_api.py tests/test_auth_totp.py tests/test_admin_api.py -v` |
| **Backend full suite** | `cd backend && python -m pytest -v` |
| **Frontend framework** | Vitest |
| **Frontend config** | `frontend/vitest.config.js` |
| **Frontend run** | `cd frontend && npx vitest run` |
---
## Per-Task Verification Map
| Task ID | Plan | Wave | Requirement | Secure Behavior | Test Type | Automated Command | File Exists | Status |
|---------|------|------|-------------|-----------------|-----------|-------------------|-------------|--------|
| 02-01-T1 | 01 | 1 | AUTH-01, AUTH-02 | Argon2 hash, JWT lifecycle, BackupCode model | Unit | `pytest tests/test_task1_models_config.py tests/test_task2_auth_service.py -v` | ✅ | ✅ green |
| 02-01-T2 | 01 | 1 | AUTH-07, SEC-06 | Refresh family revocation, constant-time backup code verify | Integration | `pytest tests/test_task2_auth_service.py -v` | ✅ | ✅ green |
| 02-01-T3 | 01 | 1 | AUTH-01, AUTH-02 | get_current_user raises 401 on bad token; get_current_admin raises 403 | Integration | `pytest tests/test_auth_deps.py -v` | ✅ | ✅ green |
| 02-02-T1 | 02 | 2 | AUTH-01, AUTH-02, AUTH-04 | Register/login/refresh/logout/me/change-password endpoints | Integration | `pytest tests/test_auth_api.py -v` | ✅ | ✅ green |
| 02-02-T1 | 02 | 2 | SEC-01 | Origin validation middleware rejects cross-origin POST with 403 | Integration | `pytest tests/test_auth_api.py::test_origin_rejected -v` | ✅ | ✅ green |
| 02-02-T1 | 02 | 2 | SEC-02 | Per-account rate limit: 11th login attempt returns 429 | Integration | `pytest tests/test_auth_api.py::test_per_account_rate_limit -v` | ✅ | ✅ green |
| 02-02-T1 | 02 | 2 | **SEC-05** | CSP + X-Frame-Options + X-Content-Type-Options on all responses | Integration | `pytest tests/test_security_headers.py -v` | ✅ | ✅ green |
| 02-02-T2 | 02 | 2 | AUTH-01, AUTH-04 | useAuthStore never writes to localStorage; login() passes backup_code | Unit (Vitest) | `cd frontend && npx vitest run src/stores/__tests__/auth.test.js` | ✅ | ✅ green |
| 02-03-T1 | 03 | 3 | AUTH-03 | TOTP setup returns provisioning_uri; enable rate-limited 10/min | Integration | `pytest tests/test_auth_totp.py -v` | ✅ | ✅ green |
| 02-03-T1 | 03 | 3 | AUTH-05 | Password reset confirm returns 200 with no access_token (no auto-login) | Integration | `pytest tests/test_auth_totp.py::test_password_reset_confirm_valid_no_autologin -v` | ✅ | ✅ green |
| 02-03-T1 | 03 | 3 | AUTH-06 | logout-all revokes all refresh tokens | Integration | `pytest tests/test_auth_totp.py::test_logout_all_revokes_tokens -v` | ✅ | ✅ green |
| 02-03-T1 | 03 | 3 | **AUTH-08** | TOTP replay: same code rejected within 90-second window | Integration | `pytest tests/test_totp_replay.py -v` | ✅ | ✅ green |
| 02-03-T1 | 03 | 3 | **SEC-03** | Constant-time comparison: hmac.compare_digest used; verify_password/backup_code reject wrong inputs | Unit | `pytest tests/test_constant_time_auth.py -v` | ✅ | ✅ green |
| 02-03-T2 | 03 | 3 | AUTH-01 | PasswordStrengthBar: correct 0-4 score (length + char types) | Unit (Vitest) | `cd frontend && npx vitest run src/components/auth/__tests__/PasswordStrengthBar.test.js` | ✅ | ✅ green |
| 02-04-T1 | 04 | 4 | ADMIN-01 | Admin-created users have password_must_change=True | Integration | `pytest tests/test_admin_api.py::test_create_user_sets_password_must_change -v` | ✅ | ✅ green |
| 02-04-T1 | 04 | 4 | ADMIN-02 | Deactivate/reactivate user + sole-admin guard | Integration | `pytest tests/test_admin_api.py::test_deactivate_user tests/test_admin_api.py::test_cannot_deactivate_only_admin -v` | ✅ | ✅ green |
| 02-04-T1 | 04 | 4 | ADMIN-03 | Admin password reset sends email via Celery; no impersonation | Integration | `pytest tests/test_admin_api.py::test_password_reset_initiates_email -v` | ✅ | ✅ green |
| 02-04-T1 | 04 | 4 | ADMIN-04 | Quota update with warning when limit < used_bytes | Integration | `pytest tests/test_admin_api.py::test_quota_below_usage_warning -v` | ✅ | ✅ green |
| 02-04-T1 | 04 | 4 | ADMIN-05 | AI provider/model assignment per user | Integration | `pytest tests/test_admin_api.py::test_update_ai_config -v` | ✅ | ✅ green |
| 02-04-T1 | 04 | 4 | ADMIN-07 | No impersonation endpoint exists (404/422) | Integration | `pytest tests/test_admin_api.py::test_admin_impersonation_not_found -v` | ✅ | ✅ green |
| 02-04-T1 | 04 | 4 | SEC-07 | get_current_admin enforced: non-admin gets 403 | Integration | `pytest tests/test_admin_api.py::test_list_users_requires_admin -v` | ✅ | ✅ green |
| 02-04-T2 | 04 | 4 | SEC-07 | Admin responses never include password_hash | Integration | `pytest tests/test_admin_api.py::test_admin_response_no_password_hash -v` | ✅ | ✅ green |
| 02-05-T2 | 05 | 5 | ADMIN-01..03 | AdminUsersTab: onMount fetch, deactivate call, empty state | Unit (Vitest) | `cd frontend && npx vitest run src/components/admin/__tests__/AdminUsersTab.test.js` | ✅ | ✅ green |
| 02-05-T2 | 05 | 5 | ADMIN-04 | AdminQuotasTab: save call, below-usage warning displayed | Unit (Vitest) | `cd frontend && npx vitest run src/components/admin/__tests__/AdminQuotasTab.test.js` | ✅ | ✅ green |
| 02-05-T2 | 05 | 5 | ADMIN-05 | AdminAiConfigTab: save call, 1.5s Saved confirmation | Unit (Vitest) | `cd frontend && npx vitest run src/components/admin/__tests__/AdminAiConfigTab.test.js` | ✅ | ✅ green |
| 02-06-T2 | 06 | 1 | SEC-07, AUTH-01 | `requiresAdmin` guard redirects non-admin to `/`; all 4 auth routes carry `meta.layout: 'auth'` | Unit (Vitest) | `cd frontend && npx vitest run src/router/__tests__/router.guard.test.js` | ✅ | ✅ green |
| 02-06-T3a | 06 | 1 | AUTH-03 | TotpEnrollment renders `<img src="data:image/...">` QR code in verify step; no `otpauth://` link | Unit (Vitest) | `cd frontend && npx vitest run src/components/auth/__tests__/TotpEnrollment.test.js` | ✅ | ✅ green |
| 02-06-T3b | 06 | 1 | AUTH-03, AUTH-04 | SettingsAccountTab mounts all 4 sections; totp_enabled toggle shows correct 2FA state | Unit (Vitest) | `cd frontend && npx vitest run src/components/settings/__tests__/SettingsAccountTab.test.js` | ✅ | ✅ green |
*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*
---
## Manual-Only Verifications
| Behavior | Requirement | Why Manual | Test Instructions |
|----------|-------------|------------|-------------------|
| TOTP QR code renders and scans correctly | AUTH-03 | Requires a physical authenticator app (Google Auth / Authy) | 1. Register + login; 2. GET /api/auth/totp/setup; 3. Open provisioning_uri in authenticator app; 4. Verify 6-digit code is accepted by POST /api/auth/totp/enable |
| Email delivery (SMTP) | AUTH-05, ADMIN-03 | Requires live SMTP server | Configure SMTP_* env vars; trigger password reset; verify email arrives with correct reset link |
| Admin panel browser rendering | ADMIN-01..05 | Vue component visual contract | Start dev server; log in as admin; verify all three tabs render correctly per UI-SPEC |
| httpOnly cookie SameSite=Strict | SEC-01 | Browser DevTools required | Log in via browser; open DevTools → Application → Cookies; verify refresh_token is HttpOnly + SameSite=Strict |
---
## Validation Audit 2026-05-31
| Metric | Count |
|--------|-------|
| Gaps found | 6 (1 MISSING backend, 2 PARTIAL backend, 3 MISSING frontend) |
| Resolved | 6 |
| Escalated | 0 |
| New test files | 8 |
| Total tests added | 60 (14 backend + 46 frontend) |
## Validation Audit 2026-06-01
| Metric | Count |
|--------|-------|
| Gaps found | 3 (3 MISSING frontend — plan 02-06 not covered in prior audit) |
| Resolved | 3 |
| Escalated | 0 |
| New test files | 3 |
| Total tests added | 16 (router guard × 10, TotpEnrollment × 2, SettingsAccountTab × 4) |
---
## Validation Sign-Off
- [x] All tasks have automated verify or Manual-Only justification
- [x] Sampling continuity: no 3 consecutive tasks without automated verify
- [x] Wave 0 covers all MISSING references (gaps filled by audit)
- [x] No watch-mode flags
- [x] `nyquist_compliant: true` set in frontmatter
**Approval:** 2026-05-31