Starter 2 — Utilisateurs / authentification¶
Starter Forge · Niveau 2
Utilisateurs / authentification
Transformer le socle de sécurité Forge en petite application navigable : accueil public, connexion, dashboard protégé, profil simple et déconnexion.
-
Objectif
Comprendre les sessions, le CSRF et les routes publiques/protégées.
-
Niveau
Intermédiaire Forge. Le CRUD mono-entité du starter 1 est supposé compris.
-
Temps estimé
2 h à 3 h.
-
Résultat attendu
Login, dashboard protégé, profil simple et logout en
POST.
Génération automatique
Ce starter est maintenant générable avec forge starter:build 2, forge starter:build auth ou forge starter:build utilisateurs-auth. Il ne génère pas un CRUD utilisateur : il crée l'entité Utilisateur, copie les fichiers applicatifs d'authentification et injecte les routes explicites.
Prérequis¶
Prérequis généraux¶
- Python 3.11 ou supérieur
- Git
pipx(recommandé) ou environnement virtuel Python- MariaDB installé et démarré
- Accès à un compte administrateur MariaDB (pour
forge db:init) - Fichier
env/devconfiguré avec les identifiants MariaDB
Prérequis spécifiques au starter¶
- Starter 1 compris : routes, contrôleurs, formulaires, vues Jinja2, messages flash
- Projet Forge vierge ou base MariaDB déjà initialisée
- Module
core.security.hashingdisponible (inclus dans Forge) pour le hachage des mots de passe - Script
scripts/create_auth_user.pyfourni automatiquement parforge starter:build 2
Partie 1 — Installer Forge sur une VM Debian vierge¶
Si Forge est déjà installé et configuré sur votre machine, passez directement à la Partie 2 — Construire l'application starter.
La procédure complète est documentée sur la page Installation sur VM Debian vierge.
Elle couvre en 7 étapes : mise à jour du système, dépendances Python/MariaDB, Node.js optionnel, configuration de pipx, démarrage de MariaDB, vérification de l'accès administrateur et installation de Forge via pipx.
Une fois que forge --version s'affiche correctement, revenez ici pour construire l'application.
Partie 2 — Construire l'application starter¶
Présentation rapide¶
Objectif¶
Construire un flux applicatif minimal :
- une page d'accueil publique ;
- un formulaire de connexion public ;
- un dashboard accessible uniquement après connexion ;
- une page profil simple ;
- une déconnexion en
POST.
Le starter explique les sessions, le CSRF, les messages flash et la différence entre routes publiques et routes protégées. Il ne met pas en place de permissions multi-rôles.
Niveau¶
Niveau 2 — intermédiaire Forge.
Il suppose que le starter 1 est compris : routes, contrôleurs, formulaires, vues Jinja2 et flash. La nouveauté est la sécurité HTTP et le cycle de session.
Temps estimé¶
2h à 3h.
Résultat attendu¶
Application avec authentification fonctionnelle — accueil public, formulaire de connexion sécurisé par CSRF, dashboard protégé, page profil et déconnexion en POST.
Flux d'authentification¶
flowchart TD
A([Navigateur]) -->|"GET /login"| B["AuthController.login_form"]
B --> C["Formulaire + csrf_token"]
C -->|"POST /login"| D["AuthController.login"]
D --> E["auth_model.py<br/>SQL visible"]
E --> F["core.security.hashing"]
F --> G{"mot de passe valide ?"}
G -->|oui| H["session authentifiée"]
G -->|non| I["retour formulaire"]
H --> J["/dashboard protégé"]
Installation du projet Forge¶
Si vous avez suivi la Partie 1
forge est déjà installé via pipx install forge-mvc. Ignorez les étapes pipx install ... ci-dessous et commencez directement par forge new AppAuth.
Méthode A — installation automatique (recommandée)¶
pipx install git+https://github.com/caucrogeGit/Forge.git
forge new AppAuth
cd AppAuth
source .venv/bin/activate
forge doctor
Méthode B — installation manuelle¶
git clone https://github.com/caucrogeGit/Forge.git AppAuth
cd AppAuth
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
npm install
pip install -e .
forge doctor
La documentation utilisateur utilise la CLI officielle
forge, disponible aprèspip install -e ..
Préparation de la base¶
Avant d'exécuter forge db:init, vérifier que env/dev contient les identifiants administrateur MariaDB :
Compte administrateur MariaDB
La procédure utilise root avec mot de passe. Pour un environnement plus sécurisé, remplacer root par un compte dédié, par exemple forge_admin.
Cette commande crée la base de données du projet, l'utilisateur applicatif et applique les droits.
Prérequis :
- MariaDB installé et en cours d'exécution.
- Les identifiants
DB_ADMIN_LOGIN,DB_ADMIN_PWD,DB_APP_LOGIN,DB_APP_PWDetDB_NAMErenseignés dansenv/dev.
Développement de l'application¶
Ce que l'on apprend¶
- Déclarer des routes publiques et protégées.
- Garder
POST /logoutderrière un token CSRF. - Lire les champs de formulaire depuis
request.body. - Créer, authentifier et supprimer une session.
- Utiliser
BaseController.redirect_with_flash(request, ...). - Rendre un dashboard protégé avec
BaseController.render(..., request=request). - Ne pas inventer de permissions avancées quand le besoin est seulement "connecté ou non".
Navigation de l'application¶
/ accueil public
/login formulaire de connexion public
/dashboard page protégée
/profil profil protégé
/logout déconnexion en POST
GET /logout n'est pas proposé : la déconnexion modifie l'état de session et doit rester une action POST.
Charte graphique¶
La charte reste proche du starter 1 :
- accueil public sobre avec un bouton "Se connecter" ;
- formulaire centré dans une carte ;
- dashboard en deux colonnes simples ;
- profil sous forme de fiche ;
- messages flash visibles après connexion et déconnexion ;
- bouton de déconnexion distinct, en style secondaire ou danger léger.
Modèle de données¶
Pour un starter pédagogique, l'utilisateur peut être représenté par une table simple.
JSON canonique complet de Utilisateur
{
"format_version": 1,
"entity": "Utilisateur",
"table": "utilisateur",
"description": "Utilisateur applicatif simple",
"fields": [
{
"name": "utilisateur_id",
"sql_type": "INT",
"primary_key": true,
"auto_increment": true
},
{
"name": "login",
"sql_type": "VARCHAR(80)",
"unique": true,
"constraints": {
"not_empty": true,
"max_length": 80
}
},
{
"name": "prenom",
"sql_type": "VARCHAR(80)",
"nullable": true,
"constraints": {
"max_length": 80
}
},
{
"name": "nom",
"sql_type": "VARCHAR(80)",
"constraints": {
"not_empty": true,
"max_length": 80
}
},
{
"name": "password_hash",
"sql_type": "VARCHAR(255)",
"constraints": {
"not_empty": true,
"max_length": 255
}
},
{
"name": "email",
"sql_type": "VARCHAR(120)",
"nullable": true,
"constraints": {
"max_length": 120
}
},
{
"name": "actif",
"sql_type": "BOOLEAN"
}
]
}
Le mot de passe en clair ne va jamais dans le JSON ni dans la base. Le starter montre la structure, puis laisse le hachage au code applicatif.
Commandes Forge¶
Pour générer directement ce starter depuis un projet Forge vierge :
Alias disponibles :
Pour prévisualiser sans écrire :
--public n'est pas applicable à ce starter : il sert justement à montrer la différence entre routes publiques (/login) et routes protégées (/dashboard, /profil, /logout).
Le flux manuel équivalent reste :
forge make:entity Utilisateur --no-input
# modifier mvc/entities/utilisateur/utilisateur.json
forge build:model --dry-run
forge build:model
forge check:model
forge db:apply
Le CRUD complet utilisateur n'est pas l'objectif de ce starter. On écrit plutôt un AuthController et un modèle applicatif SQL visible pour chercher l'utilisateur par login.
Créer un utilisateur de test¶
L'auth réelle Forge vérifie le mot de passe avec core.security.hashing.verifier_mot_de_passe(). La valeur stockée dans PasswordHash doit donc être produite avec hacher_mot_de_passe(), au format sel_hex:hash_hex.
Le starter généré copie un modèle mvc/models/auth_model.py autonome : il lit uniquement la table utilisateur et ajoute roles = [] pour rester compatible avec la session Forge sans dépendre de tables de rôles.
Après génération automatique, le script suivant est disponible :
Il crée ou met à jour l'utilisateur de test :
Le mot de passe est stocké avec hacher_mot_de_passe("secret123").
Script minimal de création d'utilisateur
from core.database.connection import get_connection, close_connection
from core.security.hashing import hacher_mot_de_passe
connection = get_connection()
cursor = connection.cursor()
cursor.execute(
"""
INSERT INTO utilisateur (Login, Prenom, Nom, Email, PasswordHash, Actif)
VALUES (?, ?, ?, ?, ?, ?)
""",
(
"admin",
"Ada",
"Lovelace",
"admin@example.test",
hacher_mot_de_passe("secret123"),
True,
),
)
connection.commit()
cursor.close()
close_connection(connection)
Connexion navigateur :
Si votre connecteur SQL utilise %s au lieu de ?, gardez l'idée du script et adaptez uniquement les placeholders à votre connexion.
Fichiers créés ou modifiés¶
Fichiers entité :
mvc/entities/utilisateur/utilisateur.json
mvc/entities/utilisateur/utilisateur.sql
mvc/entities/utilisateur/utilisateur_base.py
mvc/entities/utilisateur/utilisateur.py
Fichiers applicatifs :
mvc/controllers/auth_controller.py
mvc/controllers/dashboard_controller.py
mvc/models/auth_model.py
mvc/views/auth/login.html
mvc/views/dashboard/index.html
mvc/views/dashboard/profil.html
mvc/views/layouts/app.html
scripts/create_auth_user.py
mvc/routes.py
Classes Python utilisées¶
BaseControllerpourrender,redirectetredirect_with_flash.core.security.sessionpour créer, lire, authentifier et supprimer une session.core.security.csrfvia le middleware et le champcsrf_token.AuthControllerpour login/logout.DashboardControllerpour dashboard et profil.UtilisateuretUtilisateurBasepour la structure métier.
Lecture des données de connexion :
Le contrôleur lit les cookies et la session via les helpers de sécurité, pas via un attribut magique sur request.
Tags Jinja utilisés¶
{% extends "layouts/app.html" %};{% block content %};{% if flash %}ou affichage équivalent selon le contexte du layout ;{{ csrf_token }}dans les formulairesPOST;{{ utilisateur.Nom }},{{ utilisateur.Login }}et{{ utilisateur.Email }}dans le profil si le contexte reçoit directement le dictionnaire SQL ;{% if utilisateur %}pour adapter la navigation.
Classes CSS/Tailwind importantes¶
min-h-screen,flex,items-center,justify-centerpour la page login ;max-w-md,rounded,border,shadow-smpour la carte de connexion ;bg-orange-600,hover:bg-orange-700pour l'action principale ;bg-slate-100,text-slate-700pour les actions secondaires ;grid,gap-6,md:grid-cols-2pour le dashboard.
Test navigateur¶
- Ouvrir
/et vérifier que l'accueil est public. - Ouvrir
/dashboardsans session et vérifier la redirection vers/login. - Ouvrir
/login. - Soumettre le formulaire sans token CSRF valide et vérifier le refus.
- Se connecter avec un utilisateur de test.
- Vérifier le message flash de connexion.
- Ouvrir
/dashboard. - Ouvrir
/profil. - Cliquer sur déconnexion, qui soumet
POST /logout. - Vérifier que
/dashboardredevient inaccessible.
Limites du starter¶
- Pas de permissions multi-rôles.
- Pas de réinitialisation de mot de passe.
- Pas d'inscription publique.
- Pas de politique de mot de passe avancée.
- Pas de gestion d'équipe ou d'organisation.
- Le modèle utilisateur est volontairement minimal.
Vérification finale¶
Ouvrir dans le navigateur :
Reconstruction¶
Le fichier complet de reconstruction est disponible dans starters/02-utilisateurs-auth/rebuild.md.
Dépannage rapide¶
| Erreur | Cause probable | Correction |
|---|---|---|
forge: command not found |
pipx n'est pas dans le PATH |
pipx ensurepath puis exec $SHELL -l |
No module named venv |
python3-venv absent |
sudo apt install python3-venv |
mariadb_config not found |
dépendances MariaDB dev absentes | sudo apt install libmariadb-dev pkg-config |
Access denied for user 'root'@'localhost' |
mauvais mot de passe root ou root configuré en unix_socket |
vérifier le mot de passe, ou tester sudo mariadb |
mariadb: command not found |
client MariaDB absent | sudo apt install mariadb-client |
| erreur de compilation Python | outils de build absents | sudo apt install build-essential pkg-config libmariadb-dev |
| erreur certificat HTTPS | openssl absent |
sudo apt install openssl |