BlockLucky Documentation officielle GitHub

RGPD

Cette page décrit l'application du règlement (UE) 2016/679 (RGPD) dans BlockLucky : qui est responsable, sur quelles bases les données sont traitées, combien de temps elles sont gardées, et surtout ce que le code fait réellement quand un citoyen exerce ses droits. Le détail traitement par traitement est dans le registre des traitements ; les questions propres à la chaîne sont dans Blockchain et données personnelles. Elle ne constitue pas un avis juridique.

Responsable et contacts

Rôle Entité Contact
Responsable de traitement Ville d'EtherBay, Direction des Systèmes d'Information dsi@etherbay.fr
Délégué à la protection des données DPO de la Ville dpo@etherbay.fr
Appui technique Équipe BlockLucky (Epitech Digital School)

Ces informations figurent aussi dans la politique de confidentialité, commune au web (/confidentialite) et à l'application mobile (écran Legal) et rédigée dans frontend-web/src/content/legal.ts, ainsi que dans l'export JSON (controller). La désignation effective du DPO et l'adresse dpo@etherbay.fr sont à confirmer par la Ville.

Données traitées

Catégorie Données Stockage
Identification Email, identifiant de compte, empreinte du mot de passe, prénom et nom facultatifs Table User
Vérification d'âge Photos du recto et du verso de la pièce d'identité, statut, dates d'envoi et de décision, motif d'un refus, agent qui a statué Tables IdentityDocument (images chiffrées) et User
Portefeuille Adresse, clé privée chiffrée, hash de la dotation de bienvenue Table User
Participation Tickets, hash de transaction, adresse payeuse, tours Tables Ticket, LotteryRound
Fidélité Solde et journal des points, récompenses et codes promo obtenus Tables User, PointTransaction, ClaimedReward
Parrainage Code de parrainage, parrain Table User
Sécurité Secret TOTP chiffré, empreintes des codes de secours, codes temporaires, compteurs, révocations Table User, Redis
Déclaratif Confirmation de majorité, exigée à la connexion web et à l'inscription mobile par mot de passe ; tranche d'âge (18-25, 26-35, 36-50, 50+), demandée par la connexion web seulement ; acceptation des CGU et du règlement, vérifiée mais non enregistrée Métadonnées de l'événement analytique USER_SIGNUP
Préférences Thème, langue, interrupteur général et catégories de notifications, quêtes accomplies Redis (user:prefs:*, user:quests:*)
Mesure d'audience Type d'événement, source UTM, plateforme, page, identifiant du compte s'il est connecté ; sur le web, seulement après accord Table Analytics

Aucune donnée bancaire n'est collectée. Le prénom et le nom ne sont saisis que si le citoyen les renseigne dans l'écran Données personnelles du mobile ; ils servent à l'affichage des paramètres et aux messages envoyés au support. La date de naissance, le numéro du document et, le cas échéant, l'adresse ne sont pas saisis, mais ils figurent sur les photos de la pièce d'identité jusqu'à la décision de la Mairie : ce sont alors des données d'identité civile, à protéger en conséquence. Le modèle complet est décrit dans Modèle de données.

Bases légales

Traitement Base légale retenue Commentaire
Compte et participation à la loterie Exécution du contrat (CGU et règlement de participation) Textes acceptés par une case obligatoire à la connexion web et à l'inscription mobile par mot de passe ; rédigés pour le scénario, à relire par un juriste
Programme EtherPoints et récompenses Exécution du contrat
Vérification d'âge par pièce d'identité Obligation légale (exclusion des mineurs des jeux d'argent), à confirmer Voir Mineurs
Sécurité des comptes (mot de passe, 2FA, limites, révocation) Intérêt légitime
Mesure d'audience du web Consentement Bandeau au premier passage ; aucun événement ni source UTM enregistré avant l'accord
Événements de connexion et d'inscription enregistrés par le serveur Intérêt légitime USER_LOGIN et USER_SIGNUP, hors du champ du bandeau
Journal d'administration Obligation légale et intérêt légitime
Emails transactionnels Exécution du contrat et intérêt légitime Codes, alertes, bienvenue, confirmation RGPD, codes promo
Messages au support Exécution du contrat, à confirmer Objet, message, identifiant, email, prénom et nom transmis à l'adresse SUPPORT_EMAIL ; aucune copie en base

Le choix de l'exécution du contrat pour un service public communal, plutôt que la mission d'intérêt public, est à confirmer par le DPO (voir Points à valider).

Durées de conservation

Donnée Durée annoncée Ce que fait le code
Code de connexion, d'inscription, de réinitialisation ou de changement d'email 10 minutes Fait : TTL Redis
Photos de la pièce d'identité Jusqu'à la décision de la Mairie Fait : effacées dans la transaction de la décision ; aucune limite pour une demande jamais traitée
Décision sur l'âge (statut, date, motif d'un refus) Durée du compte Conservée sur User et dans le journal d'administration (IDENTITY_VERIFICATION_DECISION, avec le motif)
Compteur d'échecs 1 heure Fait : TTL Redis
Révocation d'une session Durée restante du jeton Fait : TTL Redis
Marqueur anti-rejeu TOTP 2 minutes Fait : TTL Redis
Jeton de suppression RGPD 24 heures Fait pour la validité ; la ligne expirée n'est pas purgée de la table Token
Réponses idempotentes 24 heures Fait : TTL Redis
Compte et participation Durée du pilote, puis anonymisation au plus tard six mois après le bilan (politique de confidentialité) Non implémenté : aucune purge automatique
Mesure d'audience 13 mois au plus (registre) Non implémenté
Journal d'administration 1 an (registre) Non implémenté
Préférences dans Redis Durée du compte Sans expiration ; non supprimées à l'anonymisation

Attention

Les durées longues ne sont aujourd'hui que des engagements écrits. Avant une exploitation réelle, il faut un traitement périodique (une tâche BullMQ planifiée, par exemple) qui anonymise les comptes inactifs, supprime les événements analytiques de plus de 13 mois, les entrées du journal d'administration de plus d'un an et les jetons expirés.

Droits des personnes

Accès et portabilité

GET /api/user/gdpr/export (authentifié) renvoie un document JSON construit par exportGdprData de backend/src/api/user/user.services.ts. Le web le propose depuis Profil, puis Données personnelles (frontend-web/src/app/(citoyen)/profil/rgpd/page.tsx) ; le mobile depuis Paramètres, puis Données personnelles, bouton « Télécharger mes données » (mobile-app/src/screens/settings/PersonalDataScreen.tsx).

Clé de l'export Contenu
exportedAt, controller Date et responsable de traitement
personalData Identifiant, adresse du portefeuille, email, prénom, nom, rôle, solde de points, code de parrainage, état de la 2FA, statut et date de la vérification d'âge, dates de création et de mise à jour
ticketsHistory Tickets avec leur tour
pointsLedger Journal complet des points
claimedRewards Récompenses, offre et commerçant
analyticsEvents Événements analytiques rattachés au compte

L'export ne contient ni la clé privée chiffrée, ni l'empreinte du mot de passe, ni le secret TOTP, ni les empreintes des codes de secours. Il omet aussi des données qui devraient y figurer : préférences et quêtes stockées dans Redis, parrain et liste des filleuls, motif d'un refus de la pièce d'identité et dates de la demande. Les photos de la pièce n'y figurent pas ; elles sont de toute façon effacées dès la décision.

Effacement

sequenceDiagram
    participant C as Citoyen
    participant A as API
    participant DB as PostgreSQL
    participant M as Email
    C->>A: "POST /api/user/gdpr/delete-request"
    A->>DB: "Token ACCOUNT_DELETION, SHA-256 du jeton, expire dans 24 h"
    A->>M: "Lien CONFIRM_URL et jeton TOKEN"
    C->>A: "POST /api/user/gdpr/confirm-delete (token)"
    A->>DB: "Transaction : anonymisation du compte, suppression du jeton"
    A->>A: "revokeAllForUser"
    A-->>C: "deleted, anonymized"

Le jeton de 32 octets aléatoires est stocké haché en SHA-256 ; seul l'email le contient en clair. Hors production, la réponse de delete-request contient aussi confirmationToken, pour faciliter les tests.

L'anonymisation, faite par anonymizeUserAccount de backend/src/api/user/user.repo.ts, modifie la ligne User :

Champ Après anonymisation
walletAddress 0xdeleted_gdpr_<8 premiers caractères de l'identifiant>
walletEncryptedKey null : la clé privée est détruite
welcomeGrantTxHash null
email null
passwordHash null
firstName, lastName null
ageVerificationNote null ; le statut et la date de vérification sont conservés
twoFactorSecret, twoFactorMethod, twoFactorBackupCodes, isTwoFactorEnabled Effacés
deletedAt Date de la suppression

La même transaction supprime la ligne IdentityDocument du compte, avec ses photos si la demande était encore en attente.

Ce qui n'est pas effacé :

Donnée Situation
Ligne User Conservée (suppression logique), avec id, role, pointsBalance, referralCode, referredById
Tickets Conservés avec transactionHash, payerWallet et userId : le lien entre l'identifiant du compte et l'ancienne adresse du portefeuille subsiste par ces colonnes
Journal des points, récompenses et codes promo Conservés, rattachés à l'identifiant
Événements analytiques Conservés avec userId et leurs métadonnées, y compris le motif d'un refus de pièce d'identité dans le journal d'administration
Données Redis Préférences et quêtes non supprimées
Entreprises partenaires Restent rattachées au compte (ownerId), puisque la ligne User n'est pas supprimée
Transactions sur la chaîne Impossibles à effacer (voir Blockchain et données personnelles)

Une fois l'email supprimé, ces données restantes ne permettent plus d'identifier directement la personne à partir de la base. Elles restent cependant pseudonymes, et non anonymes au sens strict : un tiers qui connaîtrait l'adresse du portefeuille du citoyen pourrait la retrouver dans Ticket.payerWallet. Le parcours est détaillé dans Suppression RGPD.

Attention

Le lien envoyé par email pointe vers ${FRONTEND_URL}/gdpr/confirm-delete?token=..., mais le frontend web ne contient aucune page à ce chemin : le lien aboutit à une page 404. La confirmation fonctionne en collant le jeton, également présent dans l'email, dans la page Profil, puis Données personnelles. Par ailleurs, placer un jeton dans une URL l'expose dans l'historique du navigateur et les journaux des proxys.

Rectification

Le citoyen peut modifier son thème, sa langue et ses notifications. Sur le mobile, il peut aussi rectifier lui-même :

Donnée Écran Route
Prénom et nom (50 caractères au plus ; un champ vide efface la valeur) Paramètres, puis Données personnelles PATCH /api/user/profile
Adresse email, confirmée par un code envoyé à la nouvelle adresse ; l'ancienne reçoit une alerte Paramètres, puis Sécurité POST /api/auth/email/change, POST /api/auth/email/confirm

Le web ne propose aucun de ces deux écrans. Les autres rectifications passent par une demande au DPO, avec une réponse sous un mois.

Consentement à la mesure d'audience

Sur le web, la mesure ne fonctionne qu'après un accord donné dans le bandeau de consentement. Le choix est enregistré dans bl.consent.choice du localStorage, avec sa date, pour 182 jours ; il se modifie à tout moment par « Gérer mes traceurs » (pied de page, /traceurs, /confidentialite). Tant que l'accord manque, trackEvent de frontend-web/src/lib/analytics.ts n'envoie aucun événement et la source UTM n'est pas enregistrée ; un refus efface celle déjà enregistrée. Le choix est propre à l'appareil et au navigateur. Il ne couvre pas les événements USER_LOGIN et USER_SIGNUP, enregistrés par le serveur à chaque connexion et inscription. L'application mobile n'envoie pas d'événement de parcours. Voir Analytique et consentement.

Délai de réponse

Les droits d'accès, de portabilité et d'effacement sont en libre-service et immédiats. Les autres demandes sont traitées par le DPO dans le délai d'un mois prévu par le RGPD.

Mineurs

La participation est réservée aux majeurs. Deux mécanismes coexistent :

Mécanisme Où Portée
Déclaration de majorité (isAdultConfirmed: true) Connexion web POST /api/auth/login et inscription mobile POST /api/auth/signup Déclarative ; la connexion Google ne la demande pas
Vérification d'âge par pièce d'identité POST /api/user/identity, décision d'un agent de la Mairie Exigée pour acheter ou offrir un ticket, échanger une récompense et acheter un ticket à gratter (AGE_VERIFICATION_REQUIRED, obligatoire en production)

Le citoyen envoie depuis /profil/identite sur le web, ou depuis l'application mobile, une photo du recto et du verso, après avoir coché sur le web une case de consentement à la consultation par la Mairie. Les images sont chiffrées à la réception, consultées par un agent sur la page Identités de l'espace Mairie, puis effacées à la décision ; le citoyen reçoit la décision par email, avec le motif en cas de refus, et peut envoyer une nouvelle pièce. Chaque consultation et chaque décision sont journalisées. Le détail technique est dans Autorisations.

Le contrôle a des limites à faire connaître :

  • l'agent juge sur photo : aucun contrôle ne vérifie que la pièce est authentique ni qu'elle appartient à la personne qui joue ;
  • l'API ne connaît pas la date de naissance : la décision est entièrement humaine et n'est pas réexaminée ;
  • un achat direct sur le contrat, sans passer par BlockLucky, échappe au contrôle ;

La collecte d'une copie de pièce d'identité est un traitement sensible pour la CNIL, qui recommande de la limiter au strict nécessaire et de privilégier des solutions de vérification d'âge qui ne transmettent pas l'identité au service. La proportionnalité de ce choix et la nécessité d'une analyse d'impact sont à valider (voir Points à valider).

Important

Le cadeau d'un ticket (POST /api/lottery/gift) crée un compte pour l'email du bénéficiaire s'il n'en a pas, avec un portefeuille, sans que ce bénéficiaire ait vérifié sa majorité ni accepté les conditions, et en le rattachant à l'expéditeur comme filleul. Le contrôle d'âge ne porte que sur l'expéditeur : le bénéficiaire détient un ticket sans avoir fait vérifier son âge. C'est un traitement de données d'un tiers sans démarche de sa part. La base légale de ce traitement et l'information du bénéficiaire sont à valider (voir Points à valider).

Sous-traitants et transferts

Sous-traitant Usage Localisation Contrat (article 28)
Hébergeur de l'API et de la base Hébergement À désigner, UE exigée À signer
Fournisseur SMTP Envoi des emails À désigner, UE exigée À signer
Stockage objet S3 Exports comptables et visuels AWS_REGION, eu-west-3 (Paris) par défaut À signer
Fournisseur RPC Ethereum Lecture et écriture sur la chaîne À désigner ; le RPC de secours par défaut est un service public (FALLBACK_RPC_URL) À signer
Chainlink Aléa du tirage Réseau décentralisé Ne reçoit aucune donnée personnelle

Le fournisseur RPC voit passer les adresses de portefeuille et l'adresse IP du serveur, pas l'email.

Sécurité du traitement

Les mesures de sécurité (article 32) sont décrites dans la partie Sécurité. En cas de violation de données, la procédure de notification à la CNIL sous 72 heures et d'information des personnes figure dans le plan de réponse aux incidents.

Ce qui reste à faire

Sujet État
Purges automatiques À implémenter
Export complet (Redis, parrainage) À compléter
Page web de confirmation de suppression À créer, ou lien à retirer de l'email
Effacement des données Redis à l'anonymisation À ajouter
Délai maximal pour une pièce d'identité jamais traitée À fixer et à implémenter
Information sur la vérification d'âge dans la politique de confidentialité Faite
Analyse d'impact (AIPD) Version de travail rédigée (AIPD), avis du DPO à recueillir
Contrats de sous-traitance À signer
Preuve du consentement Choix conservé sur l'appareil seulement ; aucune trace côté serveur, ni de l'acceptation des CGU et du règlement