siftlode/backend/app/routes
npeter83 e92751dbce fix(auth): security hardening — token encryption, timing, adoption + isolation
From the auth /security-review (contained fixes; architectural SA3 proxy-trust +
SA4 session-revocation deferred to the user):
- SB1: encrypt the OAuth access_token at rest (was plaintext while refresh_token
  was encrypted) — a ~1h Google bearer credential. New security.decrypt_optional()
  falls back to a refresh for legacy plaintext tokens; column is unbounded String.
  E2E-verified: token refreshed → stored as Fernet ciphertext → 319-subscription
  YouTube sync succeeded.
- SA5: password_login is no longer a timing/enumeration oracle — it always runs
  argon2 (against a decoy hash for unknown/passwordless emails), so response time
  can't reveal whether an account exists.
- SB2: Google login only ADOPTS+activates a pre-existing password account when
  Google actually attests email_verified (defense-in-depth against takeover); and
  the email sync won't overwrite with a value another account owns (avoids a 500).
- demo_login clears the wallet first (demo can't switch to / act as a real account
  via the multi-account header); switch_account rejects a suspended target (which
  would otherwise clear the whole session on the next request).

Re-review clean; ruff clean; localdev boots; YouTube auth path E2E-verified.
2026-07-11 21:26:39 +02:00
..
__init__.py feat: M1 foundation — compose stack, FastAPI, Google OAuth, encrypted tokens 2026-06-11 01:01:37 +02:00
admin.py fix(auth): last-admin guard counts only ACTIVE admins (prevents lockout) 2026-07-11 21:26:39 +02:00
channels.py chore(channels): Phase 2 #3 backend cleanup — extract blocked + summary helpers 2026-07-11 18:11:23 +02:00
config.py feat(config): DB-backed system_config infrastructure + SMTP group 2026-06-19 12:22:36 +02:00
downloads.py Merge branch 'dev' into chore/code-hygiene 2026-07-11 16:22:39 +02:00
feed.py chore(feed): Phase 2 #2 backend cleanup — dead return, dup helpers, shared const 2026-07-11 17:37:52 +02:00
health.py feat: M1 foundation — compose stack, FastAPI, Google OAuth, encrypted tokens 2026-06-11 01:01:37 +02:00
me.py fix(auth): security hardening — token encryption, timing, adoption + isolation 2026-07-11 21:26:39 +02:00
messages.py feat(auth): per-tab account — different account per browser tab 2026-07-01 23:43:35 +02:00
notifications.py feat(notifications): durable per-user inbox (P1) + maintenance schema 2026-06-18 03:20:17 +02:00
playlists.py fix(playlists): reorder position collision + dedup remove helper 2026-07-11 18:33:16 +02:00
plex.py fix(plex): genre facet offers only co-occurring genres in AND mode 2026-07-11 03:41:17 +02:00
public.py chore(downloads): C3-C6 — DRY the source-URL, filename, and serialize helpers 2026-07-11 05:47:51 +02:00
quota.py feat(stats): per-user API quota attribution + admin usage page 2026-06-12 02:47:55 +02:00
saved_views.py feat(views): saved-views backend — table, model, CRUD + reorder API 2026-07-01 03:17:36 +02:00
scheduler.py feat(config): move operational params to DB (quota/backfill/shorts/batch) 2026-06-19 13:07:45 +02:00
search.py fix(feed): repair 2 bugs found in the Phase 2 #2 review 2026-07-11 17:43:18 +02:00
setup.py feat(setup): install-wizard step endpoints (epic 6c, API) 2026-06-21 00:40:54 +02:00
sync.py refactor(backend): quota.measured helper, consistent admin gating, messageable predicate 2026-06-26 03:15:53 +02:00
tags.py refactor(auth): centralize token hashing, email validation & admin gating 2026-06-26 03:15:36 +02:00
version.py feat(version): /api/version + build-time version/commit stamping 2026-06-15 00:06:57 +02:00