Authentication

Wire email, Apple, Google, session storage, and account management.

Authentication is Integrated through Supabase Auth. The app supports email/password, email OTP sign-in, sign-up verification, forgot/reset password, email/password changes, account deletion, and native Apple + Google sign-in.

Runtime shape

AuthProvider in lib/auth.tsx is the app's auth source of truth. It loads the initial Supabase session, subscribes to onAuthStateChange, exposes useAuth(), and centralizes auth methods so screens do not call Supabase Auth directly.

AuthGate is guest-first. It does not wall the whole app behind login; public screens can render signed out, and protected surfaces show SignInGate. Once a signed-in user lands in the auth stack, AuthGate dismisses the login flow back to the requesting screen or home.

Session storage

lib/supabase.ts creates createClient<Database>(...) with platform-aware persistence:

PlatformStorageWhy
iOS / AndroidChunkedSecureStoreSupabase sessions can exceed SecureStore's practical per-value limit, so lib/secure-store-chunked.ts splits long values into 1800-character chunks
Webwindow.localStorageWeb builds use browser storage and disable URL session detection

Keep auth tokens in the Supabase client storage layer. Do not mirror sessions into AsyncStorage, app preferences, or analytics properties.

Email flows

Screens live in app/(auth)/, while form UI lives in components/auth/. Validation uses react-hook-form with Zod schemas from lib/auth-schemas.ts.

FlowCode pathSupabase call
Sign in with passwordSignInFormsignInsignInWithPassword
Sign in with codeSignInFormsendLoginOtp / verifyLoginOtpsignInWithOtp({ shouldCreateUser: false })verifyOtp(type: "email")
Sign upSignUpFormsignUpsignUp with full_name user metadata
Verify sign-upSignUpFormverifyEmailOtpverifyOtp(type: "email")
Forgot passwordforgot-password.tsxresetPasswordresetPasswordForEmail
Complete resetreset-password.tsxcompletePasswordResetverifyOtp(type: "recovery")updateUser({ password })
Change emailaccount-security.tsxchangeEmail / verifyEmailChangeOtpupdateUser({ email })verifyOtp(type: "email_change")
Change passwordaccount-security.tsxchangePasswordupdateUser({ password })

Credential submissions and OTP verification do not retry because retries can consume one-time tokens or trip provider rate limits. Idempotent helper calls use the small retryAsync wrapper in lib/network.ts.

Apple Sign-In

Apple is native iOS only. components/auth/social-connections.tsx lazily requires expo-apple-authentication when Platform.OS === "ios", requests full name and email scopes, then passes the returned identity token to:

supabase.auth.signInWithIdToken({
  provider: "apple",
  token: credential.identityToken,
  nonce: credential.nonce,
});

When Apple returns name fields on first sign-in, the app updates auth metadata and upserts the profiles.full_name value. On Android and web, the module is not loaded and the button reports that Apple Sign-In is unavailable.

Setup checklist:

  • Enable Sign in with Apple for the Apple app identifier.
  • Enable the Apple provider in Supabase Auth.
  • Rebuild the native app after capability or config-plugin changes.

Google Sign-In

Google is native iOS/Android only. The app lazily requires @react-native-google-signin/google-signin, configures it with EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID and optional EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID, asks Android for Play Services when needed, then sends the Google ID token to Supabase with signInWithIdToken.

Create OAuth clients

Create a Google Cloud Web OAuth client and add this redirect URI:

https://<ref>.supabase.co/auth/v1/callback

Create iOS OAuth clients for every bundle identifier you build:

expo.soarstarter.com.dev
expo.soarstarter.com.preview
expo.soarstarter.com

Create Android OAuth clients for every package name and signing certificate SHA-1 you test or ship.

Configure Supabase

In Authentication → Providers → Google, enable Google and set:

Client IDs = <web-client-id>,<ios-client-id-1>,<ios-client-id-2>,...
Client Secret = <web-client-secret>
Skip nonce checks = ON

Put the Web client ID first and include each native client ID whose token audience Supabase should accept.

Set Expo env values

EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID=<web-client-id>.apps.googleusercontent.com
EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID=<ios-client-id>.apps.googleusercontent.com

Restart Metro after env changes. Rebuild a development client after changing native identifiers or config-plugin behavior.

If Google is unavailable in the current runtime, the button shows a friendly toast instead of crashing. GitHub appears in the shared social button map, but it currently shows a placeholder "not yet available" message.

Account & Security

app/account-security.tsx provides signed-in account operations:

  • Change email and verify the new email code.
  • Change password.
  • Sign out other sessions.
  • Enable or disable biometric lock when native biometrics are available.
  • Delete the account through the delete-account Edge Function.

Delete account calls supabase.functions.invoke("delete-account") with the signed-in JWT. The function deletes user-owned rows, removes files under sample-uploads/<user-id>/, then deletes the auth user. Rows and files go first so a mid-flight failure leaves the user signed in and the call retryable. subscriptions and payments cascade from auth.users.

Apple requires in-app account deletion for App Store approval. Keep this flow available if users can create accounts in the iOS app.

Signed-out feature gates

Use SignInGate from components/sign-in-gate.tsx for protected feature surfaces inside the otherwise-public app. It renders a localized sign-in call to action and opens /login, letting the auth stack dismiss itself after the session lands.

Verify authentication

  • Sign up with email/password, verify the email code, and confirm the auth user plus profiles row exist.
  • Sign out, then sign in with password and with an email code.
  • Run forgot/reset password from the auth stack.
  • In a native build, test Apple on iOS and Google on iOS/Android after provider setup.
  • Delete a test account and confirm its auth user, rows, and upload files are removed.

On this page