tva
← Insights

Quand l'authentification Docker casse la connexion mobile : une histoire de bug multi-plateforme

Le ticket est arrivé un jeudi après-midi : “Google Sign-In ne fonctionne pas sur mobile.” Le fil de support était court – trois utilisateurs, tous sous iOS, tous signalant le même spinner OAuth qui ne menait nulle part. L'application web fonctionnait. Le bureau fonctionnait. Seul le mobile échouait, et seulement pour la connexion OAuth. L'authentification par email et mot de passe continuait sans problème.

C'est le genre de rapport de bug qui promet une résolution rapide et livre une après-midi de confusion. Les symptômes de surface – un flux d'auth cassé sur une plateforme – pointent simultanément vers une douzaine de directions. URI de redirection. CORS. Échange de tokens. Stockage de session. Configuration côté client. Chacune est plausible. Aucune ne s'est avérée être le problème.

Le stack

L'application tourne sur une instance Supabase auto-hébergée – le stack Docker Compose complet, déployé sur un serveur dédié. L'architecture de Supabase sépare l'authentification dans son propre microservice : GoTrue, qui gère les flux OAuth, l'émission de tokens et la gestion des utilisateurs. GoTrue communique avec PostgreSQL via une connexion directe à la base de données, lisant et écrivant dans le schéma auth. Le frontend est une application React Native communiquant avec Supabase via la bibliothèque client JavaScript standard.

C'est une configuration relativement courante pour les équipes qui préfèrent contrôler leurs données. Supabase auto-hébergé vous donne la base de données PostgreSQL complète, la couche d'authentification, les abonnements en temps réel et le service de stockage, tout fonctionnant dans des conteneurs que vous gérez. Le compromis – et cet incident en est un exemple précis – est que les services gérés gèrent silencieusement des détails de configuration qui deviennent des défaillances visibles quand vous les gérez vous-même.

La première hypothèse : inadéquation de l'URI de redirection

Les erreurs OAuth sur mobile commencent presque toujours par les URI de redirection. Les applications natives utilisent des schémas d'URL personnalisés (myapp://auth/callback) plutôt que des URL HTTPS, et une inadéquation entre le callback enregistré et ce qu'attend le fournisseur OAuth produit exactement le type de défaillance silencieuse que nous observions : le flux OAuth s'initie, le navigateur s'ouvre, l'utilisateur s'authentifie, et le callback ne se complète jamais.

Nous avons vérifié la configuration de l'URI de redirection dans Google Cloud Console. Vérifié les paramètres du projet Supabase. Vérifié la configuration de deep-link de l'application React Native. Tout correspondait. Les URI étaient correctement enregistrés des deux côtés. L'écran de consentement OAuth s'est terminé sans erreur – Google acceptait la connexion. La défaillance se produisait après le callback, quelque part dans l'échange de tokens.

La deuxième hypothèse : CORS

Les en-têtes Cross-Origin Resource Sharing sont le deuxième réflexe pour le débogage OAuth mobile. Quand une application mobile fait des requêtes vers un backend sur un domaine personnalisé, la configuration CORS détermine si le modèle de sécurité du navigateur autorise la requête. Une politique CORS mal configurée produit une classe d'erreurs qui peut sembler identique à une défaillance d'authentification.

Mais en réalité, CORS opère au niveau réseau avant que la logique d'authentification ne s'exécute. Et le symptôme – l'authentification par email fonctionnait tandis que OAuth ne fonctionnait pas – était incohérent avec une défaillance CORS. Les deux utilisent le même endpoint GoTrue. Si CORS rejetait les requêtes, il les rejetterait toutes, pas sélectivement. Nous avons ajouté une journalisation verbosité au client mobile. Les requêtes atteignaient le service GoTrue. Le service répondait. Les codes de statut étaient 500.

Lire les logs réels

Les logs de conteneurs sont là où le débogage auto-hébergé diverge le plus nettement des services gérés. Avec Supabase Cloud, le tableau de bord expose des logs structurés de chaque service. Avec Docker auto-hébergé, vous tirez les logs des conteneurs individuels directement :

docker logs supabase-auth --since 1h 2>&1 | grep -i error

Le conteneur GoTrue journalisait des erreurs en continu. La ligne pertinente :

level=error msg="Error creating oauth state" error="ERROR: function gen_random_uuid() does not exist (SQLSTATE 42883)"

Ce n'était pas une erreur de redirection OAuth. C'était un échec de résolution de fonction PostgreSQL. gen_random_uuid() est fournie par l'extension pgcrypto – une extension PostgreSQL standard installée par Supabase dans le schéma extensions plutôt que le schéma public par défaut. GoTrue appelait gen_random_uuid() sans qualificateur de schéma, attendant que PostgreSQL la trouve via le search path. Il ne la trouvait pas.

Ce que fait search_path

PostgreSQL résout les noms de fonctions et de tables non qualifiés en cherchant une séquence de schémas dans l'ordre. La valeur par défaut est "$user", public : chercher d'abord dans un schéma portant le nom de l'utilisateur de base de données actuel, puis dans public. Si une fonction vit dans un autre schéma et n'est pas explicitement qualifiée avec son nom de schéma – extensions.gen_random_uuid() au lieu de gen_random_uuid() – PostgreSQL retourne une erreur “does not exist” même si la fonction existe réellement.

