Skip to content

test(auth): verify Supabase Auth rate limits through the /auth/v1 proxy - #191

Open
JOY (JOY) wants to merge 1 commit into
feat/spa-auth-flowsfrom
test/auth-rate-limits
Open

JOY (JOY) wants to merge 1 commit into
feat/spa-auth-flowsfrom
test/auth-rate-limits

Conversation

@JOY

Copy link
Copy Markdown
Contributor

Closes #34

Stacked on #101 (which is stacked on #91): it uses the e2e package from #91 and the e2e compose override from #101. Once those merge, this PR is rebased onto main and retargeted.

What it shows

A Playwright API spec, e2e/tests/auth-rate-limits.spec.ts, runs on the compose stack and sends requests through the kernel's /auth/v1 proxy:

Endpoint Under the limit Burst per client address Refill
POST /token?grant_type=password (sign-in, also token refresh) 400 invalid_credentials 30 150 per 5 min
POST /signup 200 30 30 per 5 min
POST /recover 200 30 30 per 5 min

For each endpoint the spec checks that:

  • the first 30 requests get the normal answer, then 429 over_request_rate_limit. Attempts count too, so wrong passwords and unknown addresses use up the bucket.
  • a request with spoofed X-Forwarded-For, X-Real-IP, X-Client-IP, Forwarded and Sb-Forwarded-For still gets 429. The proxy drops these headers, so they never reach a fresh bucket.
  • the same request from another client address (Node inside the kernel container, which reaches the proxy over 127.0.0.1) gets the normal answer. So the limit is per client address, not one bucket for everyone.

The limits live in Supabase Auth's memory, and every spec shares the runner's address. So the spec restarts the auth service before it runs (fresh buckets) and after (other specs and the next local run start clean). e2e/lib/compose.ts is the shared docker compose helper, and lib/db.ts now uses it.

Proxy change (protected path)

apps/kernel/src/http.ts now also drops Sb-Forwarded-For. Supabase Auth v2.197 uses that header as the rate-limit key ahead of X-Real-IP when GOTRUE_SECURITY_SB_FORWARDED_FOR_ENABLED is on (off by default). If an operator turned it on, the header would let a client choose its own bucket on every request. The unit test sends the header and checks that it does not reach the upstream; without the change it fails with expected '1.2.3.4' to be undefined.

Checked by hand (not in the spec)

  • Called directly (bypassing the proxy), Supabase Auth keys on X-Real-IP. With the runner's bucket used up, a direct request carrying the runner's address got 429, and one carrying a made-up address got 400. So the proxy setting X-Real-IP is what the limits rely on, and Supabase Auth's port is not published (docker port is empty; no ports: in compose).
  • The email budget is one bucket for the whole deployment. With GOTRUE_RATE_LIMIT_EMAIL_SENT=2, two sign-ups from the runner made a sign-up from another client fail with 429 over_email_send_rate_limit. With the default of 30 per hour and confirmations on, one client can block confirmation and recovery emails for everyone for an hour. Opened as Supabase Auth email budget is one bucket per deployment: one client can block confirmation and recovery emails for an hour #190 (a decision on the budget and CAPTCHA), not fixed here.

Docs

The plan's /auth/v1/* row records the verified limits and links #190. .env.example now says TRUSTED_PROXY_HOPS feeds sign-in, sign-up and recovery limits.

Verification

🤖 Generated with Claude Code

https://claude.ai/code/session_013LrvuThKGaPX1uDwUiwmGQ


Generated by Claude Code

…xy (#34)

A Playwright spec on the compose stack drives password sign-in, sign-up and
recovery through /auth/v1 until Supabase Auth answers 429
over_request_rate_limit. Each allows a burst of 30 requests per client
address; client-supplied forwarding headers do not reach a fresh bucket, and
another client address (inside the kernel container) is unaffected. The spec
restarts the auth service before and after, since the limits live in its
memory and every spec shares the runner's address.

The proxy now also drops Sb-Forwarded-For, which Supabase Auth uses as the
rate-limit key ahead of X-Real-IP when GOTRUE_SECURITY_SB_FORWARDED_FOR_ENABLED
is on. The email budget turned out to be one bucket per deployment; that is
#190.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013LrvuThKGaPX1uDwUiwmGQ

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Code review skipped — your organization has no extra usage available to pay for this review.

If your organization's extra usage balance is empty, an organization admin can add extra usage credits at claude.ai/admin-settings/usage. If its monthly spend limit was reached, an admin can raise it on the same page. If neither applies, contact Anthropic support.

Once extra usage is available, someone with write access to this repository can comment @claude review on this pull request to trigger a review.

@vercel

vercel Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
crove Ready Ready Preview Oct 15, 2026 8:28pm UTC

Request Review

@coderabbitai

coderabbitai Bot commented Oct 15, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f7009214-6dae-4fd2-bc57-1ecd889ce712

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request secures Supabase Auth rate limits by dropping the client-supplied sb-forwarded-for header at the kernel proxy level, updating documentation, and adding an end-to-end test suite to verify rate-limiting behavior. Feedback on the changes points out a bug in the test suite's inline Node.js script, where incorrect argument indexing (process.argv[1] and process.argv[2]) causes the test assertion to be incorrect.

Comment thread e2e/tests/auth-rate-limits.spec.ts

This branch was successfully deployed

1 active deployment
Preview — c6b705b9 Deployed Oct 15, 2026 by vercel[bot]
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.

2 participants