Documentation
Firebase rules
Only needed when premium data lives in Firestore. Firestore rules cannot ask RevenueCat who paid, so you mirror paid status into Firestore first.
Do you actually need this?
Most apps can skip this page entirely. If your premium feature is a client-side unlock — hiding buttons, exports, themes, local limits — usePremium() already does the job and you are done.
You only need rules when paid users read or write Firestore documents that free users must never touch. Then the database itself has to enforce who paid, not just your UI.
The key limitation
Firestore security rules cannot call RevenueCat (or any external API). Rules can only look at the incoming request and at documents already in Firestore. So you copy — “mirror” — the paid status into a Firestore document, and the rules read that.
The full flow in plain English
- 1. User buys in the app. RevenueCat activates the
proentitlement, same as always. - 2. RevenueCat calls your server. A webhook fires on every purchase, renewal, and cancellation.
- 3. Your server writes one Firestore document. The webhook handler marks that user’s entitlement document as active (or inactive).
- 4. Firestore rules check that document. Premium reads are allowed only when the document says
active: true.
Good to know
Store one entitlement document per user
The webhook maintains a single small document per user. Rules read it whenever they need to decide if premium data should be available.
text
1users/{uid}/entitlements/pro2{3 active: true,4 source: "revenuecat",5 updatedAt: <server timestamp>,6 expiresAt: <timestamp, optional>7}The uid must be the same app user id your auth adapter gives RevenueCat. Same id on both sides, or the mirror breaks.
Add the Firestore rules
Copy these into your firestore.rules file. They do three things: let users read their own entitlement, block anyone from faking one, and protect premium content behind a paid check.
firestore.rules
1rules_version = '2';2service cloud.firestore {3 match /databases/{db}/documents {4 5 function isPro(uid) {6 return get(/databases/$(db)/documents/users/$(uid)/entitlements/pro)7 .data.active == true;8 }9 10 // Users own their basic document11 match /users/{uid} {12 allow read, write: if request.auth.uid == uid;13 14 // Entitlements are written only by the webhook (Admin SDK)15 match /entitlements/{name} {16 allow read: if request.auth.uid == uid;17 allow write: if false;18 }19 }20 21 // Premium-only collection22 match /premiumContent/{doc} {23 allow read: if request.auth != null && isPro(request.auth.uid);24 allow write: if false;25 }26 }27}Tip
Keep the mirror updated with a webhook
A RevenueCat webhook keeps the mirror honest — when a subscription renews, lapses, or is refunded, Firestore reflects it within seconds. The kit includes a Next.js route example that verifies a shared secret, reads app_user_id, and updates the document:
text
1POST /api/revenuecat-webhook2Authorization: Bearer ${REVENUECAT_WEBHOOK_SECRET}3 4event.app_user_id -> users/{uid}/entitlements/pro { active, expiresAt }Watch out
How to know it worked
Run through this with a sandbox purchase
- ✓A sandbox purchase creates or updates
users/{uid}/entitlements/pro. - ✓The entitlement document shows
active: truefor the paid user. - ✓A free logged-in user cannot read premium-only Firestore documents.
- ✓A paid logged-in user can read premium-only Firestore documents.
- ✓Client code cannot write or fake its own entitlement document.
- ✓Cancelling the sandbox subscription flips the document back to inactive.