Skip to content

feat(dev): DEV_AUTH_BYPASS flag to skip guest gates for headless testing - #555

Merged
thewrz merged 4 commits into
mainfrom
feat/dev-auth-bypass
Jun 24, 2026
Merged

feat(dev): DEV_AUTH_BYPASS flag to skip guest gates for headless testing#555
thewrz merged 4 commits into
mainfrom
feat/dev-auth-bypass

Conversation

@thewrz

@thewrz thewrz commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Why

Headless testing of guest flows (collect/join/kiosk submit, vote, profile) is blocked by the human-verification (wrzdj_human cookie) and email-verification gates — the external API suite and Playwright can't easily mint those. Turnstile already self-disables in dev; these two gates had no dev escape hatch. (This is exactly what blocked 16 checks in the ~/wrzdj-testing suite — all 401/403 gate rejections.)

What

Adds DEV_AUTH_BYPASS (default off). When set in a non-production env, the three guest gates (require_verified_human, require_verified_human_soft, require_email_verified) resolve a real guest identity — the request's wrzdj_guest cookie if present, else a stable lazily-created dev guest — and skip the cookie/email checks. So tests need no cookies at all. Admin/DJ auth is unchanged (JWT).

Prod-safety (by construction)

  • auth_bypass_enabled gates on not is_productioninert even if the flag leaks into a prod environment.
  • validate_settings refuses to boot (SystemExit(1)) if DEV_AUTH_BYPASS is set with ENV=production.
  • A loud WARNING is logged whenever it's active in dev.

Testing

  • Property matrix: dev+flag active / prod+flag inert / off-by-default
  • Prod refuses to boot when set; clean prod still boots; dev warns (not raises)
  • Live integration: GET /collect/{code}/profile (hard gate) → 403 without bypass, 200 with it
  • No regression across gate suites (104 passing: config, human-verification, collect, votes, bypass)
  • ruff/format/bandit clean
  • CI green

🤖 Co-authored by Claude Opus 4.8.

Headless API/Playwright testing of guest flows (collect/join/kiosk submit, vote,
profile) is blocked by the human-verification (wrzdj_human cookie) and email-
verification gates, which the external test suites can't easily satisfy. Turnstile
already self-disables in dev; these two gates had no dev escape hatch.

Add DEV_AUTH_BYPASS (default off). When set in a non-production env, the three guest
gates (require_verified_human, require_verified_human_soft, require_email_verified)
resolve a real guest identity (the request's wrzdj_guest cookie if present, else a
stable lazily-created dev guest) and skip the cookie/email checks — so tests need no
cookies at all.

Prod-safe by construction: the auth_bypass_enabled property gates on `not
is_production` (inert if the flag ever leaks into prod), AND validate_settings
refuses to boot (SystemExit) if it is set with ENV=production. A loud warning is
logged when it is active in dev.

Tests: property matrix (dev-active / prod-inert / off-by-default), prod refuses to
boot + clean prod still boots + dev warns, and a live integration check that a hard-
gated endpoint (GET /collect/{code}/profile) is 403 without the bypass and 200 with
it. 8 new tests; no regression across the gate suites (104 passing).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@thewrz, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 29 minutes and 26 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 63b1ef4e-dd4f-41e2-8365-14886dfde890

📥 Commits

Reviewing files that changed from the base of the PR and between 4543b2f and 199ea84.

📒 Files selected for processing (6)
  • .env.example
  • server/app/api/deps.py
  • server/app/api/guest.py
  • server/app/core/config.py
  • server/app/core/rate_limit.py
  • server/tests/test_dev_auth_bypass.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/dev-auth-bypass

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

