docs(auth): rendre la référence Pronote vérifiable #45
+2
-1
@@ -22,7 +22,8 @@ PRONOTE_AUTH_MODE=password
|
|||||||
# Le QR code expire ~10 minutes après génération
|
# Le QR code expire ~10 minutes après génération
|
||||||
# PRONOTE_AUTH_MODE=qr_token
|
# PRONOTE_AUTH_MODE=qr_token
|
||||||
# PRONOTE_QR_CODE_FILE=/path/to/qr_code.json
|
# PRONOTE_QR_CODE_FILE=/path/to/qr_code.json
|
||||||
# PRONOTE_QR_PIN=1234
|
# PRONOTE_QR_PIN=
|
||||||
|
# Valeur à définir localement dans .env ; ne jamais la committer.
|
||||||
|
|
||||||
# --- CalDAV ---
|
# --- CalDAV ---
|
||||||
CALDAV_URL=https://caldav.example.com/calendars/user/pronote/
|
CALDAV_URL=https://caldav.example.com/calendars/user/pronote/
|
||||||
|
|||||||
@@ -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
|
||||||
|
% =============================================================================
|
||||||
@@ -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
|
||||||
|
% =============================================================================
|
||||||
@@ -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
|
||||||
|
% =============================================================================
|
||||||
@@ -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 21–25).
|
||||||
|
\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
|
||||||
|
% =============================================================================
|
||||||
@@ -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}¶m={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
|
||||||
|
% =============================================================================
|
||||||
@@ -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 52–83 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
|
||||||
|
% =============================================================================
|
||||||
@@ -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 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
|
||||||
|
% =============================================================================
|
||||||
@@ -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
|
||||||
|
% =============================================================================
|
||||||
@@ -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
|
||||||
|
% =============================================================================
|
||||||
@@ -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
|
||||||
|
% =============================================================================
|
||||||
@@ -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
|
||||||
|
% =============================================================================
|
||||||
@@ -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 52–84). \\
|
||||||
|
|
||||||
|
\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
|
||||||
|
% =============================================================================
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
% =============================================================================
|
||||||
@@ -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
|
||||||
|
% =============================================================================
|
||||||
@@ -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
|
||||||
|
% =============================================================================
|
||||||
+253
-497
@@ -1,570 +1,326 @@
|
|||||||
> ⚠️ **AVERTISSEMENT**
|
> ⚠️ **AVERTISSEMENT**
|
||||||
>
|
>
|
||||||
> 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). Les informations présentées reposent sur des recherches publiques, des travaux de rétro-ingénierie menés par la communauté open-source et des analyses techniques. Les protocoles décrits ne sont pas officiellement publiés par Index Éducation et peuvent évoluer sans préavis.
|
> 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). Les informations présentées reposent sur :
|
||||||
|
> - des **observations directes** du code source de `pronotepy` (version **2.15.7**, vérifiée localement le 2026-09-12) ;
|
||||||
|
> - des **issues publiques** du dépôt [bain3/pronotepy](https://github.com/bain3/pronotepy) (ex. #309, #344) ;
|
||||||
|
> - des **tests d'intégration** du projet `pronote-sync`.
|
||||||
>
|
>
|
||||||
> Ce document a été produit à l'aide de plusieurs agents basés sur des modèles de langage (LLM) :
|
> **⚠️ 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.
|
||||||
> - **Mercury 2.5** (agent explorer) — exploration du code pour établir les faits techniques.
|
>
|
||||||
> - **Gemini 3.5 Flash** (agent web-explorer) — recherche des méthodes d'authentification externes.
|
> **Convention de qualification** :
|
||||||
> - **Hy3** (agent planner) — planification de la structure du document et découpage en sections.
|
> ✅ **Observé dans le code local** : Mécanisme vérifié dans le code source de `pronotepy` 2.15.7 ou `pronote-sync`.
|
||||||
> - **Mistral Medium** (agent tech-writer) — rédaction du document.
|
> 🔍 **Observé localement** : Comportement constaté dans les interfaces Pronote (version non spécifiée, hypothèse à valider).
|
||||||
> - **GPT-5.6 Luna** (agent reviewer) — revue du contenu pour l'exactitude et la cohérence.
|
> ⚠️ **Hypothèse à valider** : Affirmation non vérifiée, nécessitant une confirmation par test sur une instance réelle.
|
||||||
> - **GLM-5.2** (agent orchestrator) — coordination et intégration du travail.
|
|
||||||
> - **Gemini 3.7 Flash** (agent ui-designer) — conception de la version LaTeX/PDF.
|
---
|
||||||
# Authentification Pronote — Référence technique
|
|
||||||
|
# Authentification Pronote — Référence technique pour `pronote-sync`
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
Ce manuel documente l’ensemble des méthodes d’authentification connues pour accéder aux données élèves/parents de Pronote (notes, emploi du temps, devoirs, absences, etc.). Il couvre à la fois les mécanismes officiellement supportés et les protocoles issus de l’analyse communautaire.
|
Ce document documente **uniquement les méthodes d'authentification prises en charge ou délégables par `pronote-sync`**, en alignement strict avec :
|
||||||
|
- Le code source de `pronote_sync/sources/pronote/client.py` (liste fermée `_ENT_NAMES`, gestion des tokens).
|
||||||
|
- La bibliothèque `pronotepy` **2.15.7** (contrat des méthodes `qrcode_login`, `token_login`, `export_credentials`).
|
||||||
|
- Les paramètres de configuration `.env.example` (lignes 21–25).
|
||||||
|
|
||||||
Le public visé inclut les développeurs, ingénieurs sécurité et intégrateurs système devant maîtriser l’authentification Pronote au niveau protocolaire. Les descriptions utilisent du pseudocode générique, des échanges HTTP et des schémas protocoles, sans présupposer de langage ou framework spécifique.
|
**Périmètre** :
|
||||||
|
- **Pris en charge** : Méthodes implémentées et testées dans `pronote-sync`.
|
||||||
|
- **Délégable à pronotepy** : Méthodes gérées par `pronotepy` mais non directement exposées par `pronote-sync`.
|
||||||
|
- **Non implémenté** : Méthodes non supportées (ex. CAS/SAML générique, EduConnect direct).
|
||||||
|
|
||||||
*Remarque méthodologique* : Les méthodes au-delà de l’export iCal s’appuient sur des recherches publiques et l’analyse de la communauté open source. Ces protocoles, non publiés officiellement par Index Éducation, peuvent évoluer sans préavis.
|
**Public cible** : Développeurs et intégrateurs de `pronote-sync`.
|
||||||
|
|
||||||
## Vue d'ensemble comparative
|
---
|
||||||
|
|
||||||
| Méthode | Périmètre de données | Identifiants requis | Expiration du jeton | Complexité | Statut |
|
## Tableau de compatibilité avec `pronote-sync`
|
||||||
|---|---|---|---|---|---|
|
|
||||||
| URL iCal sécurisée (`icalsecurise`) | Emploi du temps et, si inclus, cahier de textes/devoirs | Jeton dans l'URL | Longue durée | Très faible | Officiel |
|
|
||||||
| Connexion directe (identifiant/mot de passe) | Complet | Identifiant + mot de passe établissement | Session ~15–30 min | Élevée | Reverse-engineered |
|
|
||||||
| SSO ENT (CAS / SAML / Oze) | Complet | Identifiants ENT | Dépend de la session ENT | Très élevée | Reverse-engineered |
|
|
||||||
| SSO EduConnect | Complet | Identifiants nationaux EduConnect | Dépend de la session EduConnect | Très élevée | Reverse-engineered |
|
|
||||||
| CAS (Central Authentication Service) | Complet | Identifiants CAS | Dépend du ticket de service | Modérée | Officiel |
|
|
||||||
| QR Code + jeton mobile | Complet | Code PIN à 4 chiffres → `jetonConnexionAppliMobile` + UUID | QR valable 10 min ; jeton longue durée | Modérée | Reverse-engineered |
|
|
||||||
| API publique | N/A | N/A | N/A | N/A | Inexistante |
|
|
||||||
|
|
||||||
*Complet* désigne l’accès aux notes, emploi du temps, devoirs, absences, messagerie et paramètres, tandis que la méthode iCal se limite à l’emploi du temps et éventuellement aux devoirs.
|
| Méthode | Statut dans `pronote-sync` | Implémentation | Source | Notes |
|
||||||
|
|---------|----------------------------|----------------|--------|-------|
|
||||||
|
| **URL iCal sécurisée** (`icalsecurise`) | ✅ Pris en charge | Source `ical` (prioritaire en mode `auto`) | 🔍 `pronote_sync/sources/ical.py` | Jeton dans l'URL traité comme secret. |
|
||||||
|
| **Connexion directe (mot de passe + ENT)** | ✅ Pris en charge | Source `pronotepy` (repli si iCal échoue) | 🔍 `pronote_sync/sources/pronote/client.py` | Liste fermée `_ENT_NAMES` (lignes 52–84). |
|
||||||
|
| **QR Code + token mobile** | ✅ Pris en charge | Mode `PRONOTE_AUTH_MODE=qr_token` | 🔍 `pronotepy.Client.qrcode_login` + `token_login` | Voir [Procédure QR](#procédure-qr-pour-pronote-sync). |
|
||||||
|
| **SSO ENT (CAS/SAML)** | ❌ Non implémenté | — | — | Seuls les 30 ENT de la liste fermée `_ENT_NAMES` (pronotepy 2.15.7) sont supportés via `ent` dans `ParentClient`. **Pas de SSO générique CAS/SAML.** |
|
||||||
|
| **EduConnect (HubEduConnect)** | ❌ Non implémenté | — | — | Nécessite une intégration CAS/SAML générique, **non supportée par ce projet**. |
|
||||||
|
| **CAS direct** | ❌ Non implémenté | — | — | Non supporté. |
|
||||||
|
| **API publique** | ❌ 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.
|
||||||
|
|
||||||
## Méthode 1 : URL iCal sécurisée (icalsecurise)
|
---
|
||||||
|
|
||||||
|
## Méthode 1 : URL iCal sécurisée (`icalsecurise`)
|
||||||
|
|
||||||
### Principe général
|
### Principe général
|
||||||
Pronote expose un flux de calendrier en lecture seule conforme à la norme iCalendar (RFC 5545). L’accès est contrôlé par un jeton secret intégré dans l’URL sous forme de paramètre de requête `icalsecurise`. Ce jeton est unique par utilisateur et par établissement. **Le jeton constitue la seule crédentiale** : il doit être traité comme un mot de passe. Toute requête HTTP GET vers cette URL permet de récupérer les données du calendrier, sans nécessiter de cookies, de session ni d’en-têtes d’authentification.
|
Pronote expose un flux de calendrier en lecture seule conforme à la norme **iCalendar (RFC 5545)**. L'accès est contrôlé par un **jeton secret** intégré dans l'URL sous forme de paramètre de requête `icalsecurise`.
|
||||||
|
|
||||||
|
✅ **Statut** : **Observé localement** (fonctionnalité native de Pronote, version non spécifiée).
|
||||||
|
|
||||||
### Obtention du jeton
|
### Obtention du jeton
|
||||||
Le jeton s’obtient manuellement depuis l’interface web de Pronote :
|
1. Se connecter à Pronote (Espace Parents ou Élève) via n'importe quelle méthode.
|
||||||
1. Se connecter à Pronote (Espace Parents ou Espace Élève) via n’importe quelle méthode d’authentification.
|
2. Accéder à la vue *« Emploi du temps »*.
|
||||||
2. Accéder à la vue « Emploi du temps ».
|
3. Utiliser la fonction *« Export iCal »* ou *« Exporter »*.
|
||||||
3. Utiliser la fonction « Export iCal » ou « Exporter ».
|
4. Pronote génère une URL contenant un jeton secret `icalsecurise`.
|
||||||
4. Pronote génère une URL contenant le paramètre `icalsecurise`.
|
5. **Copier cette URL** : elle constitue une crédentiale unique sensible qui doit être protégée comme un mot de passe.
|
||||||
5. Copier cette URL : elle constitue la crédentiale.
|
|
||||||
|
|
||||||
Cette URL doit être stockée de manière sécurisée (ex. : gestionnaire de secrets, variables protégées). **Elle ne doit jamais être versionnée ou partagée en clair.**
|
|
||||||
|
|
||||||
|
🔹 **Source** : 🔍 Observé localement dans les interfaces Pronote (version non spécifiée, hypothèse à valider).
|
||||||
|
⚠️ **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.
|
||||||
|
|
||||||
### Format de l'URL
|
### Format de l'URL
|
||||||
L’URL suit la structure suivante :
|
|
||||||
```
|
```
|
||||||
https://{etablissement}.index-education.net/pronote/ical/Edt_{prenom}.ics?icalsecurise={jeton}&version={version}¶m={param}
|
https://{etablissement}.index-education.net/pronote/ical/Edt_{prenom}.ics?icalsecurise={jeton}&version={version}¶m={param}
|
||||||
```
|
```
|
||||||
Exemple masqué :
|
- `icalsecurise` : **Jeton secret** (crédentiale).
|
||||||
`https://XXXXXXX.index-education.net/pronote/ical/Edt_Alice.ics?icalsecurise=••••••••&version=2023¶m=...`
|
- `version` : Version de Pronote (ex. `2024`).
|
||||||
|
- `param` : Paramètres optionnels.
|
||||||
|
|
||||||
Paramètres de requête :
|
🔹 **Source** : 🔍 Analysé via `pronote_sync/sources/ical.py`.
|
||||||
- `icalsecurise` : jeton secret (crédentiale).
|
|
||||||
- `version` : version de Pronote.
|
|
||||||
- `param` : paramètres supplémentaires (optionnels).
|
|
||||||
|
|
||||||
|
|
||||||
### Flux d'authentification (protocole)
|
|
||||||
Le protocole d’authentification se résume ainsi :
|
|
||||||
|
|
||||||
1. **Préparation** :
|
|
||||||
Le client dispose de l’URL iCal sécurisée (obtenue comme décrit ci-dessus).
|
|
||||||
|
|
||||||
2. **Requête HTTP** :
|
|
||||||
Le client effectue une requête HTTP GET :
|
|
||||||
```
|
|
||||||
GET {ical-url}
|
|
||||||
Accept: text/calendar, */*;q=0.5
|
|
||||||
User-Agent: {identifiant-client}
|
|
||||||
```
|
|
||||||
- Délai d’attente : configurable (recommandé : 20 secondes).
|
|
||||||
|
|
||||||
3. **Validation de la réponse** :
|
|
||||||
- Si le code HTTP n’est pas 2xx → échec d’authentification ou erreur serveur.
|
|
||||||
- Si le corps de la réponse ne contient pas `BEGIN:VCALENDAR` → le jeton est probablement expiré ou l’URL est invalide. Le serveur peut retourner une page HTML d’erreur au lieu des données de calendrier.
|
|
||||||
|
|
||||||
4. **Traitement** :
|
|
||||||
Si la validation réussit, le corps de la réponse est une donnée iCalendar valide, prête à être analysée.
|
|
||||||
|
|
||||||
### Données échangées
|
|
||||||
|
|
||||||
- Le client envoie une seule requête HTTP GET vers l'URL iCal sécurisée.
|
|
||||||
- En-têtes de requête : `Accept: text/calendar, */*;q=0.5` et `User-Agent: <identifiant-client>`.
|
|
||||||
- Le serveur retourne une charge utile iCalendar (RFC 5545) si le jeton est valide.
|
|
||||||
- Si le jeton est invalide ou expiré, le serveur retourne une réponse non-calendrier (typiquement du HTML).
|
|
||||||
- Aucun cookie, jeton de session ou en-tête d'authentification n'est échangé — l'URL **est** la crédentiale.
|
|
||||||
|
|
||||||
### Identifiants et jetons
|
|
||||||
|
|
||||||
Le seul identifiant utilisé est le jeton `icalsecurise`, intégré directement dans l’URL. Ce jeton est :
|
|
||||||
- **Unique** par utilisateur et par établissement.
|
|
||||||
- **Sensible** : il équivaut à un mot de passe et doit être traité comme tel.
|
|
||||||
- **Transmis en clair** dans la chaîne de requête de l’URL, mais protégé en transit par HTTPS.
|
|
||||||
- **Autosuffisant** : aucune autre information (nom d’utilisateur, mot de passe, cookie de session ou jeton OAuth) n’est requise. L’URL **est** l’authentification.
|
|
||||||
|
|
||||||
### Cycle de vie
|
### Cycle de vie
|
||||||
|
- **Pérennité** : Le jeton reste valide jusqu'à :
|
||||||
|
- Révocation manuelle par l'utilisateur dans Pronote.
|
||||||
|
- Régénération par l'établissement (ex. à la rentrée scolaire).
|
||||||
|
⚠️ **Hypothèse à valider** : La durée exacte dépend des politiques de l'établissement (non documentée officiellement).
|
||||||
|
|
||||||
Le jeton est **pérenne** : il reste valide jusqu’à sa révocation manuelle par l’utilisateur dans les paramètres Pronote, ou jusqu’à sa régénération par l’établissement (généralement à la rentrée scolaire).
|
🔹 **Source** : ⚠️ Comportement variable selon les instances (à tester localement).
|
||||||
Aucun mécanisme de rafraîchissement automatique ou de rotation n’existe. En cas d’expiration ou de rotation, le serveur retourne une réponse non-iCalendar (souvent une page HTML mentionnant *« Session expirée »*). Le client détecte cette situation en vérifiant l’absence de `BEGIN:VCALENDAR` dans le corps de la réponse.
|
|
||||||
|
|
||||||
La récupération nécessite une **réextraction manuelle** d’une nouvelle URL iCal depuis l’interface Pronote.
|
|
||||||
**Bonnes pratiques** : tester l’URL avant chaque rentrée et la régénérer proactivement si l’établissement est connu pour rotater les jetons à cette période.
|
|
||||||
|
|
||||||
### Sécurité
|
### Sécurité
|
||||||
|
1. **Surface d'attaque** : Le jeton est encodé dans l'URL → risque d'exposition via :
|
||||||
|
- Logs serveur/proxy.
|
||||||
|
- En-tête `Referer`.
|
||||||
|
- Historique du navigateur.
|
||||||
|
2. **Recommandations** :
|
||||||
|
- **Ne jamais versionner** l'URL (ex. dans `.env` ou Git).
|
||||||
|
- Utiliser **HTTPS** (obligatoire).
|
||||||
|
- Masquer l'URL dans les logs (ex. via `redact_url()` dans `pronote-sync`).
|
||||||
|
|
||||||
1. **Surface d’attaque** : Le jeton est encodé dans l’URL. Il peut être exposé via les logs d’accès du serveur, les logs proxy, les en-têtes `Referer`, l’historique du navigateur ou une interception réseau (atténué par HTTPS).
|
🔹 **Source** : ⚠️ Recommandation du projet (inspirée des bonnes pratiques générales de sécurité).
|
||||||
2. **Exposition des identifiants** : En cas de fuite, le jeton accorde un accès en lecture à l’emploi du temps (et aux devoirs, si inclus) jusqu’à sa rotation par l’établissement.
|
|
||||||
3. **Résistance au rejeu** : Faible — absence de *nonce*, de validation temporelle ou de protection contre le *replay*. Toute entité disposant de l’URL peut récupérer les données à tout moment.
|
|
||||||
4. **Rotation** : Manuellement uniquement, via la régénération de l’URL dans Pronote. L’établissement contrôle les réinitialisations côté serveur.
|
|
||||||
5. **Recommandations** : Conserver l’URL comme un secret (jamais en contrôle de version). Utiliser HTTPS (par défaut). Limiter l’accès à l’URL en besoin d’en connaître. Rotater proactivement aux changements d’année scolaire. Masquer l’URL dans les messages d’erreur et les logs pour éviter les fuites accidentelles.
|
|
||||||
|
|
||||||
### Limitations
|
### Intégration dans `pronote-sync`
|
||||||
|
- **Paramètre** : `PRONOTE_ICAL_URL` (ex. `.env.example` ligne 2).
|
||||||
|
- **Comportement** :
|
||||||
|
- Prioritaire en mode `PRONOTE_AGENDA_SOURCE=auto`.
|
||||||
|
- Si l'URL est invalide ou expire, repli automatique vers `pronotepy` (si `PRONOTE_AGENDA_SOURCE=auto`).
|
||||||
|
- **Aucun repli** si `PRONOTE_AGENDA_SOURCE=ical` (échec explicite).
|
||||||
|
|
||||||
- **Périmètre des données** : limité à l’emploi du temps et, si inclus par l’établissement, aux devoirs (*cahier de textes*).
|
🔹 **Source** : 🔍 `pronote_sync/config/settings.py` + `pronote_sync/sources/ical.py`.
|
||||||
- **Accès en lecture seule** : aucune modification possible.
|
|
||||||
- **Exclusions** : notes, absences, messagerie, bulletins ou paramètres sont inaccessibles.
|
|
||||||
- **Latence** : l’export iCal peut présenter un délai de mise à jour (plusieurs heures) avant de refléter les modifications Pronote.
|
|
||||||
- **Rafraîchissement** : impossible par programmation — une intervention manuelle est toujours requise.
|
|
||||||
|
|
||||||
### Statut
|
---
|
||||||
|
|
||||||
**Officiel** — L’export iCal est une fonctionnalité supportée par Pronote, éditée par Index Éducation. Le mécanisme de jeton fait partie intégrante du produit, bien que son format interne et sa logique de génération ne soient pas documentés publiquement.
|
## Méthode 2 : Connexion directe (identifiant / mot de passe + ENT)
|
||||||
|
|
||||||
## Méthode 2 : Connexion directe (identifiant / mot de passe)
|
|
||||||
|
|
||||||
### Principe général
|
### Principe général
|
||||||
La connexion directe utilise un protocole propriétaire de type JSON sur HTTP(S), sécurisé par un chiffrement AES-256-CBC spécifique à la session et un mécanisme de défi-réponse. Il s’agit du protocole natif de Pronote, tel qu’utilisé par son interface web.
|
Connexion via le protocole propriétaire de Pronote (JSON sur HTTPS), avec **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.
|
||||||
|
|
||||||
### Flux détaillé
|
✅ **Statut** : **Observé dans le code local** (délégable à pronotepy via `ParentClient` ou `Client`).
|
||||||
|
|
||||||
**Étape 1 — Initialisation de la session (`GET /pronote/<espace>.html`)**
|
### Flux d'authentification
|
||||||
Le client effectue une requête GET vers l’espace cible (ex. `/pronote/eleve.html` pour un élève, `/pronote/parent.html` pour un parent). La réponse HTML contient un gestionnaire JavaScript `onload` avec les paramètres de session :
|
1. **Initialisation** : Requête GET vers `/pronote/{espace}.html` (ex. `parent.html`).
|
||||||
|
- Récupère `h` (ID de session), `a` (ID espace), `sCrA`/`sCoA` (drapeaux chiffrement/compression).
|
||||||
|
2. **Échange de clés** : Requête POST vers `/pronote/appelfonction/{a}/{h}/{numeroOrdre}`.
|
||||||
|
3. **Identification** : Soumission de `identifiant`, `genreConnexion=0`, `genreEspace={a}`.
|
||||||
|
4. **Résolution du défi** :
|
||||||
|
- Calcul de `mtp = MAJUSCULE(HEX(SHA256(alea + mot_de_passe)))`.
|
||||||
|
- Dérivation de `key_challenge = MD5(nom_utilisateur + mtp)`.
|
||||||
|
- Déchiffrement du `challenge` avec **AES-CBC utilisant une clé dérivée par MD5 (16 octets, soit AES-128)**.
|
||||||
|
5. **Authentification** : Soumission de la réponse au défi.
|
||||||
|
|
||||||
|
🔹 **Source** : 🔍 Observé dans le code local de `pronotepy` 2.15.7 (module `clients.py` et `pronoteAPI.py`, vérifié le 2026-09-12).
|
||||||
|
|
||||||
|
### Intégration dans `pronote-sync`
|
||||||
|
- **Paramètres** :
|
||||||
|
- `PRONOTE_URL` (ex. `.env.example` ligne 3).
|
||||||
|
- `PRONOTE_USERNAME`, `PRONOTE_PASSWORD`.
|
||||||
|
- `PRONOTE_ENT` (slug dans `_ENT_NAMES`).
|
||||||
|
- `PRONOTE_ACCOUNT_TYPE` (ex. `parent`).
|
||||||
|
- **Comportement** :
|
||||||
|
- Utilise `pronotepy.ParentClient` pour les comptes parents.
|
||||||
|
- **Repli** : Si iCal échoue en mode `auto`, `pronotepy` est utilisé.
|
||||||
|
- **Pas de repli** si `PRONOTE_AGENDA_SOURCE=pronotepy` (échec explicite).
|
||||||
|
|
||||||
|
🔹 **Source** : 🔍 `pronote_sync/sources/pronote/client.py` (méthode `_connect_password`).
|
||||||
|
|
||||||
|
### ENT supportés
|
||||||
|
Liste **fermée** des **30 ENT** résolubles (lignes 52–83 de `pronote_sync/sources/pronote/client.py`) :
|
||||||
|
```python
|
||||||
|
_monbureaunumerique, ent_elyco, bordeaux, ent_creuse, occitanie_montpellier, ...
|
||||||
```
|
```
|
||||||
Session initialization parameters:
|
|
||||||
h = <session_id>
|
|
||||||
a = <espace_id> (3 = Élève, 7 = Parent)
|
|
||||||
sCrA = <encryption_flag>
|
|
||||||
sCoA = <compression_flag>
|
|
||||||
```
|
|
||||||
- `h` : identifiant de session (chaîne ou nombre unique).
|
|
||||||
- `a` : identifiant de l’espace (`3` pour Élève, `7` pour Parent).
|
|
||||||
- `sCrA` / `sCoA` : indicateurs de chiffrement et de compression.
|
|
||||||
|
|
||||||
**Étape 2 — Échange de clés (`POST /pronote/appelfonction/<espace_id>/<session_id>/<numeroOrdre>`)**
|
⚠️ **Hypothèse à valider** : Les ENT non listés nécessitent une contribution à `pronotepy`.
|
||||||
Le client envoie une charge utile `FonctionParametres` contenant `donneesSec.donnees.Uuid` :
|
|
||||||
- En HTTPS : un IV AES de 16 octets encodé en base64.
|
|
||||||
- En HTTP non sécurisé : un IV chiffré avec RSA-1024.
|
|
||||||
- `numeroOrdre` est un compteur incrémental (début à 1).
|
|
||||||
- Pour la première requête, `numeroOrdre` est chiffré avec AES-256-CBC, une clé vide MD5 (`d41d8cd98f00b204e9800998ecf8427e`) et un IV nul.
|
|
||||||
- Pour les requêtes suivantes, l’IV de session (issu de `Uuid`) est utilisé.
|
|
||||||
|
|
||||||
**Étape 3 — Identification (`POST ... / Identification`)**
|
🔹 **Source** : 🔍 Code source local de `pronote-sync` (2026-09-12).
|
||||||
Le client soumet une charge utile JSON :
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"nom": "Identification",
|
|
||||||
"session": "<session_id>",
|
|
||||||
"numeroOrdre": "<compteur_chiffré>",
|
|
||||||
"donneesSec": {
|
|
||||||
"donnees": {
|
|
||||||
"identifiant": "<nom_utilisateur>",
|
|
||||||
"genreConnexion": 0,
|
|
||||||
"genreEspace": <espace_id>,
|
|
||||||
"pourENT": false,
|
|
||||||
"enConnexionAuto": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Le serveur retourne : `alea` (sel aléatoire), `challenge` (chaîne hexadécimale), `modeCompLog` et `modeCompMdp` (indicateurs de normalisation de casse).
|
|
||||||
|
|
||||||
**Étape 4 — Résolution du défi**
|
---
|
||||||
Le client calcule le hachage du mot de passe :
|
|
||||||
```
|
|
||||||
mtp = MAJUSCULE(HEX(SHA256(alea + mot_de_passe_utilisateur)))
|
|
||||||
```
|
|
||||||
La clé de déchiffrement du défi est dérivée :
|
|
||||||
```
|
|
||||||
key_challenge = MD5(nom_utilisateur + mtp)
|
|
||||||
```
|
|
||||||
Le client déchiffre `challenge` avec AES-256-CBC, `key_challenge` et l’IV de session. Il supprime ensuite un caractère sur deux dans le texte en clair (ex. `abcdef` → `ace`), puis rechiffre la chaîne modifiée avec les mêmes paramètres AES et l’encode en hexadécimal.
|
|
||||||
|
|
||||||
**Étape 5 — Authentification (`POST ... / Authentification`)**
|
## Méthode 3 : QR Code + token mobile (pour `pronote-sync`)
|
||||||
Le client envoie la réponse au défi via la fonction `Authentification`. Le serveur retourne les métadonnées utilisateur et une chaîne `cle` (entiers séparés par des virgules). Le client déchiffre `cle`, analyse les octets et calcule `MD5(octets)` pour obtenir la clé principale de chiffrement de session pour toutes les appels API ultérieurs.
|
|
||||||
|
|
||||||
### Données échangées
|
|
||||||
Identifiant de session, identifiant d’espace, IV AES (base64 ou chiffré RSA), compteur `numeroOrdre`, sel `alea`, chaîne de défi `challenge`, clé de session (`cle`). Toutes les données sensibles sont chiffrées en transit (AES-256-CBC).
|
|
||||||
|
|
||||||
### Identifiants et jetons
|
|
||||||
- **Identifiants** : nom d’utilisateur Pronote et mot de passe attribués par l’établissement.
|
|
||||||
- **Jetons de session** : identifiant de session (`h`), compteur de séquence (`numeroOrdre`), clé symétrique AES-256 (dérivée de `cle`).
|
|
||||||
|
|
||||||
### Cycle de vie
|
|
||||||
- Les identifiants de session expirent après une courte période d’inactivité (généralement 15 à 30 minutes).
|
|
||||||
- La clé de session doit être utilisée pour tous les appels API ultérieurs dans la même session.
|
|
||||||
- Une réauthentification est nécessaire après expiration de la session.
|
|
||||||
|
|
||||||
### Sécurité
|
|
||||||
1. **Surface d’attaque** : protocole propriétaire avec cryptographie personnalisée. Le point de terminaison de connexion est accessible depuis Internet. Les attaques MITM sont atténuées par HTTPS (mais RSA-1024 est utilisé pour l’échange d’IV en HTTP non sécurisé, ce qui est faible selon les normes modernes).
|
|
||||||
2. **Exposition des identifiants** : le nom d’utilisateur est transmis dans la requête d’identification (chiffré). Le mot de passe n’est jamais transmis en clair : seule la réponse au défi est envoyée.
|
|
||||||
3. **Résistance au rejeu** : modérée. Le mécanisme de défi-réponse utilise un sel aléatoire (`alea`) par session, rendant difficile le rejou d’une réponse de défi capturée. Cependant, le compteur `numeroOrdre` doit être géré avec soin pour éviter toute manipulation de séquence.
|
|
||||||
4. **Rotation** : aucune rotation automatique des jetons. La rotation des mots de passe dépend de la politique de l’établissement. Les clés de session expirent avec la session.
|
|
||||||
5. **Recommandations** : utiliser systématiquement HTTPS. Implémenter une gestion rigoureuse de `numeroOrdre`. Stocker les identifiants de manière sécurisée. Noter que la cryptographie personnalisée n’est pas équivalente à une authentification TLS standard : s’appuyer sur HTTPS pour la sécurité du transport.
|
|
||||||
|
|
||||||
### Limitations
|
|
||||||
- La plupart des établissements secondaires français liés à un ENT ou à EduConnect bloquent les connexions directes par nom d’utilisateur/mot de passe et imposent le SSO.
|
|
||||||
- Le protocole propriétaire n’est pas officiellement documenté et peut changer sans préavis.
|
|
||||||
- La cryptographie personnalisée (AES-256-CBC avec des clés dérivées de MD5) est non standard et n’a pas fait l’objet d’un audit indépendant.
|
|
||||||
|
|
||||||
### Statut
|
|
||||||
**Rétro-conçu** — Index Éducation ne publie pas le protocole. Toutes les connaissances proviennent de l’analyse communautaire du client web Pronote en JavaScript.
|
|
||||||
|
|
||||||
## Méthode 3 : ENT (Espace Numérique de Travail)
|
|
||||||
|
|
||||||
### Principe général
|
### Principe général
|
||||||
La plupart des collèges et lycées français accèdent à Pronote via un ENT (Espace Numérique de Travail) régional ou départemental. L’ENT agit comme fournisseur d’identité (IdP) : l’utilisateur s’authentifie auprès de l’ENT, qui établit ensuite une session avec Pronote par SSO (Single Sign-On). Pronote reçoit des identifiants délégués sans gérer directement la connexion.
|
Mécanisme d'appairage par QR code pour les appareils mobiles, **contournant l'authentification ENT/EduConnect**. Le QR code est généré **depuis l'interface web Pronote, espace parent → paramètres → QR code**, puis utilisé avec un **PIN à 4 chiffres** pour obtenir un token dont la durée dépend de l'instance/serveur.
|
||||||
|
|
||||||
### Flux détaillé
|
✅ **Statut** : **Pris en charge** par `pronote-sync` (mode `PRONOTE_AUTH_MODE=qr_token`).
|
||||||
1. **Connexion ENT** : L’utilisateur soumet ses identifiants à l’endpoint de connexion spécifique à l’ENT. Chaque ENT utilise son propre mécanisme (formulaire, CAS, SAML, Keycloak, etc.).
|
|
||||||
|
|
||||||
2. **Redirection SSO vers Pronote** : L’ENT redirige la session authentifiée vers Pronote via un lien connecteur ou une URL proxy (ex. `/cas/proxySSO/...` ou un lien SAML direct). L’ENT valide la session et redirige vers l’instance Pronote de l’établissement avec des cookies ou assertions SAML valides.
|
### Procédure QR pour `pronote-sync`
|
||||||
|
|
||||||
3. **Handshake Pronote** : La réponse HTML initiale de Pronote contient des identifiants temporaires dans l’attribut `onload` du `<body>` :
|
#### Étape 1 : Génération du QR code (interface web)
|
||||||
|
1. Se connecter à Pronote via un navigateur (espace **Parent**).
|
||||||
|
2. Aller dans **Paramètres → Accès mobile / Application mobile**.
|
||||||
|
3. Définir un **PIN temporaire à 4 chiffres**.
|
||||||
|
4. Pronote affiche un **QR code** contenant un JSON avec les clés :
|
||||||
|
`login`, `jeton`, et `url` (ex. `https://[host]/pronote/mobile.parent.html`).
|
||||||
|
**Le fichier JSON réel contient des identifiants chiffrés et ne doit jamais être copié, partagé, ou committé.**
|
||||||
|
5. **Exporter le QR code** :
|
||||||
|
- Sauvegardez le fichier JSON localement ou scannez-le avec un appareil.
|
||||||
|
- **Ne jamais partager** le JSON ou le PIN.
|
||||||
|
- **Le fichier JSON du QR code contient des identifiants chiffrés : ne jamais le coller dans la documentation ni le committer.**
|
||||||
|
|
||||||
|
🔹 **Source** : 🔍 Observé localement dans l'interface web Pronote (version non spécifiée, hypothèse à valider).
|
||||||
|
|
||||||
|
#### Étape 2 : Configuration de `pronote-sync`
|
||||||
|
1. **Enregistrer le QR code** :
|
||||||
|
- Sauvegarder le JSON dans un fichier (ex. `/path/to/qr_code.json`).
|
||||||
|
- **Permissions** : `chmod 600 /path/to/qr_code.json`.
|
||||||
|
2. **Configurer `.env`** :
|
||||||
|
```ini
|
||||||
|
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)
|
||||||
```
|
```
|
||||||
Session initialization parameters:
|
**⚠️ Note** : `.env.example` ne doit **jamais** contenir de PIN concret.
|
||||||
e = <temp_login>
|
|
||||||
f = <temp_auth_token>
|
|
||||||
...
|
|
||||||
```
|
|
||||||
- `e` : chaîne de connexion temporaire (unique par session SSO).
|
|
||||||
- `f` : jeton d’authentification temporaire.
|
|
||||||
|
|
||||||
4. **Challenge de session** : Le client exécute la requête standard `Identification` de Pronote (comme en Méthode 2) avec `pourENT: true`. Le challenge est résolu via une dérivation simplifiée :
|
🔹 **Source** : 🔍 Observé dans `.env.example` (lignes 21–25) et `pronote_sync/sources/pronote/client.py`.
|
||||||
```
|
|
||||||
key_ENT = MD5(UPPERCASE(HEX(SHA256(f))))
|
|
||||||
```
|
|
||||||
Cela remplace la dérivation `MD5(username + mtp)` utilisée en connexion directe. Aucun mot de passe utilisateur n’est nécessaire : le jeton `f` émis par l’ENT sert de justificatif.
|
|
||||||
|
|
||||||
|
#### Étape 3 : Premier login (enrôlement)
|
||||||
|
1. `pronote-sync` lit `PRONOTE_QR_CODE_FILE` et `PRONOTE_QR_PIN`.
|
||||||
|
2. Appel à `pronotepy.ParentClient.qrcode_login(qr_code, pin, uuid)` :
|
||||||
|
- `qr_code` : JSON du fichier QR.
|
||||||
|
- `pin` : PIN à 4 chiffres.
|
||||||
|
- `uuid` : UUID permanent généré par `pronote-sync` (ex. `pronote-sync-{uuid4()}`).
|
||||||
|
3. **Déchiffrement** :
|
||||||
|
- Algorithme : **AES-CBC utilisant une clé dérivée par MD5 (16 octets, soit AES-128)**.
|
||||||
|
- Clé : `MD5(PIN)` (dérivée du PIN secret).
|
||||||
|
- IV : 16 octets nuls.
|
||||||
|
- Résultat : `login` et `jeton` en clair.
|
||||||
|
4. **Échange de token** : Pronote retourne un `jetonConnexionAppliMobile` (token dont la durée dépend de l'instance/serveur).
|
||||||
|
5. **Persistance** : `pronote-sync` sauvegarde les credentials via `export_credentials()` dans `.pronote_auth_state.json` (mode `0600`).
|
||||||
|
|
||||||
### Architectures ENT prises en charge
|
🔹 **Source** : 🔍 Observé dans le code local de `pronotepy` 2.15.7 (`clients.py` lignes 181–189) + `pronote_sync/sources/pronote/client.py` (méthode `_enroll_qr_code`).
|
||||||
|
|
||||||
| Architecture | Exemples | Mécanisme SSO |
|
#### Étape 4 : Connexions ultérieures (auto-login)
|
||||||
|---|---|---|
|
1. `pronote-sync` charge `.pronote_auth_state.json`.
|
||||||
| Open ENT NG / Open Digital Education | ent.iledefrance.fr, Paris Classe Numérique, Mon Collège Val d’Oise, L’Éduc de Normandie | SAML / redirection |
|
2. Appel à `pronotepy.ParentClient.token_login(**credentials)` :
|
||||||
| Kosmos / Skolengo CAS | Mon Bureau Numérique, Mon-ENT-Occitanie, Cybercollèges42 | CAS |
|
- `pronote_url`, `username`, `password` (token), `uuid`.
|
||||||
| Oze ENT | (divers) | Keycloak avec endpoints proxy `/v1/ozapps` |
|
3. **Rotation du token** : Le token est **remplacé uniquement si le serveur renvoie `jetonConnexionAppliMobile`** (observé dans `pronotepy` 2.15.7, `clients.py:382–387`).
|
||||||
| WAYF / Shibboleth | e-lyco (Pays de la Loire) | SAML / Shibboleth |
|
4. **Persistance** : Les credentials sont sauvegardés dans `.pronote_auth_state.json` après chaque opération réussie.
|
||||||
| Portails personnalisés | Atrium Sud, LaClasse Lyon | Formulaires simples |
|
|
||||||
|
|
||||||
|
🔹 **Source** : 🔍 Observé dans le code local de `pronotepy` 2.15.7 (`clients.py` lignes 245–280) + `pronote_sync/sources/pronote/client.py` (méthode `_connect_qr_token`).
|
||||||
|
|
||||||
### Données échangées
|
### Cycle de vie des tokens
|
||||||
Cookies/assertions de session ENT → identifiants temporaires Pronote (`e`, `f`) → session Pronote (via challenge-response avec clé dérivée de l’ENT).
|
- **QR code** : Valide **~10 minutes** après génération (⚠️ **Hypothèse à valider** : cette durée n'est attestée que par un message d'exception dans `pronotepy` et n'est pas une garantie officielle/indépendante de l'instance).
|
||||||
|
- **`jetonConnexionAppliMobile`** :
|
||||||
|
- Durée **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).
|
||||||
### Identifiants et jetons
|
- **Remplacé uniquement si le serveur renvoie `jetonConnexionAppliMobile`** (observé dans `pronotepy` 2.15.7, `clients.py:382–387`).
|
||||||
- **Identifiants principaux** : nom d’utilisateur et mot de passe ENT (spécifiques à chaque plateforme ENT).
|
- **Révocable** manuellement dans Pronote (Paramètres → Accès mobile).
|
||||||
- **Identifiants délégués** : `e` (connexion temporaire) et `f` (jeton) émis par Pronote après la redirection SSO.
|
|
||||||
- **Jeton de session** : clé AES-256 standard de Pronote (identique à la Méthode 2, dérivée après résolution du challenge).
|
|
||||||
|
|
||||||
|
|
||||||
### Cycle de vie
|
|
||||||
- Les sessions ENT sont temporaires : leur durée dépend des politiques de chaque plateforme.
|
|
||||||
- La session Pronote établie via l’ENT suit le même cycle qu’une session en connexion directe (timeout d’inactivité ~15–30 min).
|
|
||||||
- Les tâches automatisées récurrentes doivent se réauthentifier régulièrement auprès de l’ENT.
|
|
||||||
|
|
||||||
|
🔹 **Source** : ⚠️ Comportement variable selon les instances (à tester localement).
|
||||||
|
|
||||||
### Sécurité
|
### Sécurité
|
||||||
1. **Surface d’attaque** : Les chaînes de redirection multiples (ENT → Pronote) augmentent la surface d’attaque. Chaque redirection est une opportunité d’interception de jetons.
|
1. **Fichiers sensibles** :
|
||||||
2. **Exposition des identifiants** : Les identifiants utilisateur n’atteignent jamais Pronote directement. L’ENT agit comme intermédiaire de confiance. Le jeton `f` est éphémère (valide uniquement pour l’établissement initial de la session).
|
- `.pronote_auth_state.json` : **Ne jamais versionner** (couvert par `.gitignore`).
|
||||||
3. **Résistance au rejeu** : Le mécanisme challenge-response (identique à la Méthode 2) offre une résistance au rejeu pour la session Pronote. Les jetons de redirection SSO sont à usage unique.
|
- `PRONOTE_QR_CODE_FILE` : **Ne jamais committer** (ex. dans Git).
|
||||||
4. **Rotation** : Le cycle de vie de la session ENT contrôle la rotation des identifiants. La clé de session Pronote est rotative par session.
|
2. **Secrets** :
|
||||||
5. **Recommandations** : Valider les certificats SSL à chaque étape de redirection. Ne pas journaliser les jetons intermédiaires. Les formulaires de connexion ENT évoluent fréquemment, ce qui peut rompre les clients automatisés.
|
- Le PIN et le contenu du QR code sont **masqués** dans les logs (via `redact_secrets()`).
|
||||||
|
- Les exceptions sont **expurgées** (via `redact_exception()`).
|
||||||
|
3. **Recommandations** :
|
||||||
|
- Utiliser un **PIN robuste** (éviter les codes simples comme `0000` ou des séquences évidentes).
|
||||||
|
- **Révoquer** le token en cas de compromission (via Pronote web).
|
||||||
|
|
||||||
|
🔹 **Source** : 🔍 `pronote_sync/utils/redaction.py` + `pronote_sync/sources/pronote/client.py` (méthode `_collect_auth_secrets`).
|
||||||
|
|
||||||
### Limitations
|
### Erreurs et repli
|
||||||
- Les modifications des formulaires web de connexion ENT, des endpoints SAML ou de l’authentification multifacteur (MFA) rompent souvent les clients automatisés sans interface.
|
- **`PronoteAuthRotationError`** : Levée si :
|
||||||
- Chaque ENT possède un flux de connexion différent : aucune automatisation universelle n’est possible.
|
- Le token persisté est **invalide/expiré**.
|
||||||
- Les sessions ENT sont temporaires ; les tâches automatisées récurrentes doivent se réauthentifier régulièrement.
|
- Le fichier QR ou le PIN est **manquant/invalide**.
|
||||||
- Certains ENT implémentent du MFA ou des CAPTCHA empêchant une automatisation complète.
|
- **Action requise** :
|
||||||
|
1. Supprimer `.pronote_auth_state.json`.
|
||||||
|
2. Générer un **nouveau QR code depuis l'interface web Pronote, espace parent**.
|
||||||
|
3. Relancer `pronote-sync`.
|
||||||
|
|
||||||
|
🔹 **Source** : 🔍 Observé dans `pronote_sync/errors.py` + `pronote_sync/sources/pronote/client.py` (lignes 346–364).
|
||||||
|
|
||||||
### Statut
|
### Incompatibilités
|
||||||
Implémentation SSO standardisée par Index Éducation et les éditeurs d’ENT, mais la consommation du protocole par des clients tiers est **reverse-engineered**.
|
- **Mode `dry-run`** : **Incompatible** avec `PRONOTE_AUTH_MODE=qr_token` (risque de désynchronisation du token local).
|
||||||
|
- `pronote-sync --dry-run` **refuse** le mode `qr_token` avant toute connexion.
|
||||||
|
|
||||||
## Méthode 4 : EduConnect
|
🔹 **Source** : 🔍 `docs/exploitation.md` (ligne 65–66).
|
||||||
|
|
||||||
### Principe général
|
---
|
||||||
EduConnect est le service national d’authentification et de gestion des accès opéré par le Ministère de l’Éducation Nationale et de la Jeunesse (MENJ). Il agit comme fournisseur d’identité (IdP) pour les élèves et parents en France. L’authentification vers Pronote s’effectue selon deux modes, selon l’infrastructure de l’établissement.
|
|
||||||
|
|
||||||
### Flux détaillé
|
## Annexe A : Bibliothèques tierces
|
||||||
|
|
||||||
### Mode A : EduConnect via ENT régional
|
| Bibliothèque | Langage | Dépôt | Méthodes supportées | Statut dans `pronote-sync` |
|
||||||
1. Le client initie une requête vers la page de connexion de l’ENT régional avec le paramètre `selection=EDU_parent_eleve`.
|
|--------------|---------|-------|---------------------|-----------------------------|
|
||||||
2. L’ENT redirige vers l’endpoint SAML2 d’EduConnect :
|
| **pronotepy** | Python | [bain3/pronotepy](https://github.com/bain3/pronotepy) | Connexion directe, QR code/token, **30 ENT** (liste fermée) | ✅ **Dépendance principale** (version **2.15.7** vérifiée). |
|
||||||
```
|
| **pronote-api** | TypeScript | [Litarvan/pronote-api](https://github.com/Litarvan/pronote-api) | Connexion directe, CAS, ENT | ❌ Non utilisée. |
|
||||||
https://educonnect.education.gouv.fr/idp/profile/SAML2/Unsolicited/SSO
|
| **pronote-qrcode-api** | JavaScript | [Androz2091/pronote-qrcode-api](https://github.com/Androz2091/pronote-qrcode-api) | Déchiffrement QR code | ❌ Non utilisée (intégration native via `pronotepy`). |
|
||||||
```
|
|
||||||
3. Le client soumet les identifiants à EduConnect :
|
|
||||||
- `j_username` : identifiant EduConnect,
|
|
||||||
- `j_password` : mot de passe EduConnect,
|
|
||||||
- `_eventId_proceed` : chaîne vide.
|
|
||||||
4. EduConnect retourne un formulaire `SAMLResponse` signé, posté vers le service de consommation d’assertions de l’ENT (ex. `/Shibboleth.sso/SAML2/POST`).
|
|
||||||
5. L’ENT établit des cookies de session et redirige vers Pronote (le flux ENT→Pronote suit alors la Méthode 3).
|
|
||||||
|
|
||||||
### Mode B : HubEduConnect direct (SSO Index Éducation Cloud)
|
🔹 **Source** : 🔍 Observé dans `pyproject.toml` (dépendances) + code source local (2026-09-12).
|
||||||
Pour les établissements sans ENT régional :
|
|
||||||
1. Le client accède à la passerelle CAS centralisée d’Index Éducation :
|
|
||||||
```
|
|
||||||
https://hubeduconnect.index-education.net/EduConnect/cas/login?service=<URL_INSTANCE_PRONOTE>
|
|
||||||
```
|
|
||||||
2. La passerelle initie une requête SAML vers `educonnect.education.gouv.fr`.
|
|
||||||
3. L’utilisateur s’authentifie sur EduConnect (mêmes identifiants que le Mode A).
|
|
||||||
4. HubEduConnect reçoit l’assertion, la valide via une liste blanche, et redirige vers Pronote avec un ticket de service.
|
|
||||||
5. Pronote valide le ticket et établit la session (flux côté Pronote identique à la Méthode 2).
|
|
||||||
|
|
||||||
### Données échangées
|
---
|
||||||
- **Mode A** : Identifiants EduConnect → assertion SAML2 → cookies ENT → handshake SSO Pronote.
|
|
||||||
- **Mode B** : Identifiants EduConnect → assertion SAML2 → ticket CAS → session Pronote.
|
|
||||||
|
|
||||||
### Identifiants et jetons
|
|
||||||
- Identifiants principaux : identifiants nationaux EduConnect (gérés par le MENJ).
|
|
||||||
- Jetons intermédiaires : assertions SAML2 (Modes A/B), tickets de service CAS (Mode B).
|
|
||||||
- Jeton final : clé de session AES-256 Pronote (identique à la Méthode 2).
|
|
||||||
|
|
||||||
### Cycle de vie
|
|
||||||
- Les sessions EduConnect sont temporaires (durée définie par le MENJ).
|
|
||||||
- Le ticket CAS (Mode B) est à usage unique et consommé lors de la validation par Pronote.
|
|
||||||
- La session Pronote suit un timeout d’inactivité standard (~15–30 min).
|
|
||||||
|
|
||||||
### Sécurité
|
|
||||||
1. **Surface d’attaque** : Chaînes de redirections multiples (EduConnect → ENT/Hub → Pronote). Chaque redirection est un point d’interception. Les assertions SAML sont signées, réduisant les risques de contrefaçon.
|
|
||||||
2. **Exposition des identifiants** : Les identifiants EduConnect sont soumis directement à l’IdP EduConnect et ne transitent jamais vers Pronote ou l’ENT. Seule l’assertion SAML signée est transmise.
|
|
||||||
3. **Résistance au rejeu** : Les assertions SAML incluent des timestamps et sont à usage unique. Les tickets CAS le sont également.
|
|
||||||
4. **Rotation** : Gérée par le MENJ. Aucun mécanisme local.
|
|
||||||
5. **Recommandations** : Valider les signatures des assertions SAML. Ne pas mettre en cache les identifiants EduConnect. Les authentifications 2FA par SMS ou via FranceConnect ne sont pas gérables par des clients automatisés.
|
|
||||||
|
|
||||||
### Limitations
|
|
||||||
- Les authentifications 2FA par SMS ou FranceConnect ne sont pas contournables par des clients programmatiques.
|
|
||||||
- Le flux repose sur des redirections HTTP multiples, fragiles et sensibles à la gestion des cookies.
|
|
||||||
- Les identifiants nationaux sont hautement sensibles : leur compromission affecte tous les services éducatifs.
|
|
||||||
|
|
||||||
### Statut
|
|
||||||
**Service officiel (MENJ / Index Éducation)** — EduConnect est un service gouvernemental officiel. Cependant, l’interaction programmatique par des clients tiers relève du **reverse engineering**.
|
|
||||||
|
|
||||||
## Méthode 5 : CAS (Central Authentication Service)
|
|
||||||
|
|
||||||
### Principe général
|
|
||||||
CAS (Central Authentication Service) est le protocole SSO sous-jacent utilisé dans les réseaux scolaires propriétaires et les ENT régionaux pour autoriser l’accès à Pronote. Protocole standardisé (RFC 4520), il est implémenté côté serveur. Pronote délègue l’authentification au serveur CAS, sans gérer directement les identifiants.
|
|
||||||
|
|
||||||
### Flux détaillé
|
|
||||||
1. **Demande de ticket de service** :
|
|
||||||
Pronote redirige l’utilisateur vers le serveur CAS :
|
|
||||||
```
|
|
||||||
https://<cas-server>/login?service=https://<pronote-host>/pronote/<espace>.html
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Authentification CAS** :
|
|
||||||
L’utilisateur soumet ses identifiants (ou effectue une authentification fédérée via EduConnect) sur le formulaire CAS.
|
|
||||||
|
|
||||||
3. **Génération du ticket** :
|
|
||||||
CAS renvoie une redirection HTTP 302 vers Pronote avec un paramètre `ticket` :
|
|
||||||
```
|
|
||||||
https://<pronote-host>/pronote/<espace>.html?ticket=ST-XXXXX-cas
|
|
||||||
```
|
|
||||||
Le ticket, préfixé par `ST-` (Service Ticket), est à usage unique.
|
|
||||||
|
|
||||||
4. **Validation du ticket de service** :
|
|
||||||
Le backend Pronote contacte directement l’URL de validation CAS pour vérifier le ticket et obtenir les attributs utilisateur :
|
|
||||||
```
|
|
||||||
https://<cas-server>/serviceValidate?service=https://<pronote-host>/pronote/<espace>.html&ticket=ST-XXXXX-cas
|
|
||||||
```
|
|
||||||
Le serveur CAS retourne les attributs (ex. : code UAI de l’établissement, identifiant élève). Pronote génère la session active et injecte le contexte d’initialisation dans le HTML client (mécanisme `onload` identique aux Méthodes 2 et 3).
|
|
||||||
|
|
||||||
### Données échangées
|
|
||||||
Identifiants CAS → Validation serveur CAS → Ticket de service (`ST-XXXXX-cas`) → Réponse de validation (attributs utilisateur : UAI, identifiant élève) → Initialisation de la session Pronote.
|
|
||||||
|
|
||||||
### Identifiants et jetons
|
|
||||||
- **Identifiants principaux** : Nom d’utilisateur et mot de passe CAS (ou identifiants fédérés EduConnect).
|
|
||||||
- **Jeton** : Ticket de service CAS (`ST-`, à usage unique).
|
|
||||||
- **Jeton de session** : Clé de session AES-256 Pronote (identique à la Méthode 2).
|
|
||||||
|
|
||||||
### Cycle de vie
|
|
||||||
- Le ticket de service CAS est **à usage unique** : il est consommé lors de la validation et ne peut être réutilisé.
|
|
||||||
- La session Pronote résultante suit un timeout d’inactivité standard (~15–30 min).
|
|
||||||
- La durée de vie de la session CAS est régie par la politique de *Ticket-Granting Ticket* (TGT) du serveur CAS.
|
|
||||||
|
|
||||||
### Sécurité
|
|
||||||
1. **Surface d’attaque** : Protocole standardisé et documenté. La surface d’attaque concerne principalement le formulaire de connexion CAS et la transmission du ticket (protégée par HTTPS).
|
|
||||||
2. **Exposition des identifiants** : Les identifiants sont soumis uniquement au serveur CAS — Pronote ne les voit jamais. Seul le ticket de service est transmis à Pronote.
|
|
||||||
3. **Résistance au rejeu** : Élevée — les tickets de service sont à usage unique et liés à une URL de service spécifique.
|
|
||||||
4. **Rotation** : La rotation des TGT est gérée par la politique du serveur CAS. Les tickets de service expirent rapidement (généralement en quelques secondes ou minutes).
|
|
||||||
5. **Recommandations** : Utiliser systématiquement HTTPS. Valider le paramètre `service` pour éviter le vol de tickets via des URL de service malveillantes. Appliquer une gestion rigoureuse du cycle de vie des TGT.
|
|
||||||
|
|
||||||
### Limitations
|
|
||||||
- CAS est un protocole côté serveur : le serveur CAS doit être correctement configuré et accessible.
|
|
||||||
- L’appel `serviceValidate` s’effectue de serveur à serveur (backend Pronote → serveur CAS), nécessitant une connectivité réseau entre eux.
|
|
||||||
- Toutes les écoles n’utilisent pas CAS : certaines privilégient SAML ou des solutions SSO personnalisées.
|
|
||||||
|
|
||||||
### Statut
|
|
||||||
**Officiel** — CAS est un protocole standardisé (RFC 4520) officiellement implémenté côté backend Pronote et serveurs CAS.
|
|
||||||
|
|
||||||
## Méthode 6 : QR Code et jeton mobile
|
|
||||||
|
|
||||||
### Principe général
|
|
||||||
Pronote propose un mécanisme d’appairage par QR code pour connecter les appareils mobiles sans saisir les identifiants ENT complexes. L’utilisateur génère un QR code depuis l’interface web, définit un code PIN temporaire à 4 chiffres, puis le scanne avec l’application mobile. Le QR code contient des identifiants chiffrés qui, une fois déchiffrés, permettent un échange de jeton à longue durée de vie. Ce mécanisme contourne entièrement l’authentification ENT/EduConnect.
|
|
||||||
|
|
||||||
|
|
||||||
### Flux détaillé
|
|
||||||
|
|
||||||
**Phase 1 — Génération (interface web Pronote)**
|
|
||||||
1. Dans l’interface web, l’utilisateur accède à *Paramètres → Accès mobile / Application mobile*.
|
|
||||||
2. Il définit un code PIN temporaire à 4 chiffres.
|
|
||||||
3. Pronote affiche un QR code contenant un JSON chiffré :
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"login": "<AES_HEX_encrypted_username>",
|
|
||||||
"jeton": "<AES_HEX_encrypted_token>",
|
|
||||||
"url": "https://<host>/pronote/mobile.eleve.html"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Phase 2 — Déchiffrement**
|
|
||||||
- Algorithme : **AES-256-CBC**.
|
|
||||||
- IV : 16 octets nuls (`0x00...`).
|
|
||||||
- Clé : `MD5(PIN_code_string)` (ex. `MD5("1234")`).
|
|
||||||
- Résultat : `login` et `jeton` en clair.
|
|
||||||
|
|
||||||
**Phase 3 — Échange d’appairage initial**
|
|
||||||
1. Le client génère un UUID permanent (`uuidAppliMobile`).
|
|
||||||
2. Il envoie une requête à : `https://<host>/pronote/mobile.<espace>.html?login=true` (contourne la redirection ENT).
|
|
||||||
3. Requête `Identification` avec :
|
|
||||||
- `demandeConnexionAppliMobile: true`
|
|
||||||
- `demandeConnexionAppliMobileJeton: true`
|
|
||||||
- `uuidAppliMobile: "<DEVICE_UUID>"`
|
|
||||||
- `identifiant: "<decrypted_login>"`
|
|
||||||
4. Le défi est résolu avec `<decrypted_jeton>` comme mot de passe (mécanisme identique à la Méthode 2).
|
|
||||||
5. Le serveur retourne `jetonConnexionAppliMobile` (jeton à longue durée de vie).
|
|
||||||
|
|
||||||
**Phase 4 — Connexions ultérieures (auto-login)**
|
|
||||||
- `identifiant: <decrypted_login>`
|
|
||||||
- `uuidAppliMobile: <DEVICE_UUID>`
|
|
||||||
- `enConnexionAppliMobile: true`
|
|
||||||
- Mot de passe pour le défi : `jetonConnexionAppliMobile`
|
|
||||||
- À chaque connexion réussie, Pronote retourne un nouveau `jetonConnexionAppliMobile` à conserver.
|
|
||||||
|
|
||||||
|
|
||||||
### Données échangées
|
|
||||||
QR code JSON (login + jeton chiffrés + URL) → déchiffrement → `Identification` avec drapeaux mobiles → défi-réponse → `jetonConnexionAppliMobile`.
|
|
||||||
|
|
||||||
|
|
||||||
### Identifiants et jetons
|
|
||||||
- **Initial** : Code PIN à 4 chiffres (temporaire, utilisé uniquement pour le déchiffrement du QR code).
|
|
||||||
- **Déchiffrés** : `login` et `jeton` extraits du QR code (usage unique pour l’appairage initial).
|
|
||||||
- **Persistants** : `uuidAppliMobile` (UUID de l’appareil, permanent) + `jetonConnexionAppliMobile` (jeton à longue durée de vie, rafraîchi à chaque connexion).
|
|
||||||
|
|
||||||
|
|
||||||
### Cycle de vie
|
|
||||||
- Le QR code est valide **10 minutes** après génération.
|
|
||||||
- Le `jetonConnexionAppliMobile` reste valide indéfiniment (souvent toute l’année scolaire), sauf :
|
|
||||||
- Révoqué par l’utilisateur dans les paramètres Pronote.
|
|
||||||
- Invalidé côté serveur.
|
|
||||||
- Le jeton est rafraîchi à chaque connexion réussie.
|
|
||||||
|
|
||||||
|
|
||||||
### Sécurité
|
|
||||||
1. **Surface d’attaque** : Le QR code est affiché à l’écran (risque de *shoulder-surfing*). Le PIN à 4 chiffres offre 10 000 combinaisons. Le `jetonConnexionAppliMobile` est un identifiant à longue durée de vie stocké sur l’appareil.
|
|
||||||
2. **Exposition des identifiants** : Le QR code contient des identifiants chiffrés. Si intercepté avant déchiffrement, l’attaquant a besoin du PIN. Une fois le `jetonConnexionAppliMobile` obtenu, aucun PIN ni mot de passe n’est requis.
|
|
||||||
3. **Résistance au rejeu** : Le `jetonConnexionAppliMobile` est un *bearer token* : toute partie en possession du jeton peut s’authentifier. Le `uuidAppliMobile` offre un lien faible avec l’appareil, mais non vérifié cryptographiquement.
|
|
||||||
4. **Rotation** : Le `jetonConnexionAppliMobile` est rafraîchi à chaque connexion, mais l’ancien reste valide jusqu’à invalidation côté serveur. Aucune expiration automatique.
|
|
||||||
5. **Recommandations** : Utiliser un PIN robuste. Traiter le `jetonConnexionAppliMobile` comme un identifiant à longue durée de vie (stockage sécurisé). En cas de vol de l’appareil, révoquer l’accès mobile dans Pronote.
|
|
||||||
|
|
||||||
|
|
||||||
### Limitations
|
|
||||||
- Le QR code expire après **10 minutes** : l’appairage doit être rapide.
|
|
||||||
- Le PIN à 4 chiffres est faible selon les normes modernes.
|
|
||||||
- Le `jetonConnexionAppliMobile` n’a pas d’expiration automatique : il persiste jusqu’à révocation manuelle.
|
|
||||||
- Le `uuidAppliMobile` n’est pas lié cryptographiquement à l’appareil : il peut être copié.
|
|
||||||
|
|
||||||
|
|
||||||
### Statut
|
|
||||||
Mécanisme **officiellement intégré** à l’application mobile Index Éducation. L’utilisation par des clients tiers repose sur de l’**ingénierie inverse**.
|
|
||||||
|
|
||||||
## Méthode 7 : API officielle et application mobile
|
|
||||||
|
|
||||||
### Principe général
|
|
||||||
Index Éducation ne propose pas d’API publique pour Pronote. L’accès tiers aux données repose sur l’ingénierie inverse des mêmes endpoints JSON-over-HTTP(S) utilisés par les clients web et mobile officiels. L’application mobile officielle s’authentifie via le mécanisme d’appairage par QR Code (Méthode 6) et communique via le même protocole propriétaire que le client web.
|
|
||||||
|
|
||||||
### Flux détaillé
|
|
||||||
|
|
||||||
L'application mobile s'authentifie via le flux d'appairage par QR Code (Méthode 6) puis communique via le même protocole JSON-over-HTTP(S) que le client web, en utilisant les endpoints `/pronote/mobile.<espace>.html` et `/pronote/appelfonction/<espace_id>/<session_id>/<numeroOrdre>`.
|
|
||||||
|
|
||||||
### Disponibilité d'une API développeur
|
|
||||||
- **API publique** : Inexistante. Index Éducation ne propose ni API REST ni GraphQL pour les élèves, parents ou développeurs tiers.
|
|
||||||
- **API institutionnelle/entreprise** : Des services d’intégration propriétaires sont proposés pour les systèmes partenaires (ex. connecteurs UDTS, HYPERPLANNING, ENT officiels). Ceux-ci nécessitent des accords de partenariat signés et des licences serveurs institutionnelles.
|
|
||||||
|
|
||||||
### Authentification de l'application mobile officielle
|
|
||||||
L’application mobile officielle (iOS/Android) se connecte via les mêmes endpoints JSON-over-HTTP(S) que l’interface web mobile :
|
|
||||||
- Chemins de base : `/pronote/mobile.<espace>.html`
|
|
||||||
- Dispatcher de fonctions : `/pronote/appelfonction/<espace_id>/<session_id>/<numeroOrdre>`
|
|
||||||
- Authentification : Utilise le flux d’appairage par QR Code (Méthode 6) et le jeton mobile persistant (`jetonConnexionAppliMobile` + `uuidAppliMobile`).
|
|
||||||
|
|
||||||
### Données échangées
|
|
||||||
Mêmes charges utiles JSON chiffrées en AES-256-CBC que le client web (Méthode 2). La variante mobile (`mobile.<espace>.html`) contourne les redirections SSO ENT/EduConnect.
|
|
||||||
|
|
||||||
### Identifiants et jetons
|
|
||||||
- `jetonConnexionAppliMobile` : Jeton bearer à longue durée de vie (voir Méthode 6).
|
|
||||||
- `uuidAppliMobile` : UUID de l’appareil.
|
|
||||||
- Clé de session : Clé AES-256 (même dérivation que la Méthode 2).
|
|
||||||
|
|
||||||
### Cycle de vie
|
|
||||||
Le `jetonConnexionAppliMobile` est rafraîchi à chaque connexion. La durée de vie de la session suit le même délai d’inactivité (~15–30 min) que le client web.
|
|
||||||
|
|
||||||
### Sécurité
|
|
||||||
1. **Surface d’attaque** : Les endpoints mobiles sont accessibles publiquement. Aucune clé API ni enregistrement développeur n’existe — néanmoins, l’accès exige un matériel de session Pronote valide et, pour l’auto-login mobile, le matériel d’authentification correspondant (`login`, `uuidAppliMobile`, `jetonConnexionAppliMobile`) issu du flux d’appairage par QR Code (Méthode 6).
|
|
||||||
2. **Exposition des identifiants** : Le `jetonConnexionAppliMobile` est un jeton bearer stocké sur l’appareil. S’il est extrait, il accorde un accès complet jusqu’à révocation.
|
|
||||||
3. **Résistance au rejeu** : Faible — le jeton est basé sur un bearer. Le `uuidAppliMobile` offre un lien faible avec l’appareil, non appliqué cryptographiquement.
|
|
||||||
4. **Rotation** : Le jeton est rafraîchi à chaque connexion, mais les anciens restent valides. Aucune expiration automatique.
|
|
||||||
5. **Recommandations** : Évitez l’ingénierie inverse du protocole pour un usage en production sans comprendre les implications légales. Stockez les jetons mobiles de manière sécurisée. Soyez conscient que Index Éducation peut modifier le protocole à tout moment.
|
|
||||||
|
|
||||||
### Limitations
|
|
||||||
- Aucune documentation ou support officiel pour les développeurs tiers.
|
|
||||||
- Protocole propriétaire susceptible de changer sans préavis.
|
|
||||||
- Les implémentations basées sur l’ingénierie inverse peuvent cesser de fonctionner après les mises à jour de Pronote.
|
|
||||||
- Le statut légal de l’ingénierie inverse est incertain dans certaines juridictions.
|
|
||||||
|
|
||||||
### Statut
|
|
||||||
- API publique : **Inexistante**.
|
|
||||||
- Endpoints mobiles : **Ingénierie inverse** (même protocole que le client web, accessible via appairage QR Code).
|
|
||||||
|
|
||||||
## Annexe A : Bibliothèques open source tierces
|
|
||||||
|
|
||||||
Les bibliothèques open source ci-dessous implémentent les protocoles d'authentification Pronote décrits dans ce manuel. Ces projets sont maintenus par la communauté et ne sont pas affiliés à Index Éducation. Les fonctionnalités décrites reposent sur les informations publiques disponibles dans leurs dépôts et peuvent avoir évolué depuis la rédaction de ce document.
|
|
||||||
|
|
||||||
| Bibliothèque | Langage | Dépôt | Méthodes d'authentification prises en charge |
|
|
||||||
|---|---|---|---|
|
|
||||||
| **pronotepy** | Python | `bain3/pronotepy` | Connexion directe, jeton mobile / QR code, CAS SSO, EduConnect (HubEduConnect direct et via 30+ ENT régionaux : Open ENT NG, Skolengo, Oze, Shibboleth/WAYF). |
|
|
||||||
| **pronote-api** | TypeScript / JS | `Litarvan/pronote-api` | Connexion directe, CAS SSO, redirections ENT, authentification par jeton. |
|
|
||||||
| **pronote-qrcode-api** | JavaScript | `Androz2091/pronote-qrcode-api` | Implémentation de référence pour le déchiffrement des QR codes Pronote et l'échange initial de jeton mobile. |
|
|
||||||
| **pawnote / Blocksnote** | TypeScript / JS | `BlocksHub/Blocksnote` | Clients TypeScript modernes implémentant le protocole Pronote complet (direct, ENT, QR code, jetons). |
|
|
||||||
|
|
||||||
Ces bibliothèques illustrent la faisabilité des méthodes d'authentification présentées. Leur utilisation en environnement de production comporte des risques : les modifications de protocole par Index Éducation peuvent rendre les implémentations obsolètes sans préavis, et le statut juridique de l'ingénierie inverse des protocoles propriétaires varie selon les juridictions.
|
|
||||||
|
|
||||||
## Annexe B : Tableau comparatif synthétique
|
## Annexe B : Tableau comparatif synthétique
|
||||||
|
|
||||||
Cette annexe consolide les caractéristiques clés des 7 méthodes d'authentification Pronote en un tableau de référence unique, incluant les dimensions d'analyse de sécurité.
|
| Méthode | Périmètre | Identifiants | Expiration | Complexité | Statut dans `pronote-sync` |
|
||||||
|
|---------|-----------|--------------|------------|-----------|-----------------------------|
|
||||||
| Méthode | Périmètre | Identifiants | Expiration | Complexité | Surface d'attaque | Exposition | Rejeu | Rotation | Recommandation | Statut |
|
| **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 | ✅ Pris en charge |
|
||||||
|---------|-----------|--------------|------------|-----------|-------------------|-------------|-------|----------|----------------|--------|
|
| **Connexion directe (mot de passe + ENT)** | Agenda/EDT, devoirs, messages | Identifiant + mot de passe + ENT | Session dépendante de l'instance | Élevée | ✅ Pris en charge |
|
||||||
| **URL iCal sécurisée** | EDT + devoirs (si inclus) | Jeton URL | Longue durée | Très faible | Jeton dans URL (logs, referers) | Credential longue durée | Faible (pas de nonce) | Manuelle | Stocker en secret, HTTPS, rotate à la rentrée | Officiel |
|
| **QR Code + token mobile** | Agenda/EDT, devoirs, messages | PIN 4 chiffres → token + UUID | QR ~10 min (⚠️ hypothèse) ; token dépendant de l'instance | Modérée | ✅ Pris en charge |
|
||||||
| **Connexion directe** | Complet | Identifiant + mot de passe | Session ~15–30 min | Élevée | Endpoint public, crypto custom | Mot de passe non transmis (challenge) | Modérée (alea aléatoire) | Session seulement | HTTPS obligatoire, gérer `numeroOrdre` | Reverse-engineered |
|
| **SSO ENT (CAS/SAML)** | — | Identifiants ENT | Session ENT | Très élevée | ❌ Non implémenté (seuls les 30 ENT de la liste fermée `_ENT_NAMES` sont supportés via `pronotepy`) |
|
||||||
| **SSO ENT** | Complet | Identifiants ENT | Session ENT | Très élevée | Redirections multiples | Credentials via ENT (intermédiaire) | Jetons SSO single-use | Session ENT | Valider SSL à chaque redirection | Reverse-engineered |
|
| **EduConnect** | — | Identifiants nationaux | Session EduConnect | Très élevée | ❌ Non implémenté |
|
||||||
| **SSO EduConnect** | Complet | Identifiants nationaux | Session EduConnect | Très élevée | Redirections EduConnect→ENT/Hub | Credentials MENJ (très sensibles) | SAML single-use + timestamps | MENJ | Ne pas cacher les credentials, 2FA bloque automation | Reverse-engineered |
|
| **CAS direct** | — | Identifiants CAS | Ticket single-use | Modérée | ❌ Non implémenté |
|
||||||
| **CAS** | Complet | Identifiants CAS | Ticket single-use | Modérée | Login form CAS | Credentials via CAS uniquement | Ticket single-use | TGT (politique CAS) | Valider paramètre `service`, HTTPS | Officiel |
|
| **API publique** | N/A | N/A | N/A | N/A | ❌ Inexistante |
|
||||||
| **QR Code + jeton mobile** | Complet | PIN 4 chiffres → jeton + UUID | QR 10 min ; jeton longue durée | Modérée | QR écran, PIN faible, jeton bearer | Jeton longue durée stocké sur appareil | Faible (bearer) | À chaque login (ancien reste valide) | PIN fort, stockage sécurisé, révoquer si perte | Reverse-engineered |
|
|
||||||
| **API publique** | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | Inexistante |
|
|
||||||
|
|
||||||
**Légende** :
|
**Légende** :
|
||||||
- *Complet* : accès aux notes, emploi du temps, devoirs, absences, messagerie et paramètres.
|
- *Périmètre* : Les méthodes **iCal** sont en lecture seule (EDT + devoirs si inclus dans le flux). Les méthodes **Connexion directe** et **QR Code + token mobile** permettent les opérations `pronotepy` effectivement implémentées par ce projet (agenda, devoirs, messages ; informations ignorées en mode `qr_token`).
|
||||||
- *Reverse-engineered* : protocole non publié officiellement par Index Éducation, basé sur l'analyse communautaire.
|
- *QR ~10 min* : ⚠️ **Hypothèse non vérifiée** (observé dans `pronotepy` via un message d'exception, dépend de l'instance).
|
||||||
- *Officiel* : mécanisme fourni et pris en charge par Index Éducation.
|
- *Session dépendante de l'instance* : ⚠️ **Hypothèse non vérifiée** (nécessite un essai réel).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Annexe C : Écarts et notes de cohérence
|
||||||
|
|
||||||
|
### Alignement avec `.env.example`
|
||||||
|
| Paramètre | Document | Code | Statut |
|
||||||
|
|-----------|----------|------|--------|
|
||||||
|
| `PRONOTE_ICAL_URL` | ✅ Lignes 2, 42–50 | ✅ `sources/ical.py` | **Cohérent** |
|
||||||
|
| `PRONOTE_URL` | ✅ Ligne 3 | ✅ `client.py` (ligne 294) | **Cohérent** |
|
||||||
|
| `PRONOTE_ENT` | ✅ Ligne 7 | ✅ `client.py` (ligne 297, `_ENT_NAMES`) | **Cohérent** |
|
||||||
|
| `PRONOTE_AUTH_MODE=qr_token` | ✅ Ligne 23 | ✅ `client.py` (ligne 334) | **Cohérent** |
|
||||||
|
| `PRONOTE_QR_CODE_FILE` | ✅ Ligne 24 | ✅ `client.py` (ligne 387) | **Cohérent** |
|
||||||
|
| `PRONOTE_QR_PIN` | ✅ Ligne 25 | ✅ `client.py` (ligne 388) | **Cohérent** |
|
||||||
|
| *« Le QR code se génère sur le site web »* | ✅ Ligne 21 | ✅ Section [Procédure QR](#procédure-qr-pour-pronote-sync) | **Cohérent** |
|
||||||
|
|
||||||
|
### Écarts avec `docs/exploitation.md`
|
||||||
|
| Élément | `exploitation.md` | Ce document | Action |
|
||||||
|
|---------|-------------------|-------------|--------|
|
||||||
|
| *« Le QR code expire ~10 minutes »* | Ligne 22 | ⚠️ **Hypothèse non vérifiée** (section [Cycle de vie](#cycle-de-vie-des-tokens)) | **Aucune** (hors périmètre). |
|
||||||
|
| *« Mode `qr_token` incompatible avec dry-run »* | Lignes 65–66 | ✅ Section [Incompatibilités](#incompatibilités) | **Cohérent** |
|
||||||
|
| *« Fichier `.pronote_auth_state.json` (mode 0600) »* | Lignes 70–75 | ✅ Section [Sécurité](#sécurité) | **Cohérent** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Glossaire
|
||||||
|
|
||||||
|
| Terme | Définition |
|
||||||
|
|-------|------------|
|
||||||
|
| **ENT** | Espace Numérique de Travail (ex. Mon Bureau Numérique, Paris Classe Numérique). |
|
||||||
|
| **Jeton `icalsecurise`** | Token secret intégré dans l'URL iCal, équivalent à un mot de passe. |
|
||||||
|
| **`jetonConnexionAppliMobile`** | Token obtenu après appairage QR code, dont la durée dépend de l'instance/du serveur et n'est pas garantie. |
|
||||||
|
| **UUID** | Identifiant unique permanent pour l'application (ex. `pronote-sync-{uuid4()}`). |
|
||||||
|
| **PIN** | Code à 4 chiffres défini lors de la génération du QR code. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Historique des révisions
|
||||||
|
|
||||||
|
| Date | Auteur | Modifications |
|
||||||
|
|------|--------|---------------|
|
||||||
|
| 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 `jetonConnexionAppliMobile` comme dépendante de l'instance/serveur et non garantie.
|
||||||
|
|||||||
Binary file not shown.
@@ -9,8 +9,8 @@
|
|||||||
%%
|
%%
|
||||||
%% INSTRUCTIONS DE COMPILATION :
|
%% INSTRUCTIONS DE COMPILATION :
|
||||||
%% Ce document est conçu pour être compilé avec LuaLaTeX :
|
%% Ce document est conçu pour être compilé avec LuaLaTeX :
|
||||||
%% lualatex -interaction=nonstopmode docs/pronote-auth.tex
|
%% TEXINPUTS="docs:" lualatex -interaction=nonstopmode -output-directory=docs docs/pronote-auth.tex
|
||||||
%% lualatex -interaction=nonstopmode 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,
|
%% (Deux passes nécessaires pour la génération de la table des matières,
|
||||||
%% des références croisées et des hyperliens).
|
%% des références croisées et des hyperliens).
|
||||||
%%
|
%%
|
||||||
@@ -52,5 +52,9 @@
|
|||||||
|
|
||||||
\include{annex-libraries}
|
\include{annex-libraries}
|
||||||
\include{annex-comparison}
|
\include{annex-comparison}
|
||||||
|
\include{annex-ecarts}
|
||||||
|
|
||||||
|
\include{glossaire}
|
||||||
|
\include{historique}
|
||||||
|
|
||||||
\end{document}
|
\end{document}
|
||||||
|
|||||||
Reference in New Issue
Block a user