Identifier le joueur sans mot de passe
Connecter chaque joueur de façon anonyme avec Supabase Auth, sans e-mail ni mot de passe.
Objectifs de la séance
- comprendre à quoi sert l'authentification et ce qu'est une connexion anonyme ;
- distinguer les rôles
anonetauthenticated; - comprendre comment la session du joueur est mémorisée et rafraîchie ;
- écrire le service
authService.tsen évitant de créer plusieurs utilisateurs pour un même joueur ; - afficher l'identifiant du joueur sur l'écran d'accueil.
Petite vérification
Avant de commencer, vérifiez si vous savez...
- dans le tableau de bord Supabase (menu Storage), ouvrir le bucket
images-questionset compter les fichiers qu'il contient - lancer l'application, créer un quiz de 3 questions (thème « Mots de passe », niveau « Débutant ») et repérer la question qui affiche une illustration
- citer la méthode du client Supabase utilisée dans
imagesService.tspour obtenir l'adresse publique d'une image
📌 Réponse attendue
Notions théoriques
Pourquoi identifier le joueur ?
Jusqu'ici, l'application est anonyme au sens strict : le serveur ne sait pas qui joue. Or nous voulons bientôt enregistrer les parties de chaque joueur, calculer son score et afficher un classement. Il faut donc que le serveur sache, à chaque requête, quel joueur la demande. Cette reconnaissance s'appelle l'authentification.
La solution classique (e-mail + mot de passe) demande un formulaire d'inscription, une adresse e-mail valide, un mot de passe à retenir... C'est lourd pour un petit quiz. Supabase propose une alternative : la connexion anonyme.
L'authentification anonyme
Avec la connexion anonyme, le joueur n'a rien à saisir. L'application demande à Supabase Auth de
créer un utilisateur sans e-mail ni mot de passe, identifié seulement par un uuid (un long
identifiant unique, par exemple 3f2b8c1e-...). Cet utilisateur est stocké dans le schéma auth
de la base (table auth.users), que l'application ne peut pas lire directement.
Application Supabase Auth
| signInAnonymously() ------------------> crée un utilisateur (uuid)
| <---- jeton d'accès (JWT) + jeton de rafraîchissement
|
| (le jeton est mémorisé dans localStorage)
|
| requête SQL + jeton -------------------> auth.uid() = uuid du joueur
Pour des raisons de sécurité, la connexion anonyme est désactivée par défaut sur un projet Supabase.
Vous l'activerez dans le TP : Authentication > Sign In / Providers > Allow anonymous sign-ins.
Tant qu'elle est désactivée, l'appel signInAnonymously() échoue.
Les rôles anon et authenticated
Chaque requête envoyée à la base de données s'exécute avec un rôle PostgreSQL :
| Rôle | Quand ? |
|---|---|
anon | requête envoyée avec la clé publishable seule (aucune session) |
authenticated | requête envoyée avec le jeton d'un utilisateur connecté, y compris un utilisateur anonyme |
Les droits (grant) et les règles RLS (policy ... to anon, authenticated) que vous avez déjà écrits
au tutoriel 6 s'appliquent en fonction de ce rôle.
Un joueur connecté anonymement possède le rôle authenticated, comme un utilisateur qui s'est
inscrit avec un e-mail. Une règle écrite to authenticated s'applique donc aussi aux anonymes. Si
une règle devait un jour être réservée aux comptes « permanents », il faudrait lire dans le jeton
l'indicateur is_anonymous. Dans notre application, cela n'a pas d'importance : tous les joueurs sont anonymes.
La session et le jeton
Après la connexion, Supabase renvoie une session qui contient :
- un jeton d'accès (un JWT, JSON Web Token) : une preuve d'identité à durée de vie courte (une heure par défaut), signée par Supabase, qui contient l'uuid du joueur ;
- un jeton de rafraîchissement : il permet d'obtenir un nouveau jeton d'accès sans que le joueur ait quoi que ce soit à faire.
Le client Supabase joint automatiquement le jeton à chaque requête. Côté base de données, la
fonction auth.uid() renvoie l'uuid contenu dans ce jeton : c'est ce qui permettra d'écrire des règles
du type « chacun ne voit que ses lignes ».
La session est mémorisée dans le localStorage de l'appareil (c'est la configuration faite dans
src/lib/supabase.ts au tutoriel 6). Le joueur est donc reconnu au lancement suivant.
- Si le joueur efface les données de son navigateur, désinstalle l'application ou change de téléphone, sa session est perdue : il redevient un nouveau joueur (et l'ancien reste dans la base, orphelin).
- Un utilisateur anonyme peut être transformé plus tard en compte permanent (ajout d'un e-mail), mais nous ne le ferons pas dans ce cours.
- Chaque connexion anonyme crée un utilisateur : en production, on limite ces créations (par adresse IP) et on peut ajouter un CAPTCHA pour décourager les robots. C'est un réglage du tableau de bord Supabase.
Ne créer qu'un seul utilisateur : la promesse partagée
Imaginez que deux écrans demandent la session en même temps au lancement de l'application. Sans précaution, chacun lancerait sa propre connexion anonyme : deux utilisateurs seraient créés pour un seul joueur, et les données de l'un seraient perdues quand la seconde session écraserait la première.
La parade : une variable connexionEnCours qui garde la promesse de la connexion lancée. Tous les
appels suivants reçoivent la même promesse, au lieu d'en lancer une nouvelle.
appel 1 : connexionEnCours est vide -> on lance la connexion et on garde sa promesse
appel 2 : connexionEnCours est remplie -> on renvoie la MÊME promesse (aucune connexion en plus)
échec : on vide connexionEnCours -> un nouvel essai sera possible
Rafraîchir le jeton seulement au premier plan
Sur téléphone, le jeton d'accès doit être renouvelé régulièrement. Tant que l'application est visible,
c'est utile ; en arrière-plan, c'est du réseau et de la batterie gaspillés. React Native fournit
AppState qui signale quand l'application passe au premier plan ("active") ou en arrière-plan. Le client
Supabase propose startAutoRefresh() et stopAutoRefresh() pour suivre ce rythme. Sur le Web, cette
précaution n'est pas nécessaire.
TP pour réfléchir et résoudre des problèmes
Votre mission : connecter chaque joueur anonymement et afficher son numéro sur l'écran d'accueil. Rappel : dans cette série, toute fonctionnalité se développe sur une branche Git dédiée.
Étape 1 — Créer la branche du tutoriel et activer la connexion anonyme
Dans le terminal de VS Code (dossier quiz-cyber-expo), vérifiez que vous êtes à jour sur main, puis
créez la branche du tutoriel 8 :
git switch main
git pull
git switch -c feature/securite
Ensuite, dans le tableau de bord Supabase : Authentication > Sign In / Providers, activez Allow anonymous sign-ins, puis enregistrez.
Tout le travail du tutoriel 8 (connexion, pseudo, fonctions SQL...) se fera sur feature/securite. La branche
main reste ainsi toujours dans un état qui fonctionne, et vous fusionnerez le tout par une Pull Request à la fin du tutoriel.
Étape 2 — Ouvrir la session du joueur
Créez le fichier src/services/authService.ts. La fonction ouvrirSession fait deux choses : elle
regarde d'abord si une session est déjà mémorisée sur l'appareil, sinon elle lance la connexion anonyme.
Complétez la ligne qui lance la connexion anonyme :
Le message technique de Supabase part dans console.warn (utile pour vous), mais l'exception levée
contient un texte simple pour le joueur. Un utilisateur n'a pas à lire « Anonymous sign-ins are disabled ».
Étape 3 — Partager la promesse de connexion
Toujours dans authService.ts, ajoutez au-dessus de ouvrirSession la variable connexionEnCours, puis
écrivez la fonction exportée assurerSessionAnonyme. En cas d'échec, la promesse doit être oubliée pour
qu'un nouvel essai soit possible. Complétez la ligne qui l'oublie :
// Promesse de connexion partagée : si plusieurs écrans demandent la session
// en même temps, UNE SEULE connexion anonyme est lancée (sinon on créerait
// plusieurs utilisateurs anonymes pour le même joueur).
let connexionEnCours: Promise<string> | null = null;
Si on gardait une promesse rejetée dans connexionEnCours, tous les appels suivants recevraient la même
erreur, pour toujours : le bouton « Réessayer » ne servirait à rien. Pensez toujours à l'après-échec quand vous
mémorisez un résultat.
Étape 4 — Rafraîchir la session seulement au premier plan
Ouvrez src/lib/supabase.ts. Modifiez l'import de react-native pour ajouter AppState, puis ajoutez à
la fin du fichier le bloc ci-dessous. Complétez la ligne qui démarre le rafraîchissement quand
l'application redevient active :
Un téléphone a une batterie et un forfait de données limités. Une application qui travaille en arrière-plan sans nécessité est vite désinstallée. Arrêter ce qui n'est pas utile est une marque de qualité.
Étape 5 — Un hook pour utiliser la connexion dans un écran
Créez src/hooks/useSessionAnonyme.ts. Ce hook reprend le patron du tutoriel 7 : l'état est un
EtatChargement<string> (chargement, erreur ou succès) et la fonction reessayer relance l'essai.
La connexion ne peut pas être annulée comme une requête : on utilise donc un simple drapeau estAnnule.
Complétez la ligne qui demande la session :
Si le joueur quitte l'écran pendant la connexion, l'effet est nettoyé (estAnnule = true). Le test
if (!estAnnule) évite alors de modifier l'état d'un composant qui n'existe plus.
Étape 6 — Afficher le numéro du joueur sur l'accueil
Dans src/app/index.tsx, appelez le hook, puis affichez selon l'état : « Connexion... » pendant le
chargement, un message d'erreur avec un bouton « Réessayer » en cas d'échec, et en cas de succès le début
de l'identifiant puis le bouton « Créer un quiz ». Complétez la ligne qui affiche les 8 premiers
caractères de l'identifiant :
Un uuid complet n'a aucun intérêt pour le joueur. Ses 8 premiers caractères suffisent pour vérifier que la session est la même d'un lancement à l'autre, sans exposer inutilement l'identifiant.
Étape 7 — Vérifier le comportement
- Lancez
npx expo startet ouvrez la version Web (touchew). L'accueil affiche « Joueur anonyme n° » suivi de 8 caractères. Notez-les. - Rechargez la page (
F5) : les 8 caractères sont identiques (la session mémorisée est réutilisée). - Dans le tableau de bord Supabase, ouvrez Authentication > Users : un seul utilisateur apparaît, marqué comme anonyme (son identifiant commence par vos 8 caractères).
- Dans le navigateur, ouvrez les outils de développement (
F12) > Application > Stockage local et supprimez les données du site. Rechargez : un nouveau numéro apparaît et un deuxième utilisateur est créé.
Recharger la page, effacer les données, couper le réseau : ce sont ces scénarios qui révèlent les bugs de session. Vérifier que la deuxième visite se comporte bien est aussi important que la première.
Étape 8 — Enregistrer votre travail
git add -A
git status
git commit -m "Connexion anonyme du joueur avec Supabase Auth"
git push -u origin feature/securite
L'option -u relie votre branche locale à la branche du même nom sur GitHub. Les prochains git push de cette
branche s'écriront simplement git push.
📌 Une solution
Test de mémorisation/compréhension
Exercices complémentaires
Exercice A : observer le jeton
Dans les outils de développement (F12 > Application > Stockage local), repérez la clé qui contient
la session Supabase. Copiez la valeur de access_token sur le site jwt.io et retrouvez dans le contenu le champ
role et le champ is_anonymous.
Exercice B : provoquer l'échec
Désactivez temporairement Allow anonymous sign-ins dans le tableau de bord, supprimez les données du site puis rechargez l'application. Quel message s'affiche à l'écran ? Quel message technique apparaît dans la console ? Réactivez ensuite le réglage et vérifiez que le bouton « Réessayer » fonctionne.
Ce qu'il faut retenir
| Notion | Résumé |
|---|---|
| Connexion anonyme | Supabase crée un utilisateur (uuid) sans e-mail ni mot de passe ; réglage à activer dans le tableau de bord. |
| Rôles | anon = clé seule, authenticated = utilisateur connecté (y compris anonyme). |
| Session | Jeton d'accès (JWT) de courte durée + jeton de rafraîchissement, mémorisés dans le localStorage. |
auth.uid() | Fonction SQL qui renvoie l'uuid du joueur contenu dans le jeton. |
| Promesse partagée | connexionEnCours évite de créer plusieurs utilisateurs pour un seul joueur. |
AppState | Le rafraîchissement du jeton ne tourne qu'au premier plan sur téléphone. |
Aperçu de la prochaine séance
Votre joueur est maintenant identifié, mais il reste un simple numéro. Dans la prochaine séance, vous
allez lui permettre de choisir un pseudo grâce à un formulaire. Vous créerez la table joueur, écrite avec
des droits par colonne et des règles RLS « chacun ne modifie que sa propre fiche », et vous protégerez le
formulaire contre le double envoi.