Documentation
Auth adapters
Auth is optional. Most apps launch without it. Add an adapter only when RevenueCat needs to know who your logged-in user is.
Do you need auth at all?
Probably not for your first paid version. RevenueCat creates an anonymous customer for every device, and Apple/Google handle restoring purchases on a new phone. Auth only matters when your app needs to recognize the same person across devices or on your server.
Skip auth if
- • Premium features unlock on the device itself
- • Users restore purchases through Apple/Google
- • You have no server-side, user-specific data
Add auth when
- • Users log in and sync data across devices
- • Firestore rules depend on a user id
- • Your backend needs to know who paid
Tip
The entire contract is one function
An “adapter” sounds fancy, but it is just an object with one function that answers a single question: “Who is the current user?”
src/paywall-ready/authAdapter.ts
1export type PaywallReadyAuth = {2 getUserId: () => string | null | Promise<string | null>;3};Good to know
getUserId() returns a stable id, PaywallReady logs that user into RevenueCat so purchases follow them everywhere. If it returns null, RevenueCat keeps using an anonymous customer. That is the whole contract — no tokens, no sessions, no callbacks.Pick the adapter that matches your stack
Choose exactly one of these. They all do the same thing: return your app’s stable user id, or null when nobody is logged in.
Option A — No auth (fastest launch)
App.tsx
1<PaywallReadyProvider2 iosApiKey={REVENUECAT_IOS_API_KEY}3 androidApiKey={REVENUECAT_ANDROID_API_KEY}4 entitlementId="pro"5>6 <App />7</PaywallReadyProvider>Users can buy, unlock premium, and restore purchases through their store account — all with zero login screens.
Option B — Firebase Auth
firebaseAuthAdapter.ts
1import { getAuth } from "firebase/auth";2import { createAuthAdapter } from "./paywall-ready";3 4const firebaseAuthAdapter = createAuthAdapter(() => {5 return getAuth().currentUser?.uid ?? null;6});Use this when your app already signs users in with Firebase — required if your Firestore rules check request.auth.uid.
Option C — Supabase
supabaseAuthAdapter.ts
1import { createAuthAdapter } from "./paywall-ready";2 3const supabaseAuthAdapter = createAuthAdapter(async () => {4 const { data } = await supabase.auth.getSession();5 return data.session?.user.id ?? null;6});Use this when Supabase owns your logins. Return the Supabase user id — it stays stable for the lifetime of the account.
Option D — Clerk
clerkAuthAdapter.ts
1import { createAuthAdapter } from "./paywall-ready";2 3const clerkAuthAdapter = createAuthAdapter(() => {4 return user?.id ?? null; // from useUser()5});Use this when Clerk handles login. RevenueCat only needs the stable Clerk user id, nothing else.
Option E — Your own backend
customAuthAdapter.ts
1import { createAuthAdapter } from "./paywall-ready";2 3const customAuthAdapter = createAuthAdapter(async () => {4 return (await mySession.load())?.userId ?? null;5});Return whatever stable id your backend uses for the user. RevenueCat keys the customer to it — and the Firebase entitlement mirror relies on this same id.
Watch out
How to know it worked
Verify these after wiring the adapter
- ✓Logged-out users either stay anonymous or see your normal login flow.
- ✓Logged-in users appear in the RevenueCat dashboard with your app's user id.
- ✓Purchasing once unlocks the same entitlement after an app restart.
- ✓Logging in on a second device shows the same premium status.
- ✓Restore purchases still works after login.