¿Cómo compartir auth y pagos entre Next.js, Expo y Tauri?
Depende de mantener un solo modelo de identidad y un solo almacén de entitlements. La web usa sesiones con cookie HttpOnly; Expo guarda un bearer token en expo-secure-store tras el callback OAuth por deep link; Tauri reutiliza la sesión web dentro de su webview — todas validadas por la misma instancia Better Auth. Los pagos siguen la misma regla: 8 pasarelas web y RevenueCat IAP móvil desembocan en un ledger de webhooks idempotente que escribe entitlements a nivel workspace que cada superficie lee.
Las dos costuras donde se rompe el SaaS multi-plataforma
Pregunta a cualquier equipo que haya soldado una app móvil a un SaaS web dónde duele de verdad — la respuesta nunca es la UI. Son la identidad y el dinero: el flujo OAuth que vuelve limpio en el navegador debe volver a la app vía deep link, y el cliente que pagó en la web espera ver su entitlement en el teléfono al instante.
Los equipos suelen descubrirlo después de lanzar dos medias soluciones: lógica de cookies que falla en silencio dentro de WKWebView y un entitlement de RevenueCat que pelea con la suscripción Stripe en la base web. La reparación es arquitectónica — y es la misma dos veces: un modelo de identidad, un almacén de entitlements.
Una instancia Better Auth, tres transportes de sesión
supastack mantiene una única configuración Better Auth en @supastack/auth con puntos de entrada por superficie. Las dos apps web (Next.js 16 y TanStack Start) usan sesiones de cookie HttpOnly SameSite=Lax — seguras frente a CSRF por defecto y compatibles con Server Components. La app Expo canjea la finalización del OAuth o magic link al final del callback deep link por un bearer token, lo persiste en expo-secure-store (iOS Keychain / Android Keystore) y lo envía como cabecera Authorization, validado por el mismo middleware de auth.
El shell desktop de Tauri es una única webview apuntando a la app web, así que hereda la sesión web directamente — sin un tercer flujo de auth que mantener. La guía de auth móvil y la guía de deep links documentan el contrato de callback; nuestra guía de monorepo web + móvil muestra el código cliente compartido.
// packages/auth/src/client.ts — one client, two storages
import { createAuthClient } from "better-auth/react";
import { expoClient } from "@better-auth/expo/client";
export function createCrossPlatformAuthClient(baseURL: string, storage?: any) {
return createAuthClient({
baseURL,
plugins: storage ? [expoClient({ scheme: "myapp", storage })] : [],
});
}
// Web: cookie session. Mobile: bearer token in SecureStore.
// Desktop: the web app's own session inside the Tauri webview.Ocho pasarelas de pago, un contrato
Cada proveedor de pago web — Stripe, Lemon Squeezy, Polar, Creem, Dodo, Paddle, Whop y PayPal — se enchufa al mismo contrato @supastack/billing, y cada webhook firmado aterriza primero en un ledger webhook_event idempotente antes de poder tocar entitlements. Los reintentos de proveedores y las entregas desordenadas se convierten en no-ops inofensivos en la frontera de la base de datos, no en tickets de soporte.
Las compras in-app móviles entran al mismo ledger vía RevenueCat: los recibos de las stores se verifican en servidor y los entitlements se escriben en el workspace, no en el dispositivo — quien paga en iOS está pagado también al instante en web y desktop. La guía de RevenueCat cubre el cableado App Store / Play; los webhooks de billing idempotentes explican por qué el ledger es la pieza que soporta peso.
El modelo de entitlements que vuelve aburrida la sincronización
Por qué se mantiene aburrido: los entitlements viven en el workspace y se resuelven en servidor. Ningún cliente guarda el «pagado» — la web pregunta a la sesión, el móvil a la API, el desktop a la app web — y los tres obtienen la misma respuesta porque solo hay un registro que pueda responder.
Restaurar compra en un teléfono nuevo, renovación Stripe en la web, un asiento Agency añadido en el dashboard — cada uno es una escritura en las mismas líneas de entitlement que cada superficie lee en su próxima consulta. No hay motor de sync que construir porque no hay nada que sincronizar.
| Superficie | Transporte de sesión | Ruta de pago | Entitlements leídos de |
|---|---|---|---|
| Next.js 16 web | Cookie HttpOnly | Cualquiera de las 8 pasarelas → ledger de webhooks | Líneas de entitlement Postgres compartidas |
| TanStack Start web | Cookie HttpOnly | Mismos adaptadores, mismo ledger | Líneas de entitlement Postgres compartidas |
| Expo iOS / Android | Bearer token en expo-secure-store | RevenueCat IAP → recibo verificado en servidor | Mismas líneas (vía API tipada) |
| Tauri 2 desktop | Sesión web dentro de la webview | Checkout web dentro del shell | Mismas líneas (vía app web) |
| Extensión MV3 | Clave API workspace | — (lee datos del workspace) | Mismas líneas (vía /api/v1) |
Lo que te ahorras construir
Traducido a noches de trabajo, esa es la diferencia entre base y lista de features: el retorno de auth por deep link, la persistencia del token en SecureStore, ocho integraciones de webhooks a prueba de replays, la verificación de recibos de App Store y un modelo de entitlements que nunca se contradice entre dispositivos. Todo entregado, testeado (693+ tests sin credenciales) y documentado.
El Full Bundle de $299 contiene todas las superficies en un repo — asientos y niveles de agencia en la página de precios.
FAQ: compartir auth y pagos entre plataformas
Preguntas que hacen los equipos antes de comprometerse con el stack unificado:
- ¿Los usuarios móviles inician sesión aparte de la web?, No. El flujo OAuth o magic link vuelve a la app por deep link y emite la misma sesión; la instancia Better Auth emisora es la misma, así que web y móvil responden igual a «quién eres».
- ¿Y alguien que paga en iOS pero usa la app web?, RevenueCat entrega la compra en el lado servidor; el entitlement se escribe en las mismas líneas de workspace que la web lee, y la app web muestra «pagado» en la siguiente petición.
- ¿Se puede empezar solo con web y añadir apps después?, Sí — auth y billing viven en los paquetes compartidos, así que las apps móvil y desktop los heredan cuando activas esas superficies.
- ¿La app Tauri necesita su propio checkout?, No — el shell desktop reutiliza la sesión y el checkout de la app web dentro de su webview; no hay una segunda superficie de pago que blindar.