Authentication
The kit uses Supabase for authentication. By default, it uses anonymous authentication — which is the recommended approach for apps with paywalls and onboarding flows.
Why Anonymous Auth is the Default
Most successful subscription apps follow this pattern:
App Launch → Onboarding → Paywall → Main App
With anonymous auth, users:
- Don’t see a login screen before they understand your app’s value
- Can complete onboarding and see the paywall without friction
- Get a seamless experience that maximizes conversion
Anonymous users are real users. They have a unique ID, can make purchases, and their data persists. The only difference is they didn’t enter an email/password.
When to Use Anonymous Auth
| Use Case | Recommendation |
|---|
| Subscription app with paywall | ✅ Anonymous (default) |
| Content/media app | ✅ Anonymous |
| Social app requiring profiles | ❌ Use email/social auth |
| App with user-generated content | ❌ Use email/social auth |
| Enterprise/B2B app | ❌ Use email/social auth |
Authentication Options
The kit supports multiple auth methods. Enable them in config/app.config.ts:
auth: {
mode: 'anonymous', // or 'required'
providers: {
email: false, // Email + Password
apple: false, // Sign in with Apple
google: false, // Google OAuth
},
}
Available Methods
App Store Requirement: If you use Google OAuth (or any third-party social login), you must also offer Apple Sign In. See the Apple Sign In guide for details and other important App Store guidelines.
How It Works
Anonymous Auth
When the app launches, it automatically creates an anonymous user:
// This happens automatically in auth-context.tsx
const { data } = await supabase.auth.signInAnonymously();
The user gets a unique ID that persists across sessions. They can make purchases, save data, and use the app normally.
Switching to Other Auth Methods
If you need users to sign in, set auth.mode to 'required':
// config/app.config.ts
auth: {
mode: 'required',
providers: {
email: true,
apple: true,
},
}
Then use the pre-built auth screens:
// Navigate to login
router.push('/auth/login');
// Or signup
router.push('/auth/signup');
Key Files
| File | Purpose |
|---|
lib/supabase.ts | Supabase client configuration |
lib/auth.ts | Auth utility functions (email, Apple, Google) |
contexts/auth-context.tsx | Auth state provider |
config/app.config.ts | Auth configuration |
components/auth/ | Reusable auth UI components |
app/auth/ | Full auth screens (login, signup, forgot-password) |
Using Auth in Your App
Get Current User
import { useAuth } from '@/contexts/auth-context';
function MyComponent() {
const { user, isLoading } = useAuth();
if (isLoading) return <LoadingSpinner />;
return <Text>User ID: {user?.id}</Text>;
}
Check if Anonymous
import { isAnonymousUser } from '@/lib/auth';
const isAnon = isAnonymousUser(user);
// true if user signed in anonymously
Sign Out
const { signOut } = useAuth();
await signOut();
// User is now signed out, will create new anonymous user on next launch
UI Components
The kit includes ready-to-use auth components:
import { LoginForm, SignupForm, ForgotPasswordForm } from '@/components/auth';
// Login form with email/password
<LoginForm
onSuccess={() => router.replace('/(main)')}
onForgotPassword={() => router.push('/auth/forgot-password')}
onSignUp={() => router.push('/auth/signup')}
/>
// Signup form
<SignupForm
onSuccess={() => Alert.alert('Check your email')}
onLogin={() => router.push('/auth/login')}
/>
import { SocialAuthButtons } from '@/components/auth';
<SocialAuthButtons
showApple={true}
showGoogle={true}
onSuccess={() => router.replace('/(main)')}
onError={(err) => Alert.alert('Error', err.message)}
/>
Divider
import { AuthDivider } from '@/components/auth';
<SocialAuthButtons />
<AuthDivider text="or sign in with email" />
<LoginForm />
Account Linking (Coming Soon)
In a future update, anonymous users will be able to “upgrade” their account by linking an email or social provider — without losing their data or purchases.
Next Steps