Aller au contenu

Starter 2 — Utilisateurs / authentification

Accueil Retour

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.12 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/dev configuré 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.auth disponible (inclus dans Forge) pour le hachage et la vérification des mots de passe
  • Script scripts/create_auth_user.py fourni automatiquement par forge 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

Starter Auth moderne — aligné sur le socle Auth/User post-1.5.0

Ce starter utilise les API Auth/User modernes de Forge : core.auth.verify_password, core.auth.login_user, core.auth.logout_user, core.auth.get_authenticated_user_id et le décorateur @login_required.

Il démontre l'authentification minimale moderne de Forge.

Ce starter ne démontre pas MFA, OIDC, RBAC avancé, reset password complet ou administration utilisateurs.

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.auth.verify_password"]
    F --> G{"mot de passe valide ?"}
    G -->|oui| H["core.auth.login_user"]
    G -->|non| I["retour formulaire"]
    H --> J["/dashboard protégé (@login_required)"]

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ès pip install -e ..


Préparation de la base

Avant d'exécuter forge db:init, vérifier que env/dev contient les identifiants administrateur MariaDB :

DB_ADMIN_LOGIN=root
DB_ADMIN_PWD=<mot_de_passe_root_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.

forge db:init

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_PWD et DB_NAME renseignés dans env/dev.

Développement de l'application

Ce que l'on apprend

  • Déclarer des routes publiques et protégées.
  • Garder POST /logout derriè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".
/               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 :

forge starter:build 2

Alias disponibles :

forge starter:build auth
forge starter:build utilisateurs
forge starter:build utilisateurs-auth

Pour prévisualiser sans écrire :

forge starter:build 2 --dry-run

--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

Le mot de passe est haché avec core.auth.hash_password() (argon2). La valeur stockée dans PasswordHash est vérifiée par core.auth.verify_password().

Le starter génère mvc/models/auth_model.py avec deux fonctions : get_user_by_login (requête SQL par identifiant) et build_auth_user (conversion dict → AuthUser pour core.auth.login_user).

Après génération automatique, le script suivant est disponible :

python scripts/create_auth_user.py

Il crée ou met à jour l'utilisateur de test :

login    admin
password secret123
Script minimal de création d'utilisateur
from core.auth import hash_password
from core.database.connection import get_connection, close_connection

connection = get_connection()
cursor = connection.cursor()

cursor.execute(
    """
    INSERT INTO utilisateur (Login, Prenom, Nom, Email, PasswordHash, Actif)
    VALUES (?, ?, ?, ?, ?, ?)
    """,
    (
        "admin",
        "Ada",
        "Lovelace",
        "admin@example.test",
        hash_password("secret123"),
        True,
    ),
)

connection.commit()
cursor.close()
close_connection(connection)

Connexion navigateur :

login    admin
password secret123

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

  • BaseController pour render, redirect et redirect_with_flash.
  • core.auth.login_user / core.auth.logout_user pour la session Auth/User.
  • core.auth.verify_password pour vérifier le mot de passe.
  • core.auth.hash_password pour hacher le mot de passe (script de création).
  • core.auth.get_authenticated_user_id pour récupérer l'ID utilisateur courant.
  • core.auth.login_required pour protéger les routes du dashboard.
  • core.security.session pour la gestion CSRF et le cycle de session HTTP.
  • AuthController pour login/logout.
  • DashboardController pour dashboard et profil.
  • Utilisateur et UtilisateurBase pour la structure métier.

Lecture des données de connexion :

login = request.body.get("login", [""])[0]
password = request.body.get("password", [""])[0]

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 formulaires POST ;
  • {{ utilisateur.nom }}, {{ utilisateur.login }} et {{ utilisateur.email }} dans le profil ;
  • {% if utilisateur %} pour adapter la navigation.

Classes CSS/Tailwind importantes

  • min-h-screen, flex, items-center, justify-center pour la page login ;
  • max-w-md, rounded, border, shadow-sm pour la carte de connexion ;
  • bg-orange-600, hover:bg-orange-700 pour l'action principale ;
  • bg-slate-100, text-slate-700 pour les actions secondaires ;
  • grid, gap-6, md:grid-cols-2 pour le dashboard.

Test navigateur

  1. Ouvrir / et vérifier que l'accueil est public.
  2. Ouvrir /dashboard sans session et vérifier la redirection vers /login.
  3. Ouvrir /login.
  4. Soumettre le formulaire sans token CSRF valide et vérifier le refus.
  5. Se connecter avec un utilisateur de test.
  6. Vérifier le message flash de connexion.
  7. Ouvrir /dashboard.
  8. Ouvrir /profil.
  9. Cliquer sur déconnexion, qui soumet POST /logout.
  10. Vérifier que /dashboard redevient 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

forge doctor
forge routes:list
python app.py

Ouvrir dans le navigateur :

https://localhost:8000/login

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