Skip to content

Gestion des mots de passe

Configurez le hachage PBKDF2-SHA256 des mots de passe, les réinitialisations par CLI ou API admin, les liens de réinitialisation en libre-service par jeton, et le stockage des identifiants en SQLite ou PostgreSQL dans Potato.

Nouveau dans la v2.4.0

Le système d'authentification de Potato utilise PBKDF2-SHA256 avec 100 000 itérations et un sel propre à chaque utilisateur, l'approche que le NIST recommande pour le stockage sécurisé des mots de passe. Cette page explique comment les mots de passe sont stockés, comment les réinitialiser et comment conserver les identifiants d'un redémarrage du serveur à l'autre.

Elle concerne les méthodes in_memory et database. Avec OAuth, c'est le fournisseur d'identité qui gère le mot de passe, donc rien de tout cela ne s'applique, sauf aux comptes locaux en mode mixte.

Mise en œuvre de la sécurité

Les mots de passe sont stockés au format salt$hash :

  • Un sel hexadécimal de 32 caractères, unique par utilisateur
  • Un condensat hexadécimal de 64 caractères, dérivé par PBKDF2-HMAC-SHA256 à partir du mot de passe, avec ce sel et 100 000 itérations
  • Une comparaison à temps constant via hmac.compare_digest, qui empêche les attaques temporelles

Une seule passe de SHA-256 sur un mot de passe salé est assez rapide pour permettre une attaque par force brute à grande échelle. Ce sont les 100 000 itérations qui rendent chaque essai coûteux.

Les mots de passe en clair présents dans les fichiers user_config.json sont re-hachés automatiquement avec des sels uniques au chargement par Potato. Aucune migration manuelle n'est nécessaire.

Configuration par défaut

Par défaut, Potato utilise une authentification en mémoire. require_password est une clé de premier niveau, et non une partie du bloc authentication :

yaml
require_password: true
 
authentication:
  method: in_memory
 
user_config:
  users:
    - username: "annotator1"
      password: "initial-password"   # will be hashed on first load
    - username: "annotator2"
      password: "initial-password"

Identifiants persistants

Les comptes créés via le formulaire d'inscription sont écrits dans un fichier JSONL et relus au démarrage suivant. Avec in_memory, ce fichier est user_config.json, à côté du répertoire de sortie, sauf si vous en indiquez un autre.

Persistance par fichier

yaml
authentication:
  method: in_memory
  user_config_path: /shared/path/to/user_config.jsonl

Indiquez un chemin quand plusieurs instances du serveur partagent une même liste d'utilisateurs, ou quand vous voulez placer cette liste ailleurs qu'à côté des annotations. Les nouvelles inscriptions et les changements de mot de passe sont écrits au fur et à mesure.

Conservez la liste d'utilisateurs avec les résultats d'annotation. Le répertoire d'un annotateur porte son nom d'utilisateur. Si la liste d'utilisateurs a disparu alors que le répertoire de sortie existe encore, la prochaine personne qui saisirait ce nom d'utilisateur hériterait du travail de quelqu'un d'autre. Potato refuse donc l'inscription, ce qui signifie que l'annotateur ne peut pas revenir tant que vous n'avez pas restauré le fichier.

Avant la v2.8.3, in_memory n'écrivait rien si user_config_path n'était pas défini explicitement, et les comptes ne survivaient donc pas à un redémarrage. Si vous avez mené une étude sur une version antérieure, ces comptes sont perdus ; les annotations, elles, ne le sont pas.

Base de données

SQLite (aucune dépendance supplémentaire) :

yaml
authentication:
  method: database
  database_url: "sqlite:///auth/users.db"

PostgreSQL (nécessite psycopg2-binary) :

yaml
authentication:
  method: database
  database_url: "postgresql://user:password@localhost:5432/potato_auth"

Potato crée la table users au premier démarrage et fait tourner SQLite en mode WAL pour de meilleures lectures concurrentes. database_url doit commencer par sqlite:/// ou postgresql://. Vous pouvez aussi définir POTATO_DB_CONNECTION dans l'environnement ; database_url l'emporte quand les deux sont présents.

Remarque : method: database et user_config_path s'excluent mutuellement. Choisissez une seule stratégie de persistance. Potato lève une erreur si les deux sont configurés.

Réinitialiser les mots de passe

CLI d'administration

Réinitialisez un mot de passe en ligne de commande :

bash
# Prompts for username and password
potato reset-password config.yaml
 
# Prompts for the password only
potato reset-password config.yaml --username annotator1

API d'administration

Réinitialisez par programme avec la clé d'API d'administration :

bash
curl -X POST http://localhost:8000/admin/reset_password \
  -H "X-API-Key: $ADMIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"username": "annotator1", "new_password": "new-secure-password"}'

La clé provient, dans cet ordre, du réglage de premier niveau admin_api_key, de la variable d'environnement POTATO_ADMIN_API_KEY ou d'un fichier admin_api_key.txt généré dans le répertoire de la tâche.

Réinitialisation en libre-service par jeton

Les annotateurs qui oublient leur mot de passe peuvent le réinitialiser eux-mêmes. Ils se rendent sur /forgot-password et saisissent leur nom d'utilisateur, et Potato génère un jeton à usage unique valable 24 heures. Le lien de réinitialisation s'affiche à l'écran pour qu'un administrateur le transmette ; Potato n'envoie aucun e-mail. L'annotateur ouvre /reset/<token> et définit un nouveau mot de passe, ce qui consomme le jeton.

Ce parcours ne demande aucune configuration. Quand require_password: true est défini, un lien "Forgot Password?" apparaît sur la page de connexion.

Les administrateurs peuvent aussi créer un jeton directement :

bash
curl -X POST http://localhost:8000/admin/create_reset_token \
  -H "X-API-Key: $ADMIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"username": "annotator1"}'
 
# {"reset_link": "http://localhost:8000/reset/abc123...",
#  "token": "abc123...", "expires_in_hours": 24}

Passez "ttl_hours": 48 dans le corps de la requête pour une autre durée de validité.

Mode sans mot de passe

Pour des démonstrations en classe, des études rapides ou des tâches qui reposent sur une authentification externe (MTurk, Prolific), vous pouvez désactiver complètement les mots de passe :

yaml
require_password: false
 
authentication:
  method: in_memory

Les annotateurs se connectent avec n'importe quel nom d'utilisateur, sans invite de mot de passe. À éviter pour des données sensibles ou des tâches qui exigent une identité vérifiée.

Voir Connexion sans mot de passe pour les détails.

Référence complète

yaml
# Top-level: require a password at login (default: true)
require_password: true
 
# Top-level: key for the admin endpoints
admin_api_key: ${POTATO_ADMIN_API_KEY}
 
authentication:
  # in_memory (default) | database | clerk | oauth
  method: in_memory
 
  # in_memory only, mutually exclusive with method: database
  user_config_path: users.jsonl
 
  # database only; sqlite:/// or postgresql://
  # database_url: "sqlite:///auth.db"
 
user_config:
  users:
    - username: "researcher"
      password: "secure-passphrase"
    - username: "annotator1"
      password: "initial-pass"
EndpointMéthodeAuthDescription
/forgot-passwordGET, POSTaucuneDemander un jeton de réinitialisation
/reset/<token>GET, POSTaucuneDéfinir un nouveau mot de passe
/admin/reset_passwordPOSTclé d'APIRéinitialisation du mot de passe par l'administrateur
/admin/create_reset_tokenPOSTclé d'APIGénérer un jeton de réinitialisation

Pour aller plus loin

Pour les détails d'implémentation, consultez la documentation source.