
Native OAuth uses a scheme redirect
There is no page to redirect to. iOS opensASWebAuthenticationSession and Android opens a Custom Tab, the user signs in, and the system hands control back to your app through a custom URL scheme — the same myapp:// scheme used for deep links.
Neither expo-auth-session nor expo-web-browser ships in the starter — ask the agent for social sign-in and it installs both. The flow it writes:
skipBrowserRedirect: true. Without itsignInWithOAuthtries to navigate a browser that doesn’t exist here. You want the URL, not the navigation.openAuthSessionAsync(url, redirectTo). The second argument is what tells the system sheet which URL means “done” — it must be the same string you sent to Supabase.setSession. Nothing detects the callback URL for you on native. Until you callsetSession, the user is not signed in and nothing is persisted.
If your Supabase client is configured with
flowType: "pkce", the callback
carries ?code= instead of a token fragment. Swap the last two steps for
supabase.auth.exchangeCodeForSession(code) — the rest is identical.Allowlist the exact value
In your Supabase dashboard → Authentication → URL Configuration → Redirect URLs, add the literal scheme URL:https:// URL. It has to match what makeRedirectUri produced, character for character.
The dev-vs-prod trap
makeRedirectUri returns a different value depending on how the app is running, and this is what breaks in production after working all through development:
Allowlist both. Only allowlisting the
exp:// URL you saw during development means every store build fails sign-in with a dismissed sheet and no error.
Two more consequences, both Vibely-specific:
- Set your scheme before you build. The
schemeinapp.jsoncomes from Settings → Mobile app → Deep link scheme. Leave it at the template placeholder and the production build is refused with a message telling you to set it — but worse, changing it after you’ve allowlisted a redirect silently invalidates that allowlist entry. Pick the scheme first, allowlist it, then build. - The old auth proxy is gone. Guides that tell you to pass
useProxy: trueor to allowlisthttps://auth.expo.io/@you/your-appare pre-SDK-48 and no longer work. Use the scheme.
Sign in with Apple
Useexpo-apple-authentication, not the web flow above. It presents the native Apple sheet, which is what App Store reviewers expect to see, and it returns an identity token you hand straight to Supabase:
- iOS only.
AppleAuthentication.isAvailableAsync()is false on Android and in the web preview. Render the button conditionally. - Name and email arrive once. Apple returns
fullNameandemailonly on the user’s first authorization. Persist them then; a second sign-in returns nulls. - Enable the Apple provider in Supabase and add your bundle ID as an authorized client — see auth-apple.
Guideline 4.8
If your app offers a third-party or social login — Google, Facebook, X — App Store Review Guideline 4.8 requires you to also offer an equivalent login option that:- limits data collection to the user’s name and email address;
- lets the user keep their email address private from your app; and
- does not collect interactions with your app for advertising without consent.
Sessions
The starter’s Supabase client already pointsauth.storage at a Keychain / Android Keystore–backed store (src/integrations/supabase/secure-storage.ts, chunked past the platform’s 2048-byte item limit). Sessions survive a relaunch and refresh themselves. Leave that file alone, and never move identity into AsyncStorage — it is unencrypted. See Native → Secure storage.
For “Unlock with Face ID”, gate a session that is already on the device — don’t use biometrics as the primary credential. Native → Biometrics has the pattern.
Checklist before you ship
- Both redirect values allowlisted in Supabase — the
exp://dev URL andmyapp://auth-callback. - Deep link scheme set in Settings → Mobile app, and it matches the allowlisted URL.
- Sign-in tested on a real build, not just Expo Go.
- Sign in with Apple present if any other social login is.
- A way to try the app, or at least see what it does, without an account — reviewers reject blank login walls.