Vai al contenuto principale

Journal d'audit

Le journal d'audit est la memoire de la plateforme. Il repond a la question fondamentale : "Qui a fait quoi, quand et depuis ou ?" C'est un outil indispensable pour investiguer un incident de securite, verifier qu'une action a bien ete effectuee ou demontrer la conformite RGPD.

Chaque entree est horodatee, associee a un utilisateur et enrichie de metadonnees contextuelles incluant la geolocalisation IP. Le journal est immutable -- une fois enregistree, une entree ne peut etre ni modifiee ni supprimee.

La page est accessible depuis /backoffice/audit-log et utilise un datatable anime (Framer Motion) avec pagination serveur, recherche textuelle, export CSV complet et statistiques optimisees.

Types d'evenements

BeePass trace 19 types d'evenements couvrant l'authentification, la gestion de compte, l'administration et les operations metier. Ce perimetre a ete defini pour couvrir toutes les actions susceptibles d'avoir un impact sur la securite ou les donnees des utilisateurs.

Authentification (8 types)

Ces evenements tracent le cycle de vie de la session : de la connexion a la deconnexion, en passant par les verifications de securite.

TypeDescriptionSourceHelper
login_successConnexion eleveur reussie (email + OAuth Google/Facebook)AuthLogin.tsx, /auth/callbacklogLoginSuccess()
login_failedTentative de connexion echoueeAuthLogin.tsxlogLoginFailed()
logoutDeconnexion volontaireAuthContextlogLogout()
password_changeChangement de mot de passeSecurityTab.tsxlogPasswordChange()
password_reset_requestDemande de reinitialisation (HMAC token + Brevo)/api/auth/password-resetlogAuditServer()
device_verificationVerification d'un nouvel appareil (MFA email)ClientlogDeviceVerification()
session_revokedRevocation d'une session active/api/auth/sessionsinsert direct
mfa_enabledActivation du 2FA TOTPSecurityTab.tsxlogAuditEvent()

Gestion de compte (2 types)

TypeDescriptionSourceHelper
mfa_disabledDesactivation du 2FA TOTP (eleveurs uniquement)SecurityTab.tsxlogAuditEvent()
account_deletionSuppression definitive d'un compte/api/auth/delete-accountinsert direct

Administration (4 types)

Ces evenements sont specifiques aux actions des administrateurs. Ils permettent de repondre a des questions comme "Qui a banni cet utilisateur ?" ou "Qui a approuve ce changement de role ?"

TypeDescriptionSourceHelper
admin_loginConnexion au backoffice admin (succes + echec)/api/admin/auth/loginlogAuditServer()
admin_logoutDeconnexion du backoffice admin/api/admin/auth/logoutlogAuditServer()
role_changeModification des roles d'un utilisateur (approve/reject)/api/admin/roleslogAuditServer()
user_bannedBannissement ou debannissement d'un utilisateur/api/admin/users/[id]/banlogAuditServer()

Operations metier (5 types)

Ces evenements tracent les actions qui modifient les donnees metier de la plateforme -- abonnements, reines importees, cles API.

TypeDescriptionSourceHelper
subscription_changeChangement de plan d'abonnement Stripe/api/billing/webhookinsert direct
queen_importImport de reines (unitaire ou en masse)/api/admin/reference-queens/importlogAuditServer()
api_key_createdCreation d'une cle API/api/api-keys POSTinsert direct
api_key_revokedRevocation d'une cle API/api/api-keys/[id] DELETEinsert direct
profile_updateModification du profil utilisateur/api/auth/profile PUTlogAuditServer()

Structure d'un evenement

Schema de la table auth_audit_log

Chaque evenement enregistre non seulement l'action elle-meme, mais aussi son contexte : depuis quel appareil, quel navigateur, quelle adresse IP. Ces informations sont cruciales pour differencier un acces legitime d'une intrusion.

ColonneTypeDescription
idUUIDIdentifiant unique de l'evenement
user_idUUIDFK vers auth.users (ON DELETE SET NULL -- conserve le log apres suppression)
event_typeTEXTUn des 19 types listes ci-dessus
ip_addressTEXTIP source de la requete
user_agentTEXTNavigateur et systeme d'exploitation
device_nameTEXTNom de l'appareil (extrait du User Agent)
browserTEXTNavigateur (Chrome, Firefox, Safari...)
successBOOLEANResultat de l'action (succes ou echec)
metadataJSONBDonnees contextuelles specifiques au type d'evenement
created_atTIMESTAMPTZHorodatage UTC de l'evenement