thewrz and others added 3 commits June 24, 2026 14:05
The first cut only bypassed the gate dependencies, but several guest endpoints
(vote, public.py, guest.py, events.py) resolve identity INLINE via get_guest_id and
raise their own 401 — so they still blocked under the bypass. Move the dev-guest
get-or-create into get_guest_id (the single identity chokepoint the gates AND the
inline routes share) so DEV_AUTH_BYPASS covers every guest page. Same prod-safety:
the new branch is gated on the identical auth_bypass_enabled (inert in production),
and with the flag off behavior is unchanged. Adds a vote regression test for the
inline-resolution path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adversarial review found the bypass FLAG was prod-safe, but the dev guest ROW it
creates was a latent backdoor: a fixed public token (dev-auth-bypass-guest) + a
pre-set verified_email. If that row ever reached a prod DB (staging->prod promotion,
backup restore, dev dump), an attacker could present `Cookie: wrzdj_guest=
dev-auth-bypass-guest` — the NORMAL cookie lookup resolved it before any bypass check
— solve a public Turnstile, and pass require_email_verified (row pre-verified) to
reach email-gated guest actions with no OTP.

Two independent barriers:
1. get_guest_id rejects the reserved dev token outright when the bypass is off, so a
   leaked row is unresolvable in production regardless of the flag.
2. The dev guest is no longer pre-verified (no verified_email) — the email gate is
   opened by the explicit require_email_verified bypass, so the row never needs a
   pre-verified identity that could be abused if it leaked.

Regression tests: leaked dev token rejected with bypass off, resolves with bypass on,
normal tokens unaffected, dev guest not pre-verified. Codex separately confirmed no
other prod bypass path (flag inert in prod + boot-abort, no runtime toggle, no effect
on JWT/rate-limit/IDOR/kiosk/owner checks).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Re-review found the reserved-token defense was incomplete: /guest/identify does NOT
go through get_guest_id, so it could still accept the reserved-token cookie, let an
attacker write their fingerprint onto a leaked dev guest row, and after the quiet
period have reconciliation rotate it to a fresh random token — escaping the
reserved-token check.

Extract the rule into a single source of truth (rate_limit.is_inert_dev_token: the
reserved token with the bypass OFF) and apply it at BOTH guest-identity entry points
— get_guest_id and the /guest/identify endpoint (drop the token so identity resolves
as cookieless). The dev row is created with no fingerprint, so reconciliation-by-
fingerprint can't reach it once the cookie path is closed.

Regression test: /identify with the reserved cookie + bypass off neither claims the
dev row nor writes the attacker fingerprint onto it. Codex re-confirmed no prod-bypass
path otherwise (flag inert + boot-abort, no runtime toggle, JWT/rate-limit/IDOR/kiosk
untouched).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@thewrz

thewrz commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator Author

Adversarial security review (Codex GPT-5.5 xhigh) — 3 passes to convergence

Per the requirement that this dev flag cannot weaken production hardening via any path:

  • Pass 1 → [P1]: the bypass flag was prod-safe, but the dev guest row (fixed public token + pre-set verified_email) was a latent backdoor if it leaked into a prod DB — the normal cookie lookup resolved it before any bypass check, then it passed the email gate. Fixed (a8883e7): reject the reserved token in get_guest_id when bypass off + dev guest no longer pre-verified.
  • Pass 2 → [P2]: /guest/identify doesn't use get_guest_id, so it could still claim/re-tokenize a leaked dev row. Fixed (199ea84): shared is_inert_dev_token guard applied at /identify too; dev row has no fingerprint so reconciliation can't reach it once the cookie path is closed.
  • Pass 3 → clean: "No [P1]/[P2] findings." Verified directly that ENV=production DEV_AUTH_BYPASS=1auth_bypass_enabled=False → startup aborts. No endpoint/header/cookie/query/body/DB-setting can toggle the flag. _dev_bypass_guest_id is reachable only when bypass is true. Admin/DJ JWT, ownership, kiosk trust, rate limiting, and last-admin protection are all untouched.

Prod-safety invariant: inert in production by construction (dev_auth_bypass and not is_production, env is a 2-value Literal) and the app refuses to boot if the flag is set with ENV=production. 14 dev tests + 104 gate-regression tests green.

🤖 Reviewed via Codex (GPT-5.5 xhigh, 3 passes) + manual analysis.

@thewrz
thewrz merged commit c2aefdd into main Jun 24, 2026
10 checks passed
@thewrz
thewrz deleted the feat/dev-auth-bypass branch June 24, 2026 22:17
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