Skip to content

security: harden JWT validation and auth startup checks#27

Merged
marinone94 merged 1 commit into
mainfrom
fix/auth-hardening
Mar 30, 2026
Merged

security: harden JWT validation and auth startup checks#27
marinone94 merged 1 commit into
mainfrom
fix/auth-hardening

Conversation

@marinone94

Copy link
Copy Markdown
Owner

Summary

  • SEC-C5: Crash startup when AUTH_ENABLED=True but SUPABASE_JWT_SECRET is empty (was silently disabling auth)
  • SEC-C4: Add JWT issuer (iss) validation against Supabase project URL
  • SEC-C3: Add UNIQUE constraint on polar_customer_id + alembic migration
  • SEC-H3: Scope /analytics/stats to authenticated user via get_optional_user

Files changed

  • cherry_evals/config.py — ValueError instead of silent fallback
  • api/deps.py — issuer param in pyjwt.decode()
  • db/postgres/models.pyunique=True on polar_customer_id
  • api/routes/analytics.py — auth-aware stats endpoint
  • alembic/versions/007_unique_polar_customer_id.py — new migration
  • tests/conftest.pyAUTH_ENABLED=False env default before imports
  • 3 new test files (13 tests)

Test plan

  • uv run pytest passes (590 passed, 1 skipped)
  • Settings raises ValueError when jwt_secret empty + auth_enabled=True
  • JWT decode rejects tokens with wrong issuer
  • Analytics stats scoped to authenticated user

🤖 Generated with Claude Code

- SEC-C5: Raise ValueError on startup when AUTH_ENABLED=True but
  SUPABASE_JWT_SECRET is empty. Removes silent fallback to open access
  which would allow unauthenticated requests in a misconfigured prod env.
  Tests set AUTH_ENABLED=False via os.environ.setdefault in conftest.py.

- SEC-C4: Add issuer validation to _decode_supabase_jwt(). When
  supabase_url is configured, pyjwt now validates iss against
  <supabase_url>/auth/v1, blocking tokens from other Supabase projects.

- SEC-C3: Add unique=True to User.polar_customer_id, preventing duplicate
  billing lookups. Alembic migration 007_unique_polar_customer_id.py adds
  the corresponding DB constraint.

- SEC-H3: /analytics/stats now accepts get_optional_user dependency and
  scopes results to the authenticated user's user_id when present.
  Unauthenticated requests get aggregate counts without query history.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Mar 30, 2026

Copy link
Copy Markdown

Deploying cherry-evals with  Cloudflare Pages  Cloudflare Pages

Latest commit: db43c9b
Status: ✅  Deploy successful!
Preview URL: https://29fe8f2e.cherry-evals.pages.dev
Branch Preview URL: https://fix-auth-hardening.cherry-evals.pages.dev

View logs

@marinone94
marinone94 merged commit 18e8930 into main Mar 30, 2026
4 checks passed
marinone94 added a commit that referenced this pull request Mar 30, 2026
30/46 findings now fixed. All Critical and High security vulnerabilities resolved.
Remaining items: GDPR endpoints, DPAs, rate limiter Redis, Langfuse instrumentation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
marinone94 pushed a commit that referenced this pull request Mar 30, 2026
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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