Le déploiement géré de Supabase configure le search_path PostgreSQL pour inclure extensions, auth et public. Cette configuration est documentée dans la base de code de Supabase mais pas mise en évidence dans le guide de configuration auto-hébergé. Sur Supabase géré, elle est appliquée automatiquement. Sur Docker auto-hébergé, elle dépend des variables d'environnement et des scripts d'initialisation appliqués. Les nôtres n'en avaient ni l'un ni l'autre.

Pourquoi mobile et pas web

La question à laquelle l'explication des logs ne répond pas immédiatement : pourquoi l'authentification web fonctionnait-elle tandis que le mobile échouait ? La réponse se trouve dans les différences entre les flux OAuth selon les environnements.

Les applications web utilisant Supabase OAuth utilisent par défaut le flux implicite : le serveur d'autorisation retourne les tokens directement dans le fragment d'URL après le callback OAuth. Le client les extrait de l'URL, les stocke, et la session est établie sans étape séparée d'échange de tokens – un chemin de code qui n'appelait pas gen_random_uuid().

Les applications mobiles suivant les bonnes pratiques de sécurité actuelles utilisent PKCE : le Proof Key for Code Exchange. Avec PKCE, le client génère un code verifier aléatoire, le hache pour produire un code challenge, et envoie le challenge au serveur d'autorisation. Après le callback, le client envoie le code verifier original à l'endpoint de token, qui le vérifie avant d'émettre les tokens. Cette étape d'échange de tokens était le chemin de code qui échouait. GoTrue appelait gen_random_uuid() lors de la génération de l'état OAuth pour le flux PKCE, et le schéma extensions n'était pas dans le search path.

Le flux implicite sur web contournait ce chemin de code spécifique. PKCE sur mobile non. Une différence subtile dans l'implémentation OAuth a exposé une erreur de configuration qui existait dans le stack Docker depuis le déploiement – elle n'avait simplement pas été exercée jusqu'à ce qu'un client mobile tente un flux PKCE.

Le correctif

Le correctif approprié est de s'assurer que le search_path PostgreSQL inclut tous les schémas dont GoTrue et PostgREST dépendent. Dans une configuration Docker Compose, c'est plus propre appliqué au niveau de la base de données pour qu'il persiste quel que soit le conteneur qui se connecte :

-- Exécuter une fois contre la base de données
ALTER DATABASE postgres SET search_path TO extensions, auth, public, storage;
ALTER ROLE authenticator SET search_path TO extensions, auth, public, storage;
ALTER ROLE supabase_auth_admin SET search_path TO extensions, auth, public, storage;

Une alternative est de passer le paramètre via la configuration du service PostgreSQL dans Docker Compose :

services:
  db:
    image: supabase/postgres:15.1.0.117
    command:
      - postgres
      - -c
      - search_path=extensions,auth,public,storage

Nous avons appliqué les deux. Après le redémarrage du conteneur GoTrue, OAuth mobile s'est complété sans erreur. Apple Sign-In – que nous avions hésité à tester pendant le processus de débogage – a également fonctionné immédiatement, confirmant que le problème était spécifique au flux et non au fournisseur.

Ce que cet incident révèle sur le débogage auto-hébergé

Le chemin de débogage – de “auth cassée sur mobile” à “mauvais search_path dans PostgreSQL” – a pris plus de temps qu'il n'aurait dû. Plusieurs leçons méritent d'être tirées.

Les logs de conteneurs sont la vérité terrain. Les deux hypothèses incorrectes ont été poursuivies avant de lire les logs GoTrue. Les logs contenaient l'erreur exacte dès la première entrée pertinente. Dans un stack Docker auto-hébergé, lire les logs est la première étape, pas un dernier recours après avoir épuisé les autres théories.

Les défaillances spécifiques à une plateforme dans un backend partagé indiquent presque toujours une différence dans le chemin de code client, pas une différence dans la façon dont le backend traite ce client spécifique. Le backend ne sait pas qu'il sert une application mobile plutôt qu'un navigateur. Il suit le chemin de code déterminé par les paramètres de la requête. Comprendre quel chemin de code PKCE active – par opposition au flux implicite – aurait pointé immédiatement vers l'échange de tokens.

Les services gérés et les services auto-hébergés ne sont pas des configurations équivalentes. Supabase Cloud applique des détails de configuration qui sont documentés mais pas automatiquement appliqués dans la configuration Docker Compose. Quand quelque chose fonctionne sur Supabase Cloud mais échoue en auto-hébergé, l'écart est presque toujours dans l'une de ces configurations. Le tracker de problèmes du dépôt officiel auto-hébergé est souvent le moyen le plus rapide de localiser le paramètre manquant.

Un bug qui “n'affecte qu'une plateforme” est souvent un bug qui a toujours été présent, exposé seulement par le premier client à exercer un chemin de code spécifique. Dans ce cas, l'erreur de search_path existait depuis le jour où le stack Docker a été déployé. Il a fallu des mois et une implémentation OAuth mobile pour la rendre visible.

Ressources connexes

Articles connexes