Metadonnees par type d'evenement

Le champ metadata (format JSONB) contient des donnees specifiques a chaque type d'evenement. C'est ce qui donne du contexte a un evenement brut -- par exemple, un role_change seul dit qu'un role a change, mais les metadonnees precisent quel role a ete ajoute ou retire, et pour quel utilisateur.

TypeChamps metadataExemple
login_successmethod (email/google/facebook){"method": "email"}
login_failedreason{"reason": "Invalid credentials"}
password_change--{}
password_reset_requestemail (masque){"email": "m***@beepass.io"}
role_changetarget_user_id, old_roles, new_roles, action{"action": "approve", "new_roles": ["eleveur", "groupe_selection"]}
user_bannedtarget_user_id, action{"action": "ban", "target_user_id": "uuid"}
subscription_changeold_plan, new_plan, status{"old_plan": "discovery", "new_plan": "breeder", "status": "trialing"}
queen_importcount, format, destination{"count": 150, "format": "mellifera", "destination": "reference"}
session_revokedsession_id{"session_id": "uuid"}
mfa_enabledmethod{"method": "totp"}
mfa_disabledmethod{"method": "totp"}
api_key_createdkey_prefix, name{"key_prefix": "bp_live_abc1", "name": "My API Key"}
api_key_revokedkey_prefix, name{"key_prefix": "bp_live_abc1", "name": "My API Key"}
admin_loginmethod{"method": "hmac"}
account_deletiondeleted_by (self/admin){"deleted_by": "self"}

Geolocalisation IP

Pour donner un contexte geographique aux evenements, l'endpoint admin GET /api/admin/audit-log enrichit chaque evenement avec la geolocalisation de l'IP. Cela permet de reperer rapidement une connexion suspecte -- par exemple, un eleveur francais qui se connecte soudainement depuis un pays inhabituel.

La geolocalisation est fournie par ip-api.com :

Champ enrichiDescription
countryPays (code ISO + nom)
cityVille
ispFournisseur d'acces Internet
country_flagURL du drapeau via flagcdn.com (PNG 16x12 + retina 32x24)

La resolution est effectuee en batch avec un cache memoire serveur et un timeout de 3 secondes pour eviter de ralentir l'affichage.

Interface utilisateur

Filtres disponibles

L'interface de recherche est concue pour repondre a des questions specifiques : "Y a-t-il eu des echecs de connexion ce matin ?", "Quels changements de role ont ete faits cette semaine ?", "Quels sont les evenements de cet utilisateur ?"

FiltreOptionsDescription
Type d'evenementListe deroulante des 19 typesFiltre par categorie d'evenement
PeriodeSelecteur de plage de dates (debut, fin)Filtre temporel
ResultatSucces / Echec / TousFiltre par resultat de l'action
RechercheChamp texte libreRecherche par nom d'utilisateur (via profiles.full_name ilike) ou adresse IP (combinaison OR)

Cartes de statistiques

Quatre cartes synthetisent les donnees du mois en cours en haut de page, offrant un apercu immediat de l'activite et de la securite :

CarteDescriptionCalcul
Actions ce moisNombre total d'evenements enregistresCOUNT all events du mois
EchecsNombre d'evenements en echec (tentatives de connexion echouees, par exemple)COUNT WHERE success = false
Utilisateurs actifsNombre d'utilisateurs uniques ayant genere au moins un evenementCOUNT DISTINCT user_id
Connexions adminNombre de admin_login enregistresCOUNT WHERE event_type = 'admin_login'

Export CSV

Le bouton Exporter CSV telecharge l'ensemble des evenements correspondant aux filtres actifs (pas seulement la page courante). L'export fetche toutes les lignes filtrées (max 10 000) en une seule requete API avec skip_stats=true pour eviter le recalcul inutile des statistiques. Le fichier CSV inclut un BOM UTF-8 pour la compatibilite Excel.

Datatable

