Skip to content

feat: proxy the Google OAuth handshake and close the shared-domain cookie leak - #13

Merged
ksprptr merged 26 commits into
prodfrom
dev
Sep 1, 2026
Merged

ksprptr merged 26 commits into
prodfrom
dev

Conversation

@ksprptr

@ksprptr ksprptr commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Before deploying

  • Register https://<web origin>/api/oauth/google/callback on the Google OAuth client — the
    API-hosted callback is gone. Existing connections keep working on their stored refresh token.
  • GOOGLE_REDIRECT_URI → the URI above, COOKIE_DOMAIN → unset, NEXT_PUBLIC_API_URL →
    removed from the web build args, API_URL → http://dropto-api:4000/api/v1.
  • Node >=24, pnpm pinned to 11.5.1. A migration drops the write-only upload_logs table.

What's in it

  • OAuth through the web app — both legs are proxied, so the browser never touches the API and
    it needs no public route.
  • Host-only auth cookies — the shared parent domain was sending the operator's session to every
    sibling subdomain. The web app now forwards the API's cookie attributes instead of re-deriving them.
  • /storage/:backend/names skipped the ancestor walk — it returned the name and Drive link of
    any file id. Now authorized like every other id, and capped at 50.
  • Rate-limit key was client-controlled — a vendor header nothing here sets could be used to walk
    past the login limiter.
  • Exception filter logged full URLs — live OAuth codes and owner tokens ended up in the logs.
  • Three security overrides pinned versions below their advisories' patched releases.
  • /health is rate limited (120/60s) instead of exempt — it reaches Postgres.
  • 404s — a URL naming no backend returns a real 404, not 404 content as 200.
  • Closed split pane left nine controls in the tab order — inert now, transitions intact.
  • Modals got dialog semantics; name inputs cap at 255.
  • Drive status caches the quota for 30s (850ms → 300ms); root liveness stays uncached.
  • Tests: 157 → 234 unit, 46 → 96 e2e; unstubbed outbound fetch throws.
  • CI in .github/workflows/ci.yml, Postman regression collection in postman/.
  • WorkspaceClient 1580 → ~600 lines, state moved into hooks.

Verification

  • pnpm build, pnpm lint, 234 unit and 96 e2e pass.
  • docker compose up -d --build comes up healthy; migrations apply.
  • Postman collection: 37 requests, 109 assertions, zero failures.
  • OAuth proxy checked leg by leg; real consent round-trip confirmed against a live Google account.
  • CI needs no service containers — e2e passes with the database and Redis unreachable.

ksprptr added 26 commits August 29, 2026 13:10
Dependencies
- NestJS 11 -> 12 (+ @nestjs/config 4 -> 12). The packages now ship as ESM, so
  Jest runs with --experimental-vm-modules and no longer transforms node_modules;
  that also drops the hand-maintained ESM allowlist from transformIgnorePatterns.
  Requires Node >= 22.12 to run, >= 24.9 to run the tests.
- ioredis 5 -> 6 (RESP3 by default), verified against a live Redis: the rate
  limiter's consume/reject semantics are unchanged, no protocol: 2 fallback needed.
- framer-motion 12 -> motion 13 (package renamed; imports moved to 'motion/react').
- googleapis 173 -> 176, next 16.3.3, lucide-react 1.38, plus in-range patch bumps.
- Dropped eslint-plugin-import: it declares no ESLint 10 support and backed a
  single low-value warning.

Shared contract
- The API now depends on @dropto/types and its Swagger entities `implements` the
  shared interfaces, so the two descriptions of the wire format cannot drift.
  This surfaced three places that typed createdAt as Date while the wire carries
  an ISO string, and a StorageBackend that was declared twice.
- UploadStatus -> ResumableUploadStatus, freeing the name from Prisma's enum.

Performance
- The Drive ancestor walk memoizes the parents of intermediate folders (60s,
  in-process). The target item is always re-read, so a deleted item still 404s.
- The file list filter is debounced, so a keystroke no longer re-filters,
  re-sorts and re-renders the whole list in both panes.

Cleanup
- Removed docker-compose.infra.yml: it duplicated the pg/redis services verbatim;
  `docker compose up -d dropto-pg dropto-redis` builds no app image either way.
- Removed the no-op Prisma seed script and unexported an internal JWT helper.
- Added apps/api/.env.test.example, without which the e2e suite could not run
  from a fresh clone.
- tsBuildInfoFile now lives inside dist, so wiping dist actually forces a rebuild
  instead of silently emitting nothing.
- Typed the Google Picker globals, removing the last `any` in the repo.
@ksprptr
ksprptr merged commit df10597 into prod Sep 1, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant