Pick the Simplest Auth Your App Can Live With
Authentication is where a lot of first apps stall, and usually because the developer picked a harder option than the app needed. Before writing any code, decide honestly what the account is actually for. If it exists only so a user's data follows them across devices, you need far less than you think.
Auth options, from least to most complexity
| Approach | Good when | What it costs you |
|---|---|---|
| No accounts at all, local storage only | The app is a personal tool and data never needs to move devices | No sync, no recovery if the device is lost |
| Email link or one-time code | You want accounts without password handling | Depends on an email provider actually delivering reliably |
| Email and password via a managed backend | You need standard accounts and a familiar flow | Password reset, validation, and account recovery screens |
| Social sign-in through OAuth providers | Your users expect it or friction at signup is killing you | Per-provider configuration, redirect URIs, and store review requirements |
Store the Token in Encrypted Storage
Once a user signs in, you are holding a credential, and where you put it matters. Expo provides expo-secure-store, which writes to the platform's encrypted storage on iOS and Android. That is where a session token belongs. Plain key-value storage is fine for a theme preference and wrong for anything that grants access to an account.
The important detail people miss is that secure storage is a native capability. It does not exist on web, so if your Expo app also runs in a browser you need a separate path there, typically browser storage or a secure cookie set by your own backend. Writing the storage layer as one small helper that branches on platform, rather than sprinkling storage calls through your screens, saves a lot of pain later.
The Redirect Trap
This is the failure that costs people a full day, usually on the morning of their first TestFlight build. OAuth needs a redirect URI - the address the provider sends the user back to after they approve. In Expo, that address is not the same in every environment.
Running inside Expo Go during development, the redirect resolves to an Expo development URL. In a standalone or bare native build, it resolves to your app's own scheme, in the shape of your application identifier followed by a redirect path. Those are different strings, and an OAuth provider only accepts redirect URIs you have registered with it in advance.
- Register every redirect URI you will use, not just the development one. The provider console needs the standalone build's URI added before the build exists, or the first real test fails.
- Set your app's scheme deliberately in your app config and keep it consistent. The native redirect is derived from your identifier, so an identifier change is also an auth change.
- Test auth in a real build, not only in Expo Go. This is the single highest-value test in the whole feature, and it is the one almost everyone postpones.
- Test it again after any change to your bundle identifier, package name, or scheme, because all three feed the redirect.
Gate Your Screens, Not Just Your Data
Once sign-in works, the app needs to know which screens a signed-out user can reach. Keep the session in one place - a context or provider that loads the stored token on launch and exposes a single signed-in flag - and let your navigation read from that. Scattering checks through individual screens produces a flow where a signed-out user can still deep link into an authenticated screen.
Handle the loading state explicitly. Reading the token from secure storage is asynchronous, so on launch there is a brief moment where you do not yet know whether the user is signed in. Rendering the signed-out screen during that gap is what causes the flash of a login screen that then disappears, which users read as a bug even when it is not.
Before You Submit to the Stores
Both stores have review expectations around accounts, and they are a common rejection reason for apps that otherwise pass. Check the current review guidelines yourself rather than relying on a summary, including this one, because these requirements are revised over time.
- Provide working demo account credentials in your review notes if any part of the app is behind a login. A reviewer who cannot sign in will reject the build.
- Do not require an account for functionality that does not need one. Forcing registration to see content that could be shown anonymously is a known rejection trigger.
- If you offer third-party social sign-in, check the current requirements around offering an equivalent private or alternative sign-in option, since these rules have changed before.
- Make sure account deletion is reachable from inside the app if you allow account creation, and confirm the current requirement rather than assuming.
Build the Auth You Need, Then Ship
Pick one sign-in method, put the token in secure storage behind a single helper, gate your navigation from one session source, and then make a standalone build and test the whole flow on a real device before you tell anyone the beta is ready. That order is what keeps auth from becoming the reason your launch slips by three weeks.
Short, practical drops on app ideas, validating fast, store listings, and passing app review. No spam, unsubscribe anytime.
Frequently asked questions
What is the easiest way to add authentication to an Expo app?
The easiest working auth is the one with the fewest moving parts your app can survive on. For many first apps that is an email link or one-time code rather than passwords or social sign-in, because every additional provider adds a redirect configuration that can break between development and production.
Where should I store the auth token in an Expo app?
In encrypted device storage via expo-secure-store on iOS and Android, not in plain key-value storage. Secure store is native only, so if your app also runs on web you need a separate path there such as browser storage or a secure cookie set by your backend. Wrap both behind one small helper.
Why does my Expo OAuth login work in development but fail in a real build?
Because the redirect URI is different. Inside Expo Go it resolves to an Expo development URL, while a standalone build uses your app's own scheme derived from its identifier. OAuth providers only accept redirect URIs you registered in advance, so the standalone one has to be added to the provider before the first real build is tested.
Do I need a client secret for OAuth in a mobile app?
No, and you should never ship one. Anything in the app bundle can be read by anyone who downloads it, so native apps are treated as public clients and the standard mobile flow is designed to work without a secret. If a setup guide tells you to embed one, that guide is for a server, not an app.
Will Apple or Google reject my app for how I handle sign-in?
It is a common rejection area, so check the current review guidelines directly rather than any summary. The recurring themes are providing working demo credentials in your review notes, not forcing an account for functionality that does not need one, and meeting the current requirements around alternative sign-in options and in-app account deletion.
Last reviewed by David on August 12, 2026