Le tableau utilise Framer Motion (pattern motion.tbody + rowVariants) avec :

  • Pagination serveur (parametre offset + limit) pour gerer de grands volumes sans surcharger le navigateur
  • Drapeaux de pays pour chaque IP (flagcdn.com)
  • Avatar + lien cliquable vers la fiche utilisateur (/backoffice/users/{id})
  • Icones et badges colores par type d'evenement (19 mappings)
  • Temps relatif (relativeTime() centralise) avec date complete au survol

Optimisation des performances

Les cartes de statistiques (4 queries COUNT/DISTINCT sur auth_audit_log) sont recalculees uniquement lors du premier chargement ou d'un changement de filtre. Les changements de page (pagination) envoient skip_stats=true et reutilisent les stats deja en memoire, evitant 4 queries superflues a chaque clic de page. useAutoRefresh avec polling 60 secondes.

Implementation technique

Enregistrement des evenements

Les evenements arrivent dans la table auth_audit_log par trois canaux differents, selon leur origine. Cette diversite est necessaire car certains evenements sont generes cote navigateur (login), d'autres cote serveur (webhook Stripe), et d'autres par les routes admin.

MethodeFichierUsage
Client-sidesrc/lib/audit-log.tsEvenements eleveur (login, logout, MFA, password) -- POST avec Bearer token fallback
Server-sidesrc/lib/audit-log-server.tsEvenements admin (role_change, ban, import) -- insert direct via adminSupabase
Direct insertRoutes APIEvenements systeme (webhook Stripe, suppression, sessions)
Audit de deconnexion

L'evenement logout est enregistre via await fetch('/api/auth/audit') avec le Bearer token avant l'appel signOut(). L'usage de await est obligatoire car un fire-and-forget (envoyer la requete sans attendre la reponse) serait annule par la navigation qui suit la deconnexion -- le navigateur coupe les requetes en cours quand il change de page.

Audit de connexion

L'evenement login_success utilise le Bearer token en fallback pour l'authentification de la requete d'audit, car les cookies SSR Supabase ne sont pas encore etablis immediatement apres signInWithPassword(). Il y a un bref delai entre la connexion reussie et la disponibilite des cookies.

API Route

RouteMethodeAuthDescription
/api/auth/auditPOSTBearer token / SSREnregistrer un evenement (eleveur)
/api/auth/auditGETBearer token / SSRHistorique personnel de l'utilisateur connecte
/api/admin/audit-logGETCookie HMAC adminJournal complet avec geo IP, pagination serveur, filtres

Retention des donnees

Les evenements sont conserves indefiniment dans la table auth_audit_log. Ce choix est delibere -- aucune politique de purge automatique n'est appliquee -- car les donnees d'audit sont essentielles pour :

  • La conformite RGPD (tracabilite des acces aux donnees personnelles -- "qui a consulte les informations de cet eleveur ?")
  • Les investigations de securite (analyse post-incident -- "depuis quand ce compte est-il compromis ?")
  • L'historique des operations admin (changements de roles, bans, imports -- "qui a approuve ce role il y a 6 mois ?")
FK ON DELETE SET NULL

La colonne user_id utilise ON DELETE SET NULL : lorsqu'un utilisateur est supprime, ses logs d'audit sont conserves mais le user_id passe a NULL. Cela garantit que l'historique n'est jamais perdu meme apres une suppression de compte -- un choix essentiel pour les obligations legales et la tracabilite.

Journalisation des erreurs API

En complement du journal d'audit, le helper errorResponse() enregistre automatiquement les erreurs API (codes 4xx/5xx) dans la table api_error_logs en mode fire-and-forget (l'enregistrement n'impacte pas le temps de reponse de l'API, et si l'insertion echoue, l'erreur d'origine est quand meme retournee au client). Ces erreurs sont visibles :

Colonne api_error_logsTypeDescription
routeTEXT NOT NULLChemin API (ex : /api/queens/f0)
methodTEXT NOT NULLMethode HTTP (GET/POST/PUT/DELETE)
status_codeINTEGER NOT NULLCode HTTP (defaut 500)
error_messageTEXTMessage tronque a 1000 caracteres
error_stackTEXTStack trace tronquee a 4000 caracteres
metadataJSONBContexte additionnel
created_atTIMESTAMPTZHorodatage

Voir aussi : Gestion des utilisateurs | Centre de securite | Vue d'ensemble