Skip to content

feat(auth): add Cloudflare Turnstile bot protection and rate limiting#81

Open
pranavshankar1221 wants to merge 7 commits into
jpdevhub:mainfrom
pranavshankar1221:feat/turnstile-auth-protection
Open

feat(auth): add Cloudflare Turnstile bot protection and rate limiting#81
pranavshankar1221 wants to merge 7 commits into
jpdevhub:mainfrom
pranavshankar1221:feat/turnstile-auth-protection

Conversation

@pranavshankar1221
Copy link
Copy Markdown

@pranavshankar1221 pranavshankar1221 commented Jun 5, 2026

Description

Checklist

  • npm run lint passes with no errors
  • npm run build compiles without TypeScript errors
  • python -m pytest passes (including new tests I added)
  • No .env files, API keys, secrets, model weights, or __pycache__ in this diff
  • Branch is rebased on main, not merged

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Cloudflare Turnstile verification to Google authentication flow
    • Implemented rate limiting on authentication requests (5 requests per minute)
  • Documentation

    • Updated README with Turnstile configuration requirements and backend authentication details
  • Tests

    • Expanded authentication test coverage for new Turnstile and rate-limiting scenarios
  • Chores

    • Updated environment configuration templates and expanded gitignore rules

@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 5, 2026

@pranavshankar1221 is attempting to deploy a commit to the karan3431's projects Team on Vercel.

A member of the Team first needs to authorize it.

@gitguardian
Copy link
Copy Markdown

gitguardian Bot commented Jun 5, 2026

️✅ There are no secrets present in this pull request anymore.

If these secrets were true positive and are still valid, we highly recommend you to revoke them.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@pranavshankar1221 pranavshankar1221 force-pushed the feat/turnstile-auth-protection branch from 9507436 to 8e4c91c Compare June 5, 2026 17:52
@pranavshankar1221
Copy link
Copy Markdown
Author

Hi @jpdevhub I have done issue #61

Implemented Cloudflare Turnstile integration with backend token verification and auth endpoint rate limiting.

Addressed the GitGuardian finding by removing the Turnstile secret from the example environment file. Security checks are now passing.

Ready for review. Feedback is welcome.

If any of the changes is required feel free to ask mee !!

@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 6, 2026

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

Project Deployment Actions Updated (UTC)
fresh-scan-ai Ready Ready Preview, Comment Jun 7, 2026 4:28pm

@jpdevhub
Copy link
Copy Markdown
Owner

jpdevhub commented Jun 6, 2026

just git pull all the changes the merge conflicts will be either decreased or fully vanished and then fix if any error came in lint error or npm run build error then push .

@jpdevhub
Copy link
Copy Markdown
Owner

jpdevhub commented Jun 6, 2026

also tag the issue assigned to you

@pranavshankar1221
Copy link
Copy Markdown
Author

Hi @jpdevhub
Security: Add Bot Protection (Turnstile / reCAPTCHA) to Auth Routes issue #61

@jpdevhub
Copy link
Copy Markdown
Owner

jpdevhub commented Jun 7, 2026

the backend test is failing.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds Cloudflare Turnstile-based bot protection to the Google OAuth initiation flow and introduces rate limiting on auth-start endpoints, wiring the frontend to request a Turnstile token and the backend to verify it before returning/redirecting to the OAuth provider.

Changes:

  • Frontend: adds a Turnstile hook + UI gating and sends Turnstile tokens when starting Google login.
  • Backend: adds Turnstile verification helper and applies 5/minute rate limits to auth login endpoints (GET redirect + new POST returning redirect_url).
  • Docs/config: documents new env vars and expands .gitignore to avoid committing local/secrets artifacts.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/pages/AuthPage.tsx Runs Turnstile before initiating Google OAuth and disables login until verification is ready.
