# Server-Side Password Authentication Flow Assessment (Batch 3) **File inspected:** `crates/cgcx-server/src/main.rs` ## Flow Verification ### 1. `get_metadata` handler (`/api/content/:cxid`) - **Lines ~496–568** - Sequence: 1. Content status check (Deleted/Blacklisted) → **404 NotFound** 2. Max-views check (`content.view_count >= max`) → **410 Gone** 3. Password validation via `password_from_request(&headers, query.sc.as_deref(), ...)` → **401 Unauthorized** if invalid 4. Returns metadata JSON if all checks pass - **View increment:** None. Metadata requests do not consume auto-destroy views. - **Conclusion:** Password check is enforced and returns 401 on failure. ✅ ### 2. `serve_file` handler (`/api/content/:cxid/file/:file_idx`) - **Lines ~700–890** - Sequence: 1. Content status check → **404** 2. Max-views check → **410** 3. Password validation via `password_from_request` → **401** if invalid 4. Download permission check → **403 Forbidden** if `?download=true` but not allowed 5. File lookup, path-traversal validation, ETag conditional check (304) 6. Range header parsing 7. **View increment at line ~825:** `repo.increment_views(&content_id).await?` 8. If `new_views >= max`, spawns background auto-delete task after 30s 9. Streams decrypted file body - **Conclusion:** View increment happens **after** password validation and all other guards. Unauthorized requests cannot consume views. ✅ ### 3. `password_from_request` helper - **Lines ~436–475** - Validates `sc` query parameter using Argon2 (`PasswordHash::new` + `Argon2::default().verify_password`). - Falls back to `cgcx_pw` cookie verified with HMAC-SHA256 and `subtle::ConstantTimeEq`. - Returns `None` on any mismatch, causing the caller to return 401. - **Conclusion:** Argon2 verification is present and constant-time. ✅ ### 4. `serve_raw_file` handler (`/api/content/:cxid/file/:file_idx/raw`) - **Lines ~920–1020** - Sequence mirrors `serve_file` for status, max-views, and password checks. - **View increment:** None. This endpoint never increments `view_count`. --- ## Edge Cases & Concerns | # | Edge Case | Impact | Risk | |---|-----------|--------|------| | 1 | **`serve_raw_file` skips view increment** | Requests to the `/raw` endpoint do not consume auto-destroy views. A user could repeatedly preview raw text without triggering deletion. | Medium — bypasses view-count enforcement for text content previews. | | 2 | **Zero-size files in `serve_file` return early** | The zero-size branch (line ~751) returns a response **before** the increment block, so zero-byte files never consume a view. | Low — niche, but technically bypasses max-views for empty files. | | 3 | **Range/conditional/HEAD requests skip increment** | `serve_file` only increments when `!is_range && !is_conditional && !is_head`. Video/audio seeking (range requests), cache revalidation (If-None-Match), and HEAD probes do not count as views. | Low — intentional for UX, but means views are under-counted. | | 4 | **Max-views checked before password validation** | In all three handlers, if `view_count >= max`, a request with a wrong password receives **410 Gone** instead of **401 Unauthorized**. This leaks that the content existed and exhausted its views without requiring the password. | Low — information disclosure about content lifecycle. | | 5 | **TOCTOU race on view increment** | `content.view_count` is read in `repo.get()` and incremented later in a separate statement. Under concurrent requests, two clients could both pass the initial `view_count < max` check and both increment, causing `view_count` to exceed `max`. Both requests are still served. | Low — SQLite serializes writes via `conn.lock()`, but the read-write gap still allows one extra view. | --- ## Conclusion - **Password authentication flow is correct.** Wrong passwords receive 401 and do **not** increment view counts. - **No server changes are required for Batch 3.** The frontend password fix (if any) is independent of server behavior. - The identified edge cases are pre-existing behaviors. None are blockers for Batch 3, but items 1 (`serve_raw_file` bypass) and 5 (TOCTOU race) may be worth addressing in a future hardening pass if auto-destroy accuracy is critical.