Comment partager l'auth et les paiements entre Next.js, Expo et Tauri ?
Tout dépend de votre capacité à garder un seul modèle d'identité et un seul magasin d'entitlements. Le web utilise des sessions par cookie HttpOnly ; Expo stocke un bearer token dans expo-secure-store après le callback OAuth par deep link ; Tauri réutilise la session web dans sa webview — toutes validées par la même instance Better Auth. Les paiements suivent la même règle : 8 passerelles web et RevenueCat IAP mobile convergent vers un ledger de webhooks idempotent qui écrit des entitlements au niveau workspace, lus par chaque surface.
Les deux soudures où le SaaS multi-plateforme se déchire
Demandez à une équipe qui a soudé une app mobile sur un SaaS web où ça fait vraiment mal — la réponse n'est jamais l'UI. C'est l'identité et l'argent : le flux OAuth qui revient proprement dans le navigateur doit revenir dans l'app via un deep link ; et le client qui a payé sur le web attend que ses droits apparaissent aussitôt dans son téléphone.
Les équipes s'en rendent compte en général après avoir livré deux demi-solutions : une logique de cookies qui échoue silencieusement dans WKWebView, et un entitlement RevenueCat qui entre en conflit avec l'abonnement Stripe de la base web. La réparation est architecturale — et c'est deux fois la même : un modèle d'identité, un magasin d'entitlements.
Une instance Better Auth, trois transports de session
supastack garde une seule configuration Better Auth dans @supastack/auth avec des points d'entrée par surface. Les deux apps web (Next.js 16 et TanStack Start) utilisent des sessions par cookies HttpOnly SameSite=Lax — sûres contre le CSRF par défaut et compatibles Server Components. L'app Expo échange la fin du flux OAuth ou magic link au terme du callback deep link contre un bearer token, le persiste dans expo-secure-store (iOS Keychain / Android Keystore) et l'envoie en en-tête Authorization, validé par le même middleware d'auth.
Le shell desktop Tauri est une webview unique pointant sur l'app web : il hérite directement de la session web — pas de troisième flux d'auth à maintenir. Le guide auth mobile et le guide deep links documentent le contrat de callback ; notre guide monorepo web + mobile montre le code client partagé.
// 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.Huit passerelles de paiement, un contrat
Chaque prestataire de paiement web — Stripe, Lemon Squeezy, Polar, Creem, Dodo, Paddle, Whop et PayPal — se branche sur le même contrat @supastack/billing, et chaque webhook signé atterrit d'abord dans un ledger webhook_event idempotent avant de pouvoir toucher aux entitlements. Les retries des prestataires et les livraisons désordonnées deviennent des no-ops inoffensifs à la frontière de la base, pas des tickets support.
Les achats intégrés mobiles rejoignent le même ledger via RevenueCat : les reçus de store sont vérifiés côté serveur et les entitlements écrits sur le workspace, pas sur l'appareil — celui qui paie sur iOS est payé immédiatement aussi sur web et desktop. Le guide RevenueCat couvre le câblage App Store / Play ; les webhooks de facturation idempotents expliquent pourquoi le ledger est la pièce porteuse.
Le modèle d'entitlements qui rend la synchronisation ennuyeuse
Pourquoi ça reste ennuyeux : les entitlements vivent sur le workspace et sont résolus côté serveur. Aucun client ne détient l'état « payé » — le web interroge la session, le mobile interroge l'API, le desktop interroge l'app web — et les trois obtiennent la même réponse parce qu'il n'y a qu'un enregistrement pour répondre.
Restauration d'achat sur un nouveau téléphone, renouvellement Stripe sur le web, siège Agency ajouté dans le dashboard — chacun est une écriture dans les mêmes lignes d'entitlements que chaque surface lit à la prochaine demande. Il n'y a pas de moteur de synchro à construire parce qu'il n'y a rien à synchroniser.
| Plateforme | Transport de session | Chemin de paiement | Entitlements lus depuis |
|---|---|---|---|
| Next.js 16 web | Cookie HttpOnly | N'importe laquelle des 8 passerelles → ledger webhooks | Lignes d'entitlements Postgres partagées |
| TanStack Start web | Cookie HttpOnly | Mêmes adaptateurs, même ledger | Lignes d'entitlements Postgres partagées |
| Expo iOS / Android | Bearer token dans expo-secure-store | RevenueCat IAP → reçu vérifié serveur | Mêmes lignes (via API typée) |
| Tauri 2 desktop | Session web dans la webview | Checkout web dans le shell | Mêmes lignes (via app web) |
| Extension MV3 | Clé API workspace | — (lit les données workspace) | Mêmes lignes (via /api/v1) |
Ce que vous n'avez plus à construire
Converti en soirées de travail, c'est l'écart entre fondation et liste de features : le retour d'auth par deep link, la persistance de token dans SecureStore, huit intégrations de webhooks replay-safe, la vérification des reçus App Store et un modèle d'entitlements qui ne se contredit jamais entre appareils. Le tout livré, testé (693+ tests sans identifiants) et documenté.
Le Full Bundle à $299 contient toutes les surfaces dans un seul dépôt — sièges et palier agence sur la page tarifaire.
FAQ : partager auth et paiements entre plateformes
Les questions que posent les équipes avant de s'engager sur le stack unifié :
- Les utilisateurs mobile se connectent-ils séparément du web ?, Non. Le flux OAuth ou magic link revient dans l'app par deep link et émet la même session ; l'émetteur est la même instance Better Auth, donc web et mobile répondent pareil à « qui êtes-vous ».
- Et quelqu'un qui paie sur iOS mais utilise l'app web ?, RevenueCat livre l'achat côté serveur ; l'entitlement est écrit dans les mêmes lignes workspace que le web lit, et l'app web affiche « payé » à la requête suivante.
- Peut-on commencer web-only et ajouter les apps plus tard ?, Oui — l'auth et la facturation vivent dans les packages partagés, donc les apps mobile et desktop les héritent dès que ces surfaces sont activées.
- L'app Tauri a-t-elle besoin de son propre checkout ?, Non — le shell desktop réutilise la session et le checkout de l'app web dans sa webview ; il n'y a pas de seconde surface de paiement à sécuriser.