% ============================================================================= % CHAPITRE : MÉTHODE 3 — QR CODE + TOKEN MOBILE % ============================================================================= \chapter{Méthode 3 : QR Code + token mobile (pour \texttt{pronote-sync})} \label{ch:method3} \section{Principe général} Mécanisme d'appairage par QR code pour les appareils mobiles, \textbf{contournant l'authentification ENT/EduConnect}. Le QR code est généré \textbf{depuis l'interface web Pronote, espace parent \rightarrow paramètres \rightarrow QR code}, puis utilisé avec un \textbf{PIN à 4 chiffres} pour obtenir un token dont la durée dépend de l'instance/serveur. \begin{infobox} \textbf{Statut} : {\emojicheck\ Pris en charge} par \texttt{pronote-sync} (mode \texttt{PRONOTE\_AUTH\_MODE=qr\_token}). \end{infobox} \section{Procédure QR pour \texttt{pronote-sync}} \label{sec:procedure-qr} \subsection{Étape 1 : Génération du QR code (interface web)} \begin{enumerate} \item Se connecter à Pronote via un navigateur (espace \textbf{Parent}). \item Aller dans \textbf{Paramètres \rightarrow Accès mobile / Application mobile}. \item Définir un \textbf{PIN temporaire à 4 chiffres}. \item Pronote affiche un \textbf{QR code} contenant un JSON avec les clés : \begin{itemize} \item \texttt{login}, \texttt{jeton}, et \texttt{url} (ex. \texttt{https://[host]/pronote/mobile.parent.html}). \end{itemize} \textbf{Le fichier JSON réel contient des identifiants chiffrés et ne doit jamais être copié, partagé, ou committé.} \item \textbf{Exporter le QR code} : \begin{itemize} \item Sauvegardez le fichier JSON localement ou scannez-le avec un appareil. \item \textbf{Ne jamais partager} le JSON ou le PIN. \item \textbf{Le fichier JSON du QR code contient des identifiants chiffrés : ne jamais le coller dans la documentation ni le committer.} \end{itemize} \end{enumerate} \begin{infobox} \textbf{Source} : {\emojiinfo\ Observé localement dans l'interface web Pronote} (version non spécifiée, hypothèse à valider). \end{infobox} \subsection{Étape 2 : Configuration de \texttt{pronote-sync}} \begin{enumerate} \item \textbf{Enregistrer le QR code} : \begin{itemize} \item Sauvegarder le JSON dans un fichier (ex. \texttt{/path/to/qr\_code.json}). \item \textbf{Permissions} : \texttt{chmod 600 /path/to/qr\_code.json}. \end{itemize} \item \textbf{Configurer \texttt{.env}} : \begin{lstlisting}[language=bash, caption=Configuration QR Code dans .env, label=lst:qr-env] PRONOTE_AUTH_MODE=qr_token PRONOTE_QR_CODE_FILE=/path/to/qr_code.json PRONOTE_QR_PIN= # renseigner localement la valeur secrète du PIN (jamais committée) \end{lstlisting} \textbf{Note} : \texttt{.env.example} ne doit \textbf{jamais} contenir de PIN concret. \end{enumerate} \begin{infobox} \textbf{Source} : {\emojiinfo\ Observé dans \texttt{.env.example}} (lignes 21–25) et \texttt{pronote\_sync/sources/pronote/client.py}. \end{infobox} \subsection{Étape 3 : Premier login (enrôlement)} \begin{enumerate} \item \texttt{pronote-sync} lit \texttt{PRONOTE\_QR\_CODE\_FILE} et \texttt{PRONOTE\_QR\_PIN}. \item Appel à \texttt{pronotepy.ParentClient.qrcode\_login(qr\_code, pin, uuid)} : \begin{itemize} \item \texttt{qr\_code} : JSON du fichier QR. \item \texttt{pin} : PIN à 4 chiffres. \item \texttt{uuid} : UUID permanent généré par \texttt{pronote-sync} (ex. \texttt{pronote-sync-\{uuid4()\}}). \end{itemize} \item \textbf{Déchiffrement} : \begin{itemize} \item Algorithme : \textbf{AES-CBC utilisant une clé dérivée par MD5 (16 octets, soit AES-128)}. \item Clé : \texttt{MD5(PIN)} (dérivée du PIN secret). \item IV : 16 octets nuls. \item Résultat : \texttt{login} et \texttt{jeton} en clair. \end{itemize} \item \textbf{Échange de token} : Pronote retourne un \texttt{jetonConnexionAppliMobile} (token dont la durée dépend de l'instance/serveur). \item \textbf{Persistance} : \texttt{pronote-sync} sauvegarde les credentials via \texttt{export\_credentials()} dans \texttt{.pronote\_auth\_state.json} (mode \texttt{0600}). \end{enumerate} \begin{infobox} \textbf{Source} : {\emojiinfo\ Observé dans le code local de \texttt{pronotepy} 2.15.7} (\texttt{clients.py} lignes 181–189) + \texttt{pronote\_sync/sources/pronote/client.py} (méthode \texttt{\_enroll\_qr\_code}). \end{infobox} \subsection{Étape 4 : Connexions ultérieures (auto-login)} \begin{enumerate} \item \texttt{pronote-sync} charge \texttt{.pronote\_auth\_state.json}. \item Appel à \texttt{pronotepy.ParentClient.token\_login(**credentials)} : \begin{itemize} \item \texttt{pronote\_url}, \texttt{username}, \texttt{password} (token), \texttt{uuid}. \end{itemize} \item \textbf{Rotation du token} : Le token est \textbf{remplacé uniquement si le serveur renvoie \texttt{jetonConnexionAppliMobile}} (observé dans \texttt{pronotepy} 2.15.7, \texttt{clients.py:382–387}). \item \textbf{Persistance} : Les credentials sont sauvegardés dans \texttt{.pronote\_auth\_state.json} après chaque opération réussie. \end{enumerate} \begin{infobox} \textbf{Source} : {\emojiinfo\ Observé dans le code local de \texttt{pronotepy} 2.15.7} (\texttt{clients.py} lignes 245–280) + \texttt{pronote\_sync/sources/pronote/client.py} (méthode \texttt{\_connect\_qr\_token}). \end{infobox} \section{Cycle de vie des tokens} \begin{itemize} \item \textbf{QR code} : Valide \textbf{~10 minutes} après génération ({\warningicon\ \textbf{Hypothèse à valider}} : cette durée n'est attestée que par un message d'exception dans \texttt{pronotepy} et n'est pas une garantie officielle/indépendante de l'instance). \item \textbf{\texttt{jetonConnexionAppliMobile}} : \begin{itemize} \item Durée \textbf{dépendante de l'instance/serveur} (observation du 2026-09-12, hypothèse : peut persister jusqu'à la fin de l'année scolaire, non garanti). \item \textbf{Remplacé uniquement si le serveur renvoie \texttt{jetonConnexionAppliMobile}} (observé dans \texttt{pronotepy} 2.15.7, \texttt{clients.py:382–387}). \item \textbf{Révocable} manuellement dans Pronote (Paramètres \rightarrow Accès mobile). \end{itemize} \end{itemize} \begin{infobox} \textbf{Source} : {\warningicon\ Comportement variable selon les instances} (à tester localement). \end{infobox} \section{Sécurité} \begin{enumerate} \item \textbf{Fichiers sensibles} : \begin{itemize} \item \texttt{.pronote\_auth\_state.json} : \textbf{Ne jamais versionner} (couvert par \texttt{.gitignore}). \item \texttt{PRONOTE\_QR\_CODE\_FILE} : \textbf{Ne jamais committer} (ex. dans Git). \end{itemize} \item \textbf{Secrets} : \begin{itemize} \item Le PIN et le contenu du QR code sont \textbf{masqués} dans les logs (via \texttt{redact\_secrets()}). \item Les exceptions sont \textbf{expurgées} (via \texttt{redact\_exception()}). \end{itemize} \item \textbf{Recommandations} : \begin{itemize} \item Utiliser un \textbf{PIN robuste} (éviter les codes simples comme \guillemotleft 0000 \guillemotright ou des séquences évidentes). \item \textbf{Révoquer} le token en cas de compromission (via Pronote web). \end{itemize} \end{enumerate} \begin{infobox} \textbf{Source} : {\emojiinfo\ \texttt{pronote\_sync/utils/redaction.py} + \texttt{pronote\_sync/sources/pronote/client.py}} (méthode \texttt{\_collect\_auth\_secrets}). \end{infobox} \section{Erreurs et repli} \begin{itemize} \item \textbf{\texttt{PronoteAuthRotationError}} : Levée si : \begin{itemize} \item Le token persisté est \textbf{invalide/expiré}. \item Le fichier QR ou le PIN est \textbf{manquant/invalide}. \end{itemize} \item \textbf{Action requise} : \begin{enumerate} \item Supprimer \texttt{.pronote\_auth\_state.json}. \item Générer un \textbf{nouveau QR code depuis l'interface web Pronote, espace parent}. \item Relancer \texttt{pronote-sync}. \end{enumerate} \end{itemize} \begin{infobox} \textbf{Source} : {\emojiinfo\ Observé dans \texttt{pronote\_sync/errors.py} + \texttt{pronote\_sync/sources/pronote/client.py}} (lignes 346–364). \end{infobox} \section{Incompatibilités} \begin{itemize} \item \textbf{Mode \texttt{dry-run}} : \textbf{Incompatible} avec \texttt{PRONOTE\_AUTH\_MODE=qr\_token} (risque de désynchronisation du token local). \begin{itemize} \item \texttt{pronote-sync --dry-run} \textbf{refuse} le mode \texttt{qr\_token} avant toute connexion. \end{itemize} \end{itemize} \begin{infobox} \textbf{Source} : {\emojiinfo\ \texttt{docs/exploitation.md}} (ligne 65–66). \end{infobox} % ============================================================================= % FIN DU CHAPITRE % =============================================================================