Authentication Service
Configure Better Auth, Google OAuth, and Apple native sign-in
Authentication
EasyStarter's mobile app uses Better Auth for authentication, with the following built-in providers:
- Email + password login
- Google OAuth
- Apple native sign-in (iOS only)
Server configuration lives in apps/server/src/lib/auth.ts. Apple Sign-In uses the native ID token flow: the app triggers the system-level Apple sign-in sheet, retrieves the identityToken, and passes it directly to Better Auth for verification — no web redirect needed.
Required Environment Variables
Server
BETTER_AUTH_SECRET=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
APPLE_APP_BUNDLE_IDENTIFIER=Get BETTER_AUTH_SECRET
BETTER_AUTH_SECRET is used by Better Auth to sign and encrypt session data. It must be a sufficiently long random string.
openssl rand -base64 32Copy the output and add it to both local and production environments:
BETTER_AUTH_SECRET=your-long-random-secretBETTER_AUTH_SECRET=your-long-random-secretCreate Google OAuth Client
Google Cloud Console: Google Cloud Console
- Log in and select or create a project
- Go to
APIs & Services > Credentials - Click
Create Credentials→ selectOAuth client ID - Complete the
OAuth consent screenif prompted - Select
Web applicationas the application type - Configure the redirect URI
Key fields:
Authorized JavaScript origins: your frontend URL, e.g.https://yourdomain.comAuthorized redirect URIs:{SERVER_URL}/api/auth/callback/google
Mobile Google OAuth requires an HTTPS callback URL. Use ngrok to expose your local server over HTTPS.
Why ngrok is required for mobile development
Mobile Google Sign-In uses a deep link callback flow, not a simple browser redirect:
- The app opens Google's login page via
expo-web-browser - After the user logs in, Google redirects to the server callback URL (
SERVER_URL/api/auth/callback/google) - The server processes the callback, then redirects the user back to the app via a deep link (e.g.
myapp://callback) - The OS intercepts the deep link, relaunches the app, and the login completes
This flow has two hard requirements:
- Google OAuth enforces HTTPS —
http://localhostis not accepted - Physical devices and simulators cannot access your dev machine's
localhostdirectly — the server must have a publicly reachable address
ngrok solves both: it exposes your local localhost:3001 as a public HTTPS URL, so Google can complete the redirect and the server can send the user back to the app via deep link.
Install ngrok
Download and install from the ngrok website, or use Homebrew:
brew install ngrokAfter installing, sign up for a free account and authenticate:
ngrok config add-authtoken YOUR_AUTH_TOKENFind your Auth Token at ngrok Dashboard → Your Authtoken.
Start the tunnel
After starting the local server (pnpm dev:server), open a new terminal and run:
ngrok http 3001ngrok will output an HTTPS URL like:
Forwarding https://xxxx-xxxx.ngrok-free.app -> http://localhost:3001Use this URL as the Google OAuth redirect URI:
https://xxxx-xxxx.ngrok-free.app/api/auth/callback/googleAlso update SERVER_URL in .dev.vars to this ngrok URL so Better Auth's baseURL and the OAuth callback stay in sync.
Note: The free ngrok plan generates a new URL on every restart. You'll need to update both the Google Cloud Console redirect URI and your local
.dev.varseach time.
After creating, you'll receive:
Client ID→GOOGLE_CLIENT_IDClient Secret→GOOGLE_CLIENT_SECRET
Configure Apple Native Sign-In
The mobile Apple Sign-In uses the native iOS flow: the app calls expo-apple-authentication to trigger the system sign-in sheet, retrieves the Identity Token, and the server validates it using the Bundle ID (APPLE_APP_BUNDLE_IDENTIFIER) as the audience.
Reference: Better Auth Apple docs
Enable Sign In with Apple on your App ID
The App ID was already created in the Configure app.json chapter. Now enable Apple Sign-In on it:
- Log in to Apple Developer Portal → Identifiers
- Find and click your App ID
- Under Capabilities, check
Sign In with Apple - Click Continue → Save
Create and configure a Service ID
- In
Identifiers, click+, selectService IDs→ Continue - Enter a Description and Identifier (e.g.
com.yourcompany.yourapp.si) — this becomes yourAPPLE_CLIENT_ID - Click Register
- Click the Service ID you just created → check
Sign In with Apple→ clickConfigure - Under Primary App ID, select your App ID
- Configure Domains and Subdomains and Return URLs:
| Environment | Domains and Subdomains | Return URLs |
|---|---|---|
| Development | xxxx-xxxx.ngrok-free.app | https://xxxx-xxxx.ngrok-free.app/api/auth/callback/apple |
| Production | server.yourdomain.com | https://server.yourdomain.com/api/auth/callback/apple |
In development, use your ngrok URL. Remember to update both the Domain and Return URL here every time ngrok restarts. In production, use your actual server domain.
- Click Next → Done → Continue → Save
Set Environment Variables
For local development, add everything to apps/server/.dev.vars:
BETTER_AUTH_SECRET=your-long-random-secret
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
APPLE_APP_BUNDLE_IDENTIFIER=com.yourcompany.yourappFor production, put sensitive values in apps/server/.env.production:
BETTER_AUTH_SECRET=your-long-random-secret
GOOGLE_CLIENT_SECRET=your-google-client-secretAdd non-sensitive IDs to vars in apps/server/wrangler.jsonc:
"vars": {
"GOOGLE_CLIENT_ID": "your-google-client-id",
"APPLE_APP_BUNDLE_IDENTIFIER": "com.yourcompany.yourapp"
}How Better Auth Works in the Mobile App
The mobile authClient is configured in apps/native/lib/auth/auth.client.ts, using the @better-auth/expo adapter to handle deep link callbacks and cookie storage.
Currently supported:
- Email/password signup and login
- Google OAuth (via deep link callback)
- Apple native sign-in (via ID token, iOS only)
- Cookie-based cross-platform session management
Apple Sign-In is only available on physical devices and TestFlight. It does not work in the iOS Simulator.