src/lib/useTurnstile.ts New hook to load/render/execute Cloudflare Turnstile and return tokens to callers.
src/lib/api.ts Changes loginUrl() to async; optionally POSTs Turnstile token to get a redirect URL.
backend/main.py Adds Turnstile enforcement + rate limiting to auth-start endpoints and introduces a POST variant.
backend/turnstile.py New helper to verify Turnstile tokens server-side via Cloudflare siteverify.
backend/requirements-base.txt Adds/adjusts SlowAPI dependency for rate limiting.
README.md Documents Turnstile-related frontend/backend environment variables and auth rate limiting behavior.
backend/.env.example Adds TURNSTILE_SECRET_KEY example configuration.
.env.example Adds VITE_TURNSTILE_SITE_KEY example configuration.
.gitignore Adds additional ignores for local build outputs, caches, and secret files.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/main.py Outdated
Comment on lines 22 to 30
from fastapi import Body, FastAPI, File, UploadFile, Form, HTTPException, Depends, Query, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import RedirectResponse
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.middleware import SlowAPIMiddleware
from slowapi.util import get_remote_address
from fastapi import FastAPI, File, Request, UploadFile, Form, HTTPException, Depends, Query
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse, RedirectResponse
Comment thread backend/main.py Outdated
Comment on lines 133 to 138
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
app.add_exception_handler(429, _rate_limit_exceeded_handler)
app.add_middleware(SlowAPIMiddleware)
app.state.limiter = limiter
app.add_middleware(SlowAPIMiddleware)
Comment thread backend/main.py Outdated
Comment on lines +390 to +392
async def _verify_turnstile(turnstile_token: str | None, request: Request) -> None:
if TURNSTILE_SECRET_KEY:
await verify_turnstile_token(turnstile_token, request.client.host)
Comment thread backend/requirements-base.txt Outdated
Comment on lines +9 to +10
slowapi>=0.1.4
slowapi==0.1.9 # rate limiting for FastAPI; added for per-user scan endpoint throttling
Comment thread backend/turnstile.py
Comment on lines +38 to +42
except Exception as exc:
raise HTTPException(
status_code=502,
detail=f'Turnstile verification failed: {exc}',
)
Comment thread src/lib/useTurnstile.ts
Comment on lines +54 to +58
useEffect(() => {
if (!siteKey) {
setReady(false);
return;
}
Comment thread backend/main.py
Comment thread backend/main.py Outdated
@jpdevhub
Copy link
Copy Markdown
Owner

jpdevhub commented Jun 7, 2026

Also check the review of the copilot, focus on the high severity one basically as its saying its dublicated somewhere else the module is their use that.

@jpdevhub
Copy link
Copy Markdown
Owner

jpdevhub commented Jun 7, 2026

Tag your issue no with closes #..

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 8, 2026

Review Change Stack

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid input: expected object, received boolean at "reviews.auto_review"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
📝 Walkthrough

Walkthrough

This PR integrates Cloudflare Turnstile bot protection into the Google OAuth login flow. Changes span environment configuration, a new backend token verification module, OAuth endpoint refactoring with rate limiting, a frontend React hook for Turnstile widget management, API client updates, and comprehensive end-to-end tests with CI bypass support.

Changes

Cloudflare Turnstile OAuth Protection

Layer / File(s) Summary
Configuration, documentation, and dependencies
.env.example, backend/.env.example, README.md, .gitignore, backend/requirements-base.txt, .github/workflows/greetings.yml
Environment templates introduce VITE_TURNSTILE_SITE_KEY (frontend) and TURNSTILE_SECRET_KEY (backend). README documents rate limiting (5 req/min) and token rejection behavior. .gitignore expanded for dev artifacts. slowapi dependency pinned.
Backend Turnstile verification module
backend/turnstile.py
New module reads TURNSTILE_SECRET_KEY, validates token via async POST to Cloudflare /siteverify, handles network failures as 502, and rejects invalid tokens with 400 including error codes.
Backend OAuth routes with Turnstile and rate limiting
backend/main.py
/api/v1/auth/login/google endpoint split into GET (query turnstile_token, redirects) and POST (JSON body, returns redirect_url). Both handlers rate-limited via slowapi with explicit 429 exception handler. Turnstile verification called before OAuth URL generation.
Frontend Turnstile React hook
src/lib/useTurnstile.ts
New hook dynamically loads Cloudflare script once, renders widget into container ref, tracks readiness and errors, and exposes execute() that returns verification token via promise resolved by Turnstile callback.
Frontend API and AuthPage Turnstile integration
src/lib/api.ts, src/pages/AuthPage.tsx
api.loginUrl() accepts optional turnstileToken and returns Promise<string> via POST when token provided. AuthPage wires hook, renders Turnstile container, conditionally executes verification with readiness/error UI, and navigates to OAuth URL after token acquisition.
Backend auth tests with Turnstile and rate limiting
backend/test_auth.py
Tests cover POST without Turnstile, invalid Turnstile rejection, GET redirect flow, and rate-limit enforcement with recovery. SKIP_TURNSTILE_VERIFICATION env flag allows CI to bypass external service calls.

Sequence Diagram

sequenceDiagram
  participant User
  participant Browser as Browser + useTurnstile
  participant AuthPage as AuthPage Component
  participant Backend as FastAPI Backend
  participant Cloudflare
  participant GoogleOAuth as Google OAuth

  User->>AuthPage: Click "Login with Google"
  AuthPage->>Browser: useTurnstile.execute()
  Browser->>Cloudflare: Request CAPTCHA widget
  Cloudflare-->>Browser: Widget rendered
  User->>Browser: Solve CAPTCHA
  Browser->>Cloudflare: Submit response
  Cloudflare-->>Browser: Verification token
  Browser-->>AuthPage: Token returned
  AuthPage->>Backend: POST /api/v1/auth/login/google {turnstile_token}
  Backend->>Cloudflare: POST /siteverify (secret + token)
  Cloudflare-->>Backend: {success: true}
  Backend->>GoogleOAuth: Generate OAuth URL
  GoogleOAuth-->>Backend: redirect_url
  Backend-->>AuthPage: {redirect_url}
  AuthPage->>GoogleOAuth: window.location.href = redirect_url
  GoogleOAuth-->>User: OAuth consent flow
Loading

🎯 3 (Moderate) | ⏱️ ~25 minutes

🐰 A carrot-guarded gate now stands tall,
Turnstile spins to catch them all,
Bots beware, the CAPTCHA's here,
OAuth flows with rabbit cheer! 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.32% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and clearly describes the main changes: adding Cloudflare Turnstile bot protection and rate limiting to authentication routes, which are the primary features introduced across the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/greetings.yml:
- Line 1: The workflow root key "name: Greetings" is indented and must start at
column 0; remove any leading spaces on that line (and ensure other top-level
keys such as "on" and "jobs" are also not indented) so the YAML root keys begin
at the leftmost column and the workflow is valid.

In `@backend/.env.example`:
- Around line 33-35: Remove the unresolved merge artifact "=========" from the
env template; edit the .env.example so only valid dotenv lines remain (e.g.,
keep CORS_ALLOW_ALL=true) and delete the dangling "=========" line to prevent
dotenv parsing/linting errors.

In `@backend/main.py`:
- Line 397: Replace the HTTPException detail that interpolates raw exception
text (the raise HTTPException(status_code=500, detail=f"Could not generate OAuth
URL: {exc}") and the similar raise around line 413) with a generic client-facing
message like "Internal server error generating OAuth URL", and instead log the
full exception server-side using the application's logger (e.g., call
logger.exception or logger.error with exc_info=True) inside the except block; do
this for both occurrences so clients never receive raw exception text while full
details are kept in server logs.
- Around line 379-383: The _verify_turnstile function currently skips
verification when TURNSTILE_SECRET_KEY is falsy, silently disabling protection;
change it to fail closed by rejecting requests (raise an HTTP error like
HTTPException 401/403) when TURNSTILE_SECRET_KEY is not set unless an explicit
test-only bypass flag is enabled (e.g., TURNSTILE_BYPASS_FOR_TESTING). Update
_verify_turnstile to check TURNSTILE_SECRET_KEY and if missing: if the bypass
flag is true, allow; otherwise log context (include client_host) and raise the
HTTP error; keep the existing call to verify_turnstile_token when the secret
exists. Ensure references: _verify_turnstile, TURNSTILE_SECRET_KEY,
verify_turnstile_token, and the new TURNSTILE_BYPASS_FOR_TESTING flag are used
so reviewers can locate the change.
- Around line 386-402: The GET and POST handlers currently use independent rate
buckets; replace the decorator on both login_google_get and login_google_post by
using limiter.shared_limit("5/minute", scope="login_google") (i.e., swap
`@limiter.limit`(...) for `@limiter.shared_limit`("5/minute", scope="login_google")
on both functions) so they share the same rate-limit scope; ensure the
import/limiter setup supports shared_limit if needed.

In `@backend/test_auth.py`:
- Around line 297-306: The test currently permits silent false positives by
logging when no 429 was seen; change it to assert that the limiter was hit
(raise an AssertionError with a clear message if rate_limit_hit is False) so the
test fails when no 429 occurred, and update the recovery check: replace
time.sleep(1) with a wait that exceeds the configured window for the 5/minute
policy (e.g., sleep(61) or use the policy window variable) and assert
r_recovery.status_code indicates success (e.g., 200) rather than just logging
success; reference variables/functions rate_limit_hit, attempts, info, url_get,
and r_recovery when making these changes.

In `@backend/turnstile.py`:
- Line 10: The module currently captures TURNSTILE_SECRET_KEY (and other
env-derived values) at import time; change this by removing the module-level
TURNSTILE_SECRET_KEY and any other env.get() calls and instead read
os.environ.get('TURNSTILE_SECRET_KEY', '') inside the verification routine
(e.g., verify_turnstile or whatever function performs the call), or accept a
secret_key argument passed in and fall back to reading the env there; update
references to use the local variable so runtime calls reflect env changes (e.g.,
after load_dotenv).

In `@README.md`:
- Around line 117-120: The blockquote in README.md contains a blank line
breaking the quote (triggering MD028); fix it by making the blockquote
contiguous: ensure each quoted line begins with ">" (e.g. for the two paragraphs
starting "Note: When using production auth flows..." and "Authentication start
requests...") and remove the empty line between them (or combine them into a
single continuous blockquote), so there are no blank lines inside the
blockquote.

In `@src/lib/useTurnstile.ts`:
- Around line 54-57: The hook useTurnstile leaves a stale error state set via
setError, causing execute() to keep throwing after a later successful
initialization; update the useEffect branches in useTurnstile (the siteKey check
and the other effect blocks around lines noted) to clear the error on
retry/success by calling setError(undefined/null) when you setReady(true) or
when re-initializing, and also clear error when calling reset/cleanup paths so
execute() sees a fresh state; locate functions/vars useTurnstile, setReady,
setError, execute and ensure every successful setup path resets the error state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2ee5eb6e-3801-4c61-8d05-33c7ad4ca6f7

📥 Commits

Reviewing files that changed from the base of the PR and between d33d33e and c2d139b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • .env.example
  • .github/workflows/greetings.yml
  • .gitignore
  • README.md
  • backend/.env.example
  • backend/main.py
  • backend/requirements-base.txt
  • backend/test_auth.py
  • backend/turnstile.py
  • src/lib/api.ts
  • src/lib/useTurnstile.ts
  • src/pages/AuthPage.tsx

Comment thread .github/workflows/greetings.yml
Comment thread backend/.env.example
Comment thread backend/main.py
Comment thread backend/main.py
Comment thread backend/main.py
Comment thread backend/test_auth.py
Comment on lines +297 to 306
if not rate_limit_hit and attempts >= 5:
info("Rate limiting may take time to accumulate; requests might not trigger immediately.")
info(f" (Made {attempts} requests without hitting 429 limit)")

# Wait a bit and try again to confirm recovery
time.sleep(1)
r_recovery = requests.get(url_get, allow_redirects=False, timeout=10)
if r_recovery.status_code != 429:
info("✓ Rate limit counter appears to reset after brief wait")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Rate-limit test currently allows false positives.

If no 429 is hit, the test only logs and still passes; and the 1-second “recovery” check is not valid for a 5/minute policy. This should fail when the limiter isn’t enforced.

Proposed fix
-    if not rate_limit_hit and attempts >= 5:
-        info("Rate limiting may take time to accumulate; requests might not trigger immediately.")
-        info(f"  (Made {attempts} requests without hitting 429 limit)")
+    if not rate_limit_hit:
+        fail(f"Rate limiting was not enforced after {attempts} requests")

-    # Wait a bit and try again to confirm recovery
-    time.sleep(1)
-    r_recovery = requests.get(url_get, allow_redirects=False, timeout=10)
-    if r_recovery.status_code != 429:
-        info("✓ Rate limit counter appears to reset after brief wait")
+    # Wait for server-provided retry window before checking recovery
+    retry_after = int(r.headers.get("Retry-After", "60")) if rate_limit_hit else 60
+    time.sleep(retry_after)
+    r_recovery = requests.get(url_get, allow_redirects=False, timeout=10)
+    if r_recovery.status_code == 429:
+        fail("Rate limit did not recover after Retry-After window")
+    ok("Rate limit recovered after Retry-After window ✓")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/test_auth.py` around lines 297 - 306, The test currently permits
silent false positives by logging when no 429 was seen; change it to assert that
the limiter was hit (raise an AssertionError with a clear message if
rate_limit_hit is False) so the test fails when no 429 occurred, and update the
recovery check: replace time.sleep(1) with a wait that exceeds the configured
window for the 5/minute policy (e.g., sleep(61) or use the policy window
variable) and assert r_recovery.status_code indicates success (e.g., 200) rather
than just logging success; reference variables/functions rate_limit_hit,
attempts, info, url_get, and r_recovery when making these changes.

Comment thread backend/turnstile.py

logger = logging.getLogger(__name__)

TURNSTILE_SECRET_KEY = os.environ.get('TURNSTILE_SECRET_KEY', '')
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid import-time snapshot of TURNSTILE_SECRET_KEY.

Because this value is captured at import time, later env loading (e.g., via load_dotenv) won’t update it. Read the secret inside verification instead.

Proposed fix
-TURNSTILE_SECRET_KEY = os.environ.get('TURNSTILE_SECRET_KEY', '')
+def _get_turnstile_secret_key() -> str:
+    return os.environ.get("TURNSTILE_SECRET_KEY", "")

 async def verify_turnstile_token(
     turnstile_token: Optional[str],
     remote_ip: Optional[str] = None,
 ) -> None:
-    if not TURNSTILE_SECRET_KEY:
+    secret = _get_turnstile_secret_key()
+    if not secret:
         raise HTTPException(
             status_code=500,
             detail='Turnstile secret key is not configured. Set TURNSTILE_SECRET_KEY.',
         )
@@
     payload = {
-        'secret': TURNSTILE_SECRET_KEY,
+        'secret': secret,
         'response': turnstile_token,
     }

Also applies to: 16-27

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/turnstile.py` at line 10, The module currently captures
TURNSTILE_SECRET_KEY (and other env-derived values) at import time; change this
by removing the module-level TURNSTILE_SECRET_KEY and any other env.get() calls
and instead read os.environ.get('TURNSTILE_SECRET_KEY', '') inside the
verification routine (e.g., verify_turnstile or whatever function performs the
call), or accept a secret_key argument passed in and fall back to reading the
env there; update references to use the local variable so runtime calls reflect
env changes (e.g., after load_dotenv).

Comment thread README.md
Comment on lines +117 to +120
> Note: When using production auth flows, set `VITE_TURNSTILE_SITE_KEY` in the frontend and `TURNSTILE_SECRET_KEY` in the backend. This enables Cloudflare Turnstile protection for auth endpoint requests.

> Authentication start requests are rate limited in the backend to 5 requests per minute. Invalid or missing Turnstile tokens are rejected with a standard 400 response.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix blockquote formatting to satisfy markdownlint.

There is a blank line inside a blockquote block, which triggers MD028. Keep the blockquote contiguous.

Suggested patch
 > Note: When using production auth flows, set `VITE_TURNSTILE_SITE_KEY` in the frontend and `TURNSTILE_SECRET_KEY` in the backend. This enables Cloudflare Turnstile protection for auth endpoint requests.
-
 > Authentication start requests are rate limited in the backend to 5 requests per minute. Invalid or missing Turnstile tokens are rejected with a standard 400 response.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
> Note: When using production auth flows, set `VITE_TURNSTILE_SITE_KEY` in the frontend and `TURNSTILE_SECRET_KEY` in the backend. This enables Cloudflare Turnstile protection for auth endpoint requests.
> Authentication start requests are rate limited in the backend to 5 requests per minute. Invalid or missing Turnstile tokens are rejected with a standard 400 response.
> Note: When using production auth flows, set `VITE_TURNSTILE_SITE_KEY` in the frontend and `TURNSTILE_SECRET_KEY` in the backend. This enables Cloudflare Turnstile protection for auth endpoint requests.
> Authentication start requests are rate limited in the backend to 5 requests per minute. Invalid or missing Turnstile tokens are rejected with a standard 400 response.
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 118-118: Blank line inside blockquote

(MD028, no-blanks-blockquote)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 117 - 120, The blockquote in README.md contains a
blank line breaking the quote (triggering MD028); fix it by making the
blockquote contiguous: ensure each quoted line begins with ">" (e.g. for the two
paragraphs starting "Note: When using production auth flows..." and
"Authentication start requests...") and remove the empty line between them (or
combine them into a single continuous blockquote), so there are no blank lines
inside the blockquote.

Source: Linters/SAST tools

Comment thread src/lib/useTurnstile.ts
Comment on lines +54 to +57
useEffect(() => {
if (!siteKey) {
setReady(false);
return;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clear stale Turnstile error state on retry/success.

Once error is set, it is never reset on a later successful setup, so execute() keeps throwing and auth can stay blocked after transient failures.

Suggested patch
   useEffect(() => {
     if (!siteKey) {
       setReady(false);
+      setError(null);
       return;
     }
 
     let canceled = false;
 
     async function setup() {
       try {
+        setError(null);
         await loadTurnstileScript();
         if (canceled) return;
         if (!window.turnstile || !containerRef.current) {
           throw new Error('Turnstile is unavailable in this environment.');
         }
@@
         }
+        setError(null);
         setReady(true);
       } catch (err) {
         if (!canceled) {
           const loadError = err instanceof Error ? err : new Error('Unknown Turnstile load error');
           setError(loadError);

Also applies to: 96-101, 117-119

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/useTurnstile.ts` around lines 54 - 57, The hook useTurnstile leaves a
stale error state set via setError, causing execute() to keep throwing after a
later successful initialization; update the useEffect branches in useTurnstile
(the siteKey check and the other effect blocks around lines noted) to clear the
error on retry/success by calling setError(undefined/null) when you
setReady(true) or when re-initializing, and also clear error when calling
reset/cleanup paths so execute() sees a fresh state; locate functions/vars
useTurnstile, setReady, setError, execute and ensure every successful setup path
resets the error state.

@pranavshankar1221
Copy link
Copy Markdown
Author

  • add Cloudflare Turnstile bot protection and rate limiting
    @jpdevhub

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants