docs(auth): rendre la référence Pronote vérifiable #45

Merged
OpenCode merged 3 commits from docs/issue-10-pronote-auth-reference into main 2026-09-12 21:18:05 +02:00
18 changed files with 1018 additions and 2 deletions
Showing only changes of commit 423a32f220 - Show all commits
+51
View File
@@ -0,0 +1,51 @@
% =============================================================================
% ANNEXE B : TABLEAU COMPARATIF SYNTHÉTIQUE
% =============================================================================
\chapter{Tableau comparatif synthétique}
\label{annex:comparison}
\begin{table}[htbp]
\centering
\caption{Comparatif des méthodes d'authentification}
\label{tab:comparison}
\begin{tabularx}{\textwidth}{L{2cm} L{2.5cm} L{2.5cm} L{2cm} L{2cm} L{2.5cm}}
\toprule
\textbf{Méthode} & \textbf{Périmètre} & \textbf{Identifiants} & \textbf{Expiration} & \textbf{Complexité} & \textbf{Statut dans \texttt{pronote-sync}} \\
\midrule
\textbf{URL iCal sécurisée} & EDT + devoirs (si inclus dans le flux) & Jeton URL & Longue durée (dépend de l'instance) & Très faible & \emojicheck\ Pris en charge \\
\textbf{Connexion directe} (mot de passe + ENT) & Agenda/EDT, devoirs, messages & Identifiant + mot de passe + ENT & Session dépendante de l'instance & Élevée & \emojicheck\ Pris en charge \\
\textbf{QR Code + token mobile} & Agenda/EDT, devoirs, messages & PIN 4 chiffres \rightarrow token + UUID & QR ~10 min ({\warningicon\ hypothèse}) ; token dépendant de l'instance & Modérée & \emojicheck\ Pris en charge \\
\textbf{SSO ENT (CAS/SAML)} && Identifiants ENT & Session ENT & Très élevée & \emojicross\ Non implémenté (seuls les 30 ENT de la liste fermée \texttt{\_ENT\_NAMES} sont supportés via \texttt{pronotepy}) \\
\textbf{EduConnect} && Identifiants nationaux & Session EduConnect & Très élevée & \emojicross\ Non implémenté \\
\textbf{CAS direct} && Identifiants CAS & Ticket single-use & Modérée & \emojicross\ Non implémenté \\
\textbf{API publique} & N/A & N/A & N/A & N/A & \emojicross\ Inexistante \\
\bottomrule
\end{tabularx}
\end{table}
\section*{Légende}
\addcontentsline{toc}{section}{Légende}
\begin{itemize}
\item \textbf{Périmètre} : Les méthodes \textbf{iCal} sont en lecture seule (EDT + devoirs si inclus
dans le flux). Les méthodes \textbf{Connexion directe} et \textbf{QR Code + token mobile}
permettent les opérations \texttt{pronotepy} effectivement implémentées par ce projet (agenda,
devoirs, messages ; informations ignorées en mode \texttt{qr\_token}).
\item \textbf{QR ~10 min} : {\warningicon\ \textbf{Hypothèse non vérifiée}} (observé dans
\texttt{pronotepy} via un message d'exception, dépend de l'instance).
\item \textbf{Session dépendante de l'instance} : {\warningicon\ \textbf{Hypothèse non vérifiée}}
(nécessite un essai réel).
\end{itemize}
% =============================================================================
% FIN DE L'ANNEXE
% =============================================================================
+62
View File
@@ -0,0 +1,62 @@
% =============================================================================
% ANNEXE C : ÉCARTS ET NOTES DE COHÉRENCE
% =============================================================================
\chapter{Écarts et notes de cohérence}
\label{annex:ecarts}
\section{Alignement avec \texttt{.env.example}}
\label{sec:alignement-env}
\begin{table}[htbp]
\centering
\caption{Alignement avec \texttt{.env.example}}
\label{tab:alignement-env}
\begin{tabularx}{\textwidth}{L{4.2cm} L{3.2cm} L{4.4cm} L{2.2cm}}
\toprule
\textbf{Paramètre} & \textbf{Document} & \textbf{Code} & \textbf{Statut} \\
\midrule
\texttt{PRONOTE\_ICAL\_URL} & \emojicheck\ Lignes 2, 42--50 & \emojicheck\ \texttt{sources/ical.py} & \textbf{Cohérent} \\
\texttt{PRONOTE\_URL} & \emojicheck\ Ligne 3 & \emojicheck\ \texttt{client.py} (ligne 294) & \textbf{Cohérent} \\
\texttt{PRONOTE\_ENT} & \emojicheck\ Ligne 7 & \emojicheck\ \texttt{client.py} (ligne 297, \texttt{\_ENT\_NAMES}) & \textbf{Cohérent} \\
\texttt{PRONOTE\_AUTH\_MODE=qr\_token} & \emojicheck\ Ligne 23 & \emojicheck\ \texttt{client.py} (ligne 334) & \textbf{Cohérent} \\
\texttt{PRONOTE\_QR\_CODE\_FILE} & \emojicheck\ Ligne 24 & \emojicheck\ \texttt{client.py} (ligne 387) & \textbf{Cohérent} \\
\texttt{PRONOTE\_QR\_PIN} & \emojicheck\ Ligne 25 & \emojicheck\ \texttt{client.py} (ligne 388) & \textbf{Cohérent} \\
\emph{« Le QR code se génère sur le site web »} & \emojicheck\ Ligne 21 & \emojicheck\ Section « Procédure QR pour \texttt{pronote-sync} » & \textbf{Cohérent} \\
\bottomrule
\end{tabularx}
\end{table}
\section{Écarts avec \texttt{docs/exploitation.md}}
\label{sec:ecarts-exploitation}
\begin{table}[htbp]
\centering
\caption{Écarts avec \texttt{docs/exploitation.md}}
\label{tab:ecarts-exploitation}
\begin{tabularx}{\textwidth}{L{4.2cm} L{2.6cm} L{5cm} L{2.6cm}}
\toprule
\textbf{Élément} & \textbf{\texttt{exploitation.md}} & \textbf{Ce document} & \textbf{Action} \\
\midrule
\emph{« Le QR code expire \textasciitilde{}10 minutes »} & Ligne 22 & \warningicon\ \textbf{Hypothèse non vérifiée} (section « Cycle de vie des tokens ») & \textbf{Aucune} (hors périmètre). \\
\emph{« Mode \texttt{qr\_token} incompatible avec dry-run »} & Lignes 65--66 & \emojicheck\ Section « Incompatibilités » & \textbf{Cohérent} \\
\emph{« Fichier \texttt{.pronote\_auth\_state.json} (mode 0600) »} & Lignes 70--75 & \emojicheck\ Section « Sécurité » & \textbf{Cohérent} \\
\bottomrule
\end{tabularx}
\end{table}
% =============================================================================
% FIN DE L'ANNEXE
% =============================================================================
+34
View File
@@ -0,0 +1,34 @@
% =============================================================================
% ANNEXE A : BIBLIOTHÈQUES TIERCES
% =============================================================================
\chapter{Bibliothèques tierces}
\label{annex:libraries}
\begin{table}[htbp]
\centering
\caption{Bibliothèques tierces et leur statut dans \texttt{pronote-sync}}
\label{tab:libraries}
\begin{tabularx}{\textwidth}{L{2.5cm} L{1.5cm} L{3cm} L{3cm} L{2.5cm}}
\toprule
\textbf{Bibliothèque} & \textbf{Langage} & \textbf{Dépôt} & \textbf{Méthodes supportées} & \textbf{Statut dans \texttt{pronote-sync}} \\
\midrule
\textbf{pronotepy} & Python & \url{https://github.com/bain3/pronotepy} & Connexion directe, QR code/token, \textbf{30 ENT} (liste fermée) & \emojicheck\ \textbf{Dépendance principale} (version \textbf{2.15.7} vérifiée). \\
\textbf{pronote-api} & TypeScript & \url{https://github.com/Litarvan/pronote-api} & Connexion directe, CAS, ENT & \emojicross\ Non utilisée. \\
\textbf{pronote-qrcode-api} & JavaScript & \url{https://github.com/Androz2091/pronote-qrcode-api} & Déchiffrement QR code & \emojicross\ Non utilisée (intégration native via \texttt{pronotepy}). \\
\bottomrule
\end{tabularx}
\end{table}
\begin{infobox}
\textbf{Source} : {\emojiinfo\ Observé dans \texttt{pyproject.toml}} (dépendances) + code source local
(2026-09-12).
\end{infobox}
% =============================================================================
% FIN DE L'ANNEXE
% =============================================================================
+36
View File
@@ -0,0 +1,36 @@
% =============================================================================
% CHAPITRE : INTRODUCTION
% =============================================================================
\chapter{Introduction}
\label{ch:introduction}
Ce document documente \textbf{uniquement les méthodes d'authentification prises en charge ou
délégables par \texttt{pronote-sync}}, en alignement strict avec :
\begin{itemize}
\item Le code source de \texttt{pronote\_sync/sources/pronote/client.py} (liste fermée
\texttt{\_ENT\_NAMES}, gestion des tokens).
\item La bibliothèque \texttt{pronotepy} \textbf{2.15.7} (contrat des méthodes
\texttt{qrcode\_login}, \texttt{token\_login}, \texttt{export\_credentials}).
\item Les paramètres de configuration \texttt{.env.example} (lignes 2125).
\end{itemize}
\section{Périmètre}
\begin{itemize}
\item {\emojicheck\ \textbf{Pris en charge}} : Méthodes implémentées et testées dans
\texttt{pronote-sync}.
\item {\emojiinfo\ \textbf{Délégable à pronotepy}} : Méthodes gérées par \texttt{pronotepy}
mais non directement exposées par \texttt{pronote-sync}.
\item {\emojicross\ \textbf{Non implémenté}} : Méthodes non supportées (ex. CAS/SAML
générique, EduConnect direct).
\end{itemize}
\section{Public cible}
Développeurs et intégrateurs de \texttt{pronote-sync}.
% =============================================================================
% FIN DU CHAPITRE
% =============================================================================
+112
View File
@@ -0,0 +1,112 @@
% =============================================================================
% CHAPITRE : MÉTHODE 1 — URL ICAL SÉCURISÉE
% =============================================================================
\chapter{Méthode 1 : URL iCal sécurisée (\texttt{icalsecurise})}
\label{ch:method1}
\section{Principe général}
Pronote expose un flux de calendrier en lecture seule conforme à la norme
\textbf{iCalendar (RFC 5545)}. L'accès est contrôlé par un \textbf{jeton secret} intégré dans
l'URL sous forme de paramètre de requête \texttt{icalsecurise}.
\begin{infobox}
\textbf{Statut} : {\emojiinfo\ Observé localement} (fonctionnalité native de Pronote, version non spécifiée).
\end{infobox}
\section{Obtention du jeton}
\begin{enumerate}
\item Se connecter à Pronote (Espace Parents ou Élève) via n'importe quelle méthode.
\item Accéder à la vue \guillemotleft Emploi du temps \guillemotright.
\item Utiliser la fonction \guillemotleft Export iCal \guillemotright ou \guillemotleft Exporter \guillemotright.
\item Pronote génère une URL contenant un jeton secret \texttt{icalsecurise}.
\item \textbf{Copier cette URL} : elle constitue une crédentiale unique sensible qui doit être
protégée comme un mot de passe.
\end{enumerate}
\begin{infobox}
\textbf{Source} : {\emojiinfo\ Observé localement dans les interfaces Pronote} (version non spécifiée,
hypothèse à valider).
\textbf{Note} : Les étapes dépendent de l'instance et du type de compte. L'exemple ci-dessus est
non fonctionnel et ne contient aucun secret.
\end{infobox}
\section{Format de l'URL}
\begin{lstlisting}[language=bash, caption=Format d'URL iCal Pronote, label=lst:ical-url]
https://{etablissement}.index-education.net/pronote/ical/Edt_{prenom}.ics?icalsecurise={jeton}&version={version}&param={param}
\end{lstlisting}
\begin{itemize}
\item \texttt{icalsecurise} : \textbf{Jeton secret} (crédentiale).
\item \texttt{version} : Version de Pronote (ex. \texttt{2024}).
\item \texttt{param} : Paramètres optionnels.
\end{itemize}
\begin{infobox}
\textbf{Source} : {\emojiinfo\ Analysé via \texttt{pronote\_sync/sources/ical.py}}.
\end{infobox}
\section{Cycle de vie}
\begin{itemize}
\item \textbf{Pérennité} : Le jeton reste valide jusqu'à :
\begin{itemize}
\item Révocation manuelle par l'utilisateur dans Pronote.
\item Régénération par l'établissement (ex. à la rentrée scolaire).
\end{itemize}
\item {\warningicon\ \textbf{Hypothèse à valider}} : La durée exacte dépend des politiques
de l'établissement (non documentée officiellement).
\end{itemize}
\begin{infobox}
\textbf{Source} : {\warningicon\ Comportement variable selon les instances} (à tester localement).
\end{infobox}
\section{Sécurité}
\begin{enumerate}
\item \textbf{Surface d'attaque} : Le jeton est encodé dans l'URL \rightarrow risque d'exposition via :
\begin{itemize}
\item Logs serveur/proxy.
\item En-tête \texttt{Referer}.
\item Historique du navigateur.
\end{itemize}
\item \textbf{Recommandations} :
\begin{itemize}
\item \textbf{Ne jamais versionner} l'URL (ex. dans \texttt{.env} ou Git).
\item Utiliser \textbf{HTTPS} (obligatoire).
\item Masquer l'URL dans les logs (ex. via \texttt{redact\_url()} dans
\texttt{pronote-sync}).
\end{itemize}
\end{enumerate}
\begin{infobox}
\textbf{Source} : {\warningicon\ Recommandation du projet} (inspirée des bonnes pratiques générales
de sécurité).
\end{infobox}
\section{Intégration dans \texttt{pronote-sync}}
\begin{itemize}
\item \textbf{Paramètre} : \texttt{PRONOTE\_ICAL\_URL} (ex. \texttt{.env.example} ligne 2).
\item \textbf{Comportement} :
\begin{itemize}
\item Prioritaire en mode \texttt{PRONOTE\_AGENDA\_SOURCE=auto}.
\item Si l'URL est invalide ou expire, repli automatique vers \texttt{pronotepy} (si
\texttt{PRONOTE\_AGENDA\_SOURCE=auto}).
\item \textbf{Aucun repli} si \texttt{PRONOTE\_AGENDA\_SOURCE=ical} (échec explicite).
\end{itemize}
\end{itemize}
\begin{infobox}
\textbf{Source} : {\emojiinfo\ \texttt{pronote\_sync/config/settings.py} +
\texttt{pronote\_sync/sources/ical.py}}.
\end{infobox}
% =============================================================================
% FIN DU CHAPITRE
% =============================================================================
+90
View File
@@ -0,0 +1,90 @@
% =============================================================================
% CHAPITRE : MÉTHODE 2 — CONNEXION DIRECTE
% =============================================================================
\chapter{Méthode 2 : Connexion directe (identifiant / mot de passe + ENT)}
\label{ch:method2}
\section{Principe général}
Connexion via le protocole propriétaire de Pronote (JSON sur HTTPS), avec
\textbf{chiffrement AES-CBC utilisant une clé dérivée par MD5 (16 octets, soit AES-128)} comme
implémenté dans pronotepy 2.15.7 et mécanisme de défi-réponse. Utilisé par l'interface web.
\begin{infobox}
\textbf{Statut} : {\emojiinfo\ Observé dans le code local} (délégable à pronotepy via
\texttt{ParentClient} ou \texttt{Client}).
\end{infobox}
\section{Flux d'authentification}
\begin{enumerate}
\item \textbf{Initialisation} : Requête GET vers \texttt{/pronote/\{espace\}.html} (ex.
\texttt{parent.html}).
\begin{itemize}
\item Récupère \texttt{h} (ID de session), \texttt{a} (ID espace), \texttt{sCrA}/\texttt{sCoA}
(drapeaux chiffrement/compression).
\end{itemize}
\item \textbf{Échange de clés} : Requête POST vers
\texttt{/pronote/appelfonction/\{a\}/\{h\}/\{numeroOrdre\}}.
\item \textbf{Identification} : Soumission de \texttt{identifiant},
\texttt{genreConnexion=0}, \texttt{genreEspace=\{a\}}.
\item \textbf{Résolution du défi} :
\begin{itemize}
\item Calcul de \texttt{mtp = MAJUSCULE(HEX(SHA256(alea + mot\_de\_passe)))}.
\item Dérivation de \texttt{key\_challenge = MD5(nom\_utilisateur + mtp)}.
\item Déchiffrement du \texttt{challenge} avec
\textbf{AES-CBC utilisant une clé dérivée par MD5 (16 octets, soit AES-128)}.
\end{itemize}
\item \textbf{Authentification} : Soumission de la réponse au défi.
\end{enumerate}
\begin{infobox}
\textbf{Source} : {\emojiinfo\ Observé dans le code local de \texttt{pronotepy} 2.15.7} (module
\texttt{clients.py} et \texttt{pronoteAPI.py}, vérifié le 2026-09-12).
\end{infobox}
\section{Intégration dans \texttt{pronote-sync}}
\begin{itemize}
\item \textbf{Paramètres} :
\begin{itemize}
\item \texttt{PRONOTE\_URL} (ex. \texttt{.env.example} ligne 3).
\item \texttt{PRONOTE\_USERNAME}, \texttt{PRONOTE\_PASSWORD}.
\item \texttt{PRONOTE\_ENT} (slug dans \texttt{\_ENT\_NAMES}).
\item \texttt{PRONOTE\_ACCOUNT\_TYPE} (ex. \texttt{parent}).
\end{itemize}
\item \textbf{Comportement} :
\begin{itemize}
\item Utilise \texttt{pronotepy.ParentClient} pour les comptes parents.
\item \textbf{Repli} : Si iCal échoue en mode \texttt{auto}, \texttt{pronotepy} est utilisé.
\item \textbf{Pas de repli} si \texttt{PRONOTE\_AGENDA\_SOURCE=pronotepy} (échec explicite).
\end{itemize}
\end{itemize}
\begin{infobox}
\textbf{Source} : {\emojiinfo\ \texttt{pronote\_sync/sources/pronote/client.py}} (méthode
\texttt{\_connect\_password}).
\end{infobox}
\section{ENT supportés}
Liste \textbf{fermée} des \textbf{30 ENT} résolubles (lignes 5283 de
\texttt{pronote\_sync/sources/pronote/client.py}) :
\begin{lstlisting}[language=Python, caption=Liste des ENT supportés, label=lst:ent-list]
_monbureaunumerique, ent_elyco, bordeaux, ent_creuse, occitanie_montpellier, ...
\end{lstlisting}
\begin{warningbox}
\textbf{Hypothèse à valider} : Les ENT non listés nécessitent une contribution à
\texttt{pronotepy}.
\end{warningbox}
\begin{infobox}
\textbf{Source} : {\emojiinfo\ Code source local de \texttt{pronote-sync}} (2026-09-12).
\end{infobox}
% =============================================================================
% FIN DU CHAPITRE
% =============================================================================
+212
View File
@@ -0,0 +1,212 @@
% =============================================================================
% 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 2125) 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 181189) + \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:382387}).
\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 245280) + \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:382387}).
\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 346364).
\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 6566).
\end{infobox}
% =============================================================================
% FIN DU CHAPITRE
% =============================================================================
+17
View File
@@ -0,0 +1,17 @@
% =============================================================================
% CHAPITRE : MÉTHODE 4 — SSO ENT (CAS/SAML)
% =============================================================================
\chapter{Méthode 4 : SSO ENT (CAS/SAML)}
\label{ch:method4}
\begin{warningbox}
\textbf{Statut} : {\emojicross\ Non implémenté} dans \texttt{pronote-sync}.
Seuls les 30 ENT de la liste fermée \texttt{\_ENT\_NAMES} (pronotepy 2.15.7) sont supportés via
\texttt{ent} dans \texttt{ParentClient}. \textbf{Pas de SSO générique CAS/SAML.}
\end{warningbox}
% =============================================================================
% FIN DU CHAPITRE
% =============================================================================
+16
View File
@@ -0,0 +1,16 @@
% =============================================================================
% CHAPITRE : MÉTHODE 5 — EDUCONNECT (HUBEDUCONNECT)
% =============================================================================
\chapter{Méthode 5 : EduConnect (HubEduConnect)}
\label{ch:method5}
\begin{warningbox}
\textbf{Statut} : {\emojicross\ Non implémenté} dans \texttt{pronote-sync}.
Nécessite une intégration CAS/SAML générique, \textbf{non supportée par ce projet}.
\end{warningbox}
% =============================================================================
% FIN DU CHAPITRE
% =============================================================================
+16
View File
@@ -0,0 +1,16 @@
% =============================================================================
% CHAPITRE : MÉTHODE 6 — CAS DIRECT
% =============================================================================
\chapter{Méthode 6 : CAS direct}
\label{ch:method6}
\begin{warningbox}
\textbf{Statut} : {\emojicross\ Non implémenté} dans \texttt{pronote-sync}.
Non supporté.
\end{warningbox}
% =============================================================================
% FIN DU CHAPITRE
% =============================================================================
+17
View File
@@ -0,0 +1,17 @@
% =============================================================================
% CHAPITRE : MÉTHODE 7 — API PUBLIQUE
% =============================================================================
\chapter{Méthode 7 : API publique}
\label{ch:method7}
\begin{warningbox}
\textbf{Statut} : {\emojicross\ Inexistante}.
Aucune API publique n'est utilisée/implémentée par ce projet ; aucune API officielle
publique n'a été vérifiée à la date du 2026-09-12.
\end{warningbox}
% =============================================================================
% FIN DU CHAPITRE
% =============================================================================
+38
View File
@@ -0,0 +1,38 @@
% =============================================================================
% CHAPITRE : TABLEAU DE COMPATIBILITÉ
% =============================================================================
\chapter{Tableau de compatibilité avec \texttt{pronote-sync}}
\label{ch:overview}
\begin{table}[htbp]
\centering
\caption{Méthodes d'authentification et leur statut dans \texttt{pronote-sync}}
\label{tab:compatibilite}
\begin{tabularx}{\textwidth}{L{2.5cm} L{3cm} L{3cm} L{2.5cm} L{4cm}}
\toprule
\textbf{Méthode} & \textbf{Statut dans \texttt{pronote-sync}} & \textbf{Implémentation} & \textbf{Source} & \textbf{Notes} \\
\midrule
\textbf{URL iCal sécurisée} (\texttt{icalsecurise}) & \emojicheck\ Pris en charge & Source \texttt{ical} (prioritaire en mode \texttt{auto}) & \emojiinfo\ \texttt{pronote\_sync/sources/ical.py} & Jeton dans l'URL traité comme secret. \\
\textbf{Connexion directe} (mot de passe + ENT) & \emojicheck\ Pris en charge & Source \texttt{pronotepy} (repli si iCal échoue) & \emojiinfo\ \texttt{pronote\_sync/sources/pronote/client.py} & Liste fermée \texttt{\_ENT\_NAMES} (lignes 5284). \\
\textbf{QR Code + token mobile} & \emojicheck\ Pris en charge & Mode \texttt{PRONOTE\_AUTH\_MODE=qr\_token} & \emojiinfo\ \texttt{pronotepy.Client.qrcode\_login} + \texttt{token\_login} & Voir \hyperref[sec:procedure-qr]{Procédure QR} (Section \ref{sec:procedure-qr}). \\
\textbf{SSO ENT (CAS/SAML)} & \emojicross\ Non implémenté &&& Seuls les 30 ENT de la liste fermée \texttt{\_ENT\_NAMES} (pronotepy 2.15.7) sont supportés via \texttt{ent} dans \texttt{ParentClient}. \textbf{Pas de SSO générique CAS/SAML.} \\
\textbf{EduConnect (HubEduConnect)} & \emojicross\ Non implémenté &&& Nécessite une intégration CAS/SAML générique, \textbf{non supportée par ce projet}. \\
\textbf{CAS direct} & \emojicross\ Non implémenté &&& Non supporté. \\
\textbf{API publique} & \emojicross\ Inexistante &&& Aucune API publique n'est utilisée/implémentée par ce projet ; aucune API officielle publique n'a été vérifiée à la date du 2026-09-12. \\
\bottomrule
\end{tabularx}
\end{table}
% =============================================================================
% FIN DU CHAPITRE
% =============================================================================
+100
View File
@@ -0,0 +1,100 @@
% =============================================================================
% PAGE DE TITRE — Authentification Pronote
% =============================================================================
\begin{titlepage}
\newgeometry{margin=2cm}
\begin{center}
\vspace*{2cm}
{\Huge\bfseries\color{primaryNavy} Authentification Pronote\\}
{\huge\bfseries\color{secondarySlate} Référence technique pour \texttt{pronote-sync}}
\vspace{1.5cm}
{\Large\color{secondarySlate} Version 1.0 — 12 septembre 2026}
\vspace{2cm}
{\large\color{gray} Équipe Architecture et Sécurité}
\vspace{1cm}
{\small\color{gray}
Ce document n'est pas un guide officiel. Il n'est ni approuvé, ni validé, ni autorisé
par le Ministère de l'Éducation Nationale française ni par Docaposte (éditeur de
Pronote / Index Éducation).
}
\vspace{2cm}
\begin{warningbox}
\centering
{\bfseries\large\color{warningRed} AVERTISSEMENT}\\
\vspace{0.5cm}
{\small
Ce document repose sur des observations directes du code source de
\texttt{pronotepy} (version 2.15.7, vérifiée localement le 2026-09-12), des issues
publiques du dépôt \texttt{bain3/pronotepy}, et des tests d'intégration du projet
\texttt{pronote-sync}.\\
{\bfseries Un essai réel sur l'instance Pronote cible est nécessaire avant tout usage
en production. Les tests/mocks ne prouvent pas la compatibilité d'instance.}
}
\end{warningbox}
\vfill
{\small\color{gray} Compilé avec LuaLaTeX}
\end{center}
\restoregeometry
\end{titlepage}
% =============================================================================
% TABLE DES MATIÈRES
% =============================================================================
\newpage
\tableofcontents
\thispagestyle{fancy}
% =============================================================================
% LISTE DES TABLEAUX (optionnel)
% =============================================================================
\newpage
\listoftables
\thispagestyle{fancy}
% =============================================================================
% AVERTISSEMENT INITIAL
% =============================================================================
\newpage
\section*{Avertissement initial}
\addcontentsline{toc}{section}{Avertissement initial}
\begin{infobox}
\textbf{Convention de qualification} :
\begin{itemize}
\item {\emojiinfo\ \textbf{Observé dans le code local}} : Mécanisme vérifié dans le code
source de \texttt{pronotepy} 2.15.7 ou \texttt{pronote-sync}.
\item {\emojiinfo\ \textbf{Observé localement}} : Comportement constaté dans les interfaces
Pronote (version non spécifiée, hypothèse à valider).
\item {\warningicon\ \textbf{Hypothèse à valider}} : Affirmation non vérifiée, nécessitant
une confirmation par test sur une instance réelle.
\end{itemize}
\end{infobox}
\vspace{1cm}
\begin{warningbox}
\textbf{Note importante (2026-09-12)} :
Un essai réel sur l'instance Pronote cible est nécessaire avant tout usage en production.
Les tests/mocks ne prouvent pas la compatibilité d'instance.
\end{warningbox}
\newpage
+28
View File
@@ -0,0 +1,28 @@
% =============================================================================
% GLOSSAIRE (chapitre non numéroté)
% =============================================================================
\chapter*{Glossaire}
\addcontentsline{toc}{chapter}{Glossaire}
\begin{tabularx}{\textwidth}{L{4cm} L{12cm}}
\toprule
\textbf{Terme} & \textbf{Définition} \\
\midrule
\textbf{ENT} & Espace Numérique de Travail (ex. Mon Bureau Numérique, Paris Classe Numérique). \\
\textbf{Jeton \texttt{icalsecurise}} & Token secret intégré dans l'URL iCal, équivalent à un mot de passe. \\
\textbf{\texttt{jetonConnexionAppliMobile}} & Token obtenu après appairage QR code, dont la durée dépend de l'instance/du serveur et n'est pas garantie. \\
\textbf{UUID} & Identifiant unique permanent pour l'application (ex. \texttt{pronote-sync-\{uuid4()\}}). \\
\textbf{PIN} & Code à 4 chiffres défini lors de la génération du QR code. \\
\bottomrule
\end{tabularx}
% =============================================================================
% FIN DU GLOSSAIRE
% =============================================================================
+24
View File
@@ -0,0 +1,24 @@
% =============================================================================
% HISTORIQUE DES RÉVISIONS (chapitre non numéroté)
% =============================================================================
\chapter*{Historique des révisions}
\addcontentsline{toc}{chapter}{Historique des révisions}
\begin{tabularx}{\textwidth}{L{2.4cm} L{2.6cm} L{11cm}}
\toprule
\textbf{Date} & \textbf{Auteur} & \textbf{Modifications} \\
\midrule
2026-09-12 & Agent tech-writer & Refonte complète : qualification des affirmations, ajout des sources, tableau de compatibilité, procédure QR alignée sur pronotepy 2.15.7. \\
2026-09-12 & Agent tech-writer & Corrections suite à revue indépendante (ticket \#10) : précision cryptographie (AES-128), qualification des sources, alignement nombre d'ENT (30), clarification SSO/CAS, exemples non copiables, note d'essai réel. \\
2026-09-12 & Agent tech-writer & Corrections suite à revue ticket \#10 : suppression de tous les placeholders de secrets remplacés par de la prose descriptive ; clarification du périmètre réel des méthodes d'authentification (accès limité aux opérations implémentées) ; qualification de la durée du token \texttt{jetonConnexionAppliMobile} comme dépendante de l'instance/serveur et non garantie. \\
\bottomrule
\end{tabularx}
% =============================================================================
% FIN DE L'HISTORIQUE
% =============================================================================
+159
View File
@@ -0,0 +1,159 @@
% =============================================================================
% PRÉAMBULE LaTeX — Authentification Pronote
% =============================================================================
% --- Classe de document ---
\documentclass[11pt,a4paper,oneside]{report}
% --- Encodage et langue ---
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[french]{babel}
\usepackage{lmodern}
% --- Polices (plan documentaire : EB Garamond / Carlito / DejaVu Sans Mono) ---
% EB Garamond (sérif) : police principale du document
\usepackage{ebgaramond}
% Carlito (sans-sérif, compatible métriquement Calibri)
\usepackage[sfdefault]{carlito}
% DejaVu Sans Mono (chasse fixe) : code, JSON, requêtes HTTP
% (fontspec est déjà chargé par EB Garamond sous LuaLaTeX)
\setmonofont{DejaVu Sans Mono}[Scale=0.9]
% --- Couleurs (palette Bleu Institutionnel & Ardoise) ---
\usepackage{xcolor}
\definecolor{primaryNavy}{RGB}{20,45,85}
\definecolor{secondarySlate}{RGB}{70,95,125}
\definecolor{accentSteel}{RGB}{0,115,150}
\definecolor{lightGray}{RGB}{240,240,240}
\definecolor{warningRed}{RGB}{180,50,50}
% --- Mise en page ---
\usepackage[a4paper, margin=2.5cm, top=2cm, bottom=2cm]{geometry}
\usepackage{parskip}
\setlength{\parskip}{1em}
\setlength{\parindent}{0pt}
% --- Hyperliens et PDF ---
\usepackage{hyperref}
\hypersetup{
colorlinks=true,
linkcolor=accentSteel,
urlcolor=accentSteel,
citecolor=secondarySlate,
filecolor=primaryNavy,
menucolor=primaryNavy,
runcolor=primaryNavy,
pdfauthor={Équipe Architecture & Sécurité},
pdftitle={Authentification Pronote — Référence technique},
pdfsubject={Référence technique d'authentification pour pronote-sync},
pdfkeywords={Pronote, authentification, pronote-sync, QR code, token, ENT, iCal},
bookmarksopen=true,
bookmarksnumbered=true
}
% --- Titres et sections ---
\usepackage{titlesec}
\titleformat{\chapter}[display]
{\normalfont\huge\bfseries\color{primaryNavy}}
{\chaptertitlename\ \thechapter}
{20pt}
{\Huge\color{primaryNavy}}
\titleformat{\section}
{\normalfont\Large\bfseries\color{primaryNavy}}
{\thesection}
{1em}
{\color{primaryNavy}}
\titleformat{\subsection}
{\normalfont\large\bfseries\color{secondarySlate}}
{\thesubsection}
{1em}
{\color{secondarySlate}}
\titleformat{\subsubsection}
{\normalfont\bfseries\color{secondarySlate}}
{\thesubsubsection}
{1em}
{\color{secondarySlate}}
% --- Tableaux ---
\usepackage{tabularx}
\usepackage{booktabs}
\usepackage{array}
\newcolumntype{L}[1]{>{\raggedright\let\newline\\\arraybackslash\hspace{0pt}\hspace{1em}}p{#1}}
\newcolumntype{C}[1]{>{\centering\let\newline\\\arraybackslash\hspace{0pt}\hspace{1em}}p{#1}}
\newcolumntype{R}[1]{>{\raggedleft\let\newline\\\arraybackslash\hspace{0pt}\hspace{1em}}p{#1}}
% --- Listes et encadrés ---
\usepackage{enumitem}
\setlist[itemize]{leftmargin=*, topsep=0.5em, itemsep=0.25em}
\setlist[enumerate]{leftmargin=*, topsep=0.5em, itemsep=0.25em}
% --- Callouts (encadrés colorés) ---
\usepackage{mdframed}
\newmdenv[linecolor=warningRed, linewidth=2pt, leftmargin=10pt, rightmargin=10pt, innerleftmargin=10pt, innerrightmargin=10pt, backgroundcolor=red!5!white, roundcorner=5pt, skipabove=10pt, skipbelow=10pt]{warningbox}
\newmdenv[linecolor=secondarySlate, linewidth=2pt, leftmargin=10pt, rightmargin=10pt, innerleftmargin=10pt, innerrightmargin=10pt, backgroundcolor=blue!5!white, roundcorner=5pt, skipabove=10pt, skipbelow=10pt]{infobox}
% --- Code source ---
\usepackage{listings}
\lstdefinestyle{pronote}{
basicstyle=\ttfamily\footnotesize,
breaklines=true,
frame=single,
rulecolor=\color{gray!30},
backgroundcolor=\color{lightGray},
keywordstyle=\color{primaryNavy},
stringstyle=\color{secondarySlate},
commentstyle=\color{gray},
showstringspaces=false,
tabsize=2,
captionpos=b,
xleftmargin=10pt,
xrightmargin=10pt,
aboveskip=10pt,
belowskip=10pt
}
\lstset{style=pronote}
% --- Symboles et icônes ---
\usepackage{amssymb}
\usepackage{pifont}
\newcommand{\checkmarkicon}{\ding{51}} % ✓
\newcommand{\warningicon}{\ding{43}} % ⚠
\newcommand{\infoicon}{\ding{48}} %
\newcommand{\crossicon}{\ding{55}} % ✗
% --- Mathématiques ---
\usepackage{amsmath}
% --- Table des matières ---
\usepackage{tocloft}
\renewcommand{\cftchapfont}{\normalfont\bfseries\color{primaryNavy}}
\renewcommand{\cftsecfont}{\normalfont\color{primaryNavy}}
\renewcommand{\cftsubsecfont}{\normalfont\color{secondarySlate}}
% --- En-têtes et pieds de page ---
\usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhf{}
\fancyhead[LE,RO]{\thepage}
\fancyhead[LO]{\nouppercase{\rightmark}}
\fancyhead[RE]{\nouppercase{\leftmark}}
\renewcommand{\headrulewidth}{0.5pt}
\renewcommand{\footrulewidth}{0pt}
% --- Espacement vertical ---
\usepackage{setspace}
\onehalfspacing
% --- Césure ---
\usepackage{microtype}
% --- Support des emojis (repli sur texte si non disponible) ---
\newcommand{\emojiwarning}{\warningicon}
\newcommand{\emojicheck}{\checkmarkicon}
\newcommand{\emojicross}{\crossicon}
\newcommand{\emojiinfo}{\infoicon}
% =============================================================================
% FIN DU PRÉAMBULE
% =============================================================================
Binary file not shown.
+6 -2
View File
@@ -9,8 +9,8 @@
%%
%% INSTRUCTIONS DE COMPILATION :
%% Ce document est conçu pour être compilé avec LuaLaTeX :
%% lualatex -interaction=nonstopmode docs/pronote-auth.tex
%% lualatex -interaction=nonstopmode docs/pronote-auth.tex
%% TEXINPUTS="docs:" lualatex -interaction=nonstopmode -output-directory=docs docs/pronote-auth.tex
%% TEXINPUTS="docs:" lualatex -interaction=nonstopmode -output-directory=docs docs/pronote-auth.tex
%% (Deux passes nécessaires pour la génération de la table des matières,
%% des références croisées et des hyperliens).
%%
@@ -52,5 +52,9 @@
\include{annex-libraries}
\include{annex-comparison}
\include{annex-ecarts}
\include{glossaire}
\include{historique}
\end{document}