Skip to content

Issue 57: AppCheck Enforcement#84

Merged
rk234 merged 16 commits into
mainfrom
issue57-appcheck-enforcement
May 18, 2026
Merged

Issue 57: AppCheck Enforcement#84
rk234 merged 16 commits into
mainfrom
issue57-appcheck-enforcement

Conversation

@orimcoding
Copy link
Copy Markdown
Collaborator

@orimcoding orimcoding commented May 17, 2026

Pull Request

Description

Added AppCheck enforcement to protect public endpoints from bot abuse. Donor registration and family form submissions now require reCAPTCHA validation on the client side and token verification on the server side before processing.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Performance improvement
  • Other (please describe):

Related Issues (put task name here from notion)

Issue #57

Additional Notes

@rk234 @ArnavGupta23 -> here's some setup required:

Assuming you both have access to Google Cloud Console, one of y'all needs to do the following to enable AppCheck:

  1. Create a reCAPTCHA Enterprise key in Google Cloud Console
  2. Add the public key to your .env.local and share it with us so we can do the same 👍

That's everything else that's needed to activate the protection :)

Summary by CodeRabbit

  • New Features
    • App Check protection added to donor registration and family form submissions for improved security.
    • Client-side App Check initialization added on checkout and form pages to enable token checks.
    • Submissions now surface a clear error asking users to refresh and retry if App Check token retrieval fails.

Review Change Stack

@orimcoding orimcoding requested review from ArnavGupta23 and rk234 May 17, 2026 23:20
@orimcoding orimcoding self-assigned this May 17, 2026
@orimcoding orimcoding linked an issue May 17, 2026 that may be closed by this pull request
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 17, 2026

Warning

Rate limit exceeded

@rk234 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 27 minutes and 8 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c59cd7ce-85e3-4fea-a8da-d533fdc8b0fe

📥 Commits

Reviewing files that changed from the base of the PR and between e48b0d4 and af18794.

📒 Files selected for processing (8)
  • .github/workflows/deploy.yaml
  • app/src/hooks/mutations/useRegisterDonor.ts
  • app/src/hooks/mutations/useSubmitFamilyForm.ts
  • app/src/lib/firebase.client.ts
  • app/src/routes/__root.tsx
  • app/src/server/functions/familyForm.ts
  • app/src/server/functions/profile.ts
  • app/src/server/middleware/appCheckMiddleware.ts
📝 Walkthrough

Walkthrough

Client routes initialize Firebase App Check; client mutation hooks fetch App Check tokens and attach them to request payloads; new server middleware verifies tokens and injects claims; protected server functions require and validate appCheckToken in their input schemas.

Changes

Firebase App Check Integration

Layer / File(s) Summary
Firebase App Check client library
app/src/lib/firebase.client.ts
getClientAppCheck() initializes App Check with reCAPTCHA Enterprise using VITE_RECAPTCHA_ENTERPRISE_KEY, and getAppCheckToken() retrieves tokens (both handle missing config and errors).
Route-based App Check initialization
app/src/routes/_storefront/checkout.tsx, app/src/routes/family/drive/$driveId/form.tsx
Checkout and family form routes call getClientAppCheck() in useEffect on mount and warn on initialization failure.
Client mutation token injection
app/src/hooks/mutations/useRegisterDonor.ts, app/src/hooks/mutations/useSubmitFamilyForm.ts, app/src/hooks/useCheckoutFlow.ts
useRegisterDonor and useSubmitFamilyForm fetch App Check tokens and merge them into payloads before calling registerDonor/submitFamilyForm; useCheckoutFlow updates the mutateAsync call shape.
Server-side token verification middleware
app/src/server/middleware/appCheckMiddleware.ts
Middleware reads appCheckToken from data, verifies with admin.appCheck().verifyToken, injects verified claims into context.appCheckClaims, and normalizes errors.
Protected server endpoints
app/src/server/functions/profile.ts, app/src/server/functions/familyForm.ts
registerDonor and submitFamilyForm apply appCheckMiddleware and validate inputs against schemas extended with required appCheckToken fields.

Sequence Diagram

sequenceDiagram
  participant Browser
  participant MutationHook
  participant FirebaseClient as firebase.client.ts
  participant ServerFn
  participant Middleware as appCheckMiddleware
  participant FirebaseAdmin as firebase-admin

  Browser->>MutationHook: user submits form / register
  MutationHook->>FirebaseClient: getAppCheckToken()
  FirebaseClient-->>MutationHook: token string | null
  MutationHook->>ServerFn: call with payload + appCheckToken
  ServerFn->>Middleware: appCheckMiddleware receives data
  Middleware->>FirebaseAdmin: admin.appCheck().verifyToken(appCheckToken)
  FirebaseAdmin-->>Middleware: verified claims | error
  Middleware-->>ServerFn: next({ context.appCheckClaims })
  ServerFn-->>Browser: response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • ArnavGupta23
  • rk234

Poem

🐰 A Rabbit's App Check Cheer
I fetched a token, snug and neat,
From client code with bounding feet.
Server checks nod, no fraud in sight—
Donations hop through safe tonight. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% 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 pull request title 'Issue 57: AppCheck Enforcement' is clear and directly related to the changeset. It accurately represents the main objective of adding AppCheck enforcement for protecting public endpoints, which aligns with the substantial changes across multiple files for reCAPTCHA validation and token verification.
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
  • Commit unit tests in branch issue57-appcheck-enforcement

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
Contributor

@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: 3

🤖 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 `@app/src/hooks/mutations/useRegisterDonor.ts`:
- Around line 16-26: Run the project formatter (oxfmt) on the file containing
useRegisterDonor and commit the formatted output so CI passes; specifically
reformat the mutationFn block that calls getAppCheckToken and registerDonor (the
async callback in useRegisterDonor) using the project's oxfmt settings and then
stage the updated file.

In `@app/src/lib/firebase.client.ts`:
- Around line 3-6: The code is using an unsafe type assertion to call
.getToken() on the AppCheck instance; instead, import and call the typed
getToken(appCheck) helper from firebase/app-check. Locate the AppCheck
initialization where initializeAppCheck(...) and ReCaptchaEnterpriseProvider are
used and remove the "(... as unknown as ...).getToken()" style call; import
getToken from "firebase/app-check" and call await getToken(appCheck) (or pass
the AppCheck instance variable) to obtain the token with proper typing.

In `@app/src/server/middleware/appCheckMiddleware.ts`:
- Around line 1-36: This file fails the repo formatter; run the project's
formatter (oxfmt) on the new middleware file and commit the changes so CI
passes. Specifically, open the file containing appCheckMiddleware (created via
createMiddleware) and run "oxfmt --replace" or the repository's configured
formatting command to apply the expected line breaks/indentation and import
styling, then stage the formatted file and push the commit.
🪄 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

Run ID: 483991f6-877f-4850-974e-8bccf1a8f6ad

📥 Commits

Reviewing files that changed from the base of the PR and between b2033a3 and fbe14ff.

📒 Files selected for processing (8)
  • app/src/hooks/mutations/useRegisterDonor.ts
  • app/src/hooks/mutations/useSubmitFamilyForm.ts
  • app/src/lib/firebase.client.ts
  • app/src/routes/_storefront/checkout.tsx
  • app/src/routes/family/drive/$driveId/form.tsx
  • app/src/server/functions/familyForm.ts
  • app/src/server/functions/profile.ts
  • app/src/server/middleware/appCheckMiddleware.ts

Comment thread app/src/hooks/mutations/useRegisterDonor.ts Outdated
Comment thread app/src/lib/firebase.client.ts
Comment thread app/src/server/middleware/appCheckMiddleware.ts Outdated
Copy link
Copy Markdown
Contributor

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/hooks/useCheckoutFlow.ts (1)

105-140: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Preserve the App Check failure message in the toast.

Line 107 can now fail with the explicit App Check refresh guidance from useRegisterDonor, but this catch path always shows a generic toast. That hides the only actionable recovery step for the new failure mode.

Proposed fix
   } catch (error) {
     const message =
       error instanceof Error ? error.message : "Failed to register or login";
     console.error(message);
-    toast.error("Failed to register or login");
+    toast.error(message);
   }
🤖 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 `@app/src/hooks/useCheckoutFlow.ts` around lines 105 - 140, submitRegister's
catch path (and the loginMutation onError) currently always shows a generic
"Failed to register or login" toast, which hides actionable App Check refresh
guidance returned by useRegisterDonor; update the catch block after
registerMutation.mutateAsync (and the loginMutation onError handler) to compute
the message as done (const message = error instanceof Error ? error.message :
"Failed to register or login") and pass that message into toast.error(message)
instead of the hardcoded string so the App Check guidance is preserved;
references: submitRegister, registerMutation.mutateAsync, loginMutation.mutate,
onError.
🤖 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.

Outside diff comments:
In `@app/src/hooks/useCheckoutFlow.ts`:
- Around line 105-140: submitRegister's catch path (and the loginMutation
onError) currently always shows a generic "Failed to register or login" toast,
which hides actionable App Check refresh guidance returned by useRegisterDonor;
update the catch block after registerMutation.mutateAsync (and the loginMutation
onError handler) to compute the message as done (const message = error
instanceof Error ? error.message : "Failed to register or login") and pass that
message into toast.error(message) instead of the hardcoded string so the App
Check guidance is preserved; references: submitRegister,
registerMutation.mutateAsync, loginMutation.mutate, onError.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 47d2453a-6ac1-4be6-908a-32acaacfad49

📥 Commits

Reviewing files that changed from the base of the PR and between fbe14ff and e48b0d4.

📒 Files selected for processing (4)
  • app/src/hooks/mutations/useRegisterDonor.ts
  • app/src/hooks/useCheckoutFlow.ts
  • app/src/lib/firebase.client.ts
  • app/src/server/middleware/appCheckMiddleware.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/src/lib/firebase.client.ts
  • app/src/server/middleware/appCheckMiddleware.ts

@rk234 rk234 merged commit 54d85e1 into main May 18, 2026
4 checks passed
@rk234 rk234 deleted the issue57-appcheck-enforcement branch May 18, 2026 01:51
@coderabbitai coderabbitai Bot mentioned this pull request May 19, 2026
7 tasks
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.

AppCheck enforcement for public routes

2 participants