DEV Community

Shipstack
Shipstack

Posted on

How to share Supabase auth between Next.js and Expo (one client, both platforms)

Most teams building a web + mobile product end up with two auth integrations that slowly drift apart. You don't need that. Here's how to run a single Supabase auth layer across a Next.js web app and an Expo mobile app in a monorepo β€” including the gotchas nobody warns you about.

1. Keep the Supabase client framework-agnostic

Depend only on @supabase/supabase-js. Expose a factory that takes the storage adapter as an argument:

export function createSupabaseClient({ url, anonKey, storage, detectSessionInUrl }) {
  return createClient(url, anonKey, {
    auth: { storage, autoRefreshToken: true, persistSession: true, detectSessionInUrl },
  });
}
Enter fullscreen mode Exit fullscreen mode

Web uses default browser storage; mobile passes AsyncStorage. Same client, same auth helpers, same session context everywhere.

2. Reference env vars literally

Next and Expo only inline literal process.env.NEXT_PUBLIC_X / EXPO_PUBLIC_X accesses. A dynamic process.env[key] is undefined in the bundle. Pass the literals into the factory from each app.

3. Authenticate API routes with a Bearer token, not cookies

Cookie sessions are awkward to share with a mobile app. Instead, send the access token from the client session and validate it server-side:

const token = req.headers.get('Authorization')?.replace('Bearer ', '');
const { data } = await admin.auth.getUser(token);
Enter fullscreen mode Exit fullscreen mode

Identical from web fetch and from the mobile app.

4. The monorepo gotchas

  • One React version across the workspace (Expo pins it) β€” mixing breaks shared components.
  • node-linker=hoisted so pnpm's symlinks don't trip Metro.
  • A metro.config.js that adds the workspace root to watchFolders and nodeModulesPaths.

5. Keep billing server-authoritative

Let clients read their subscription (RLS, select-own) but never write it. The Stripe webhook (service role) is the only writer. No trust placed in the client.

Close

That's the whole pattern: a portable client, token-based route auth, and a monorepo that respects Metro's quirks. I packaged it (plus Stripe, push, RLS, docs) into a starter kit called Shipstack if you'd rather not wire it yourself β€” you can grab it here. But the patterns above are yours to use either way.

Top comments (0)