Skip to content

Auth Service

rom98759 edited this page Jan 27, 2026 · 1 revision

Auth Service

Le service d'authentification gรจre l'ensemble des fonctionnalitรฉs liรฉes ร  l'authentification des utilisateurs, incluant l'inscription, la connexion, l'authentification ร  deux facteurs (2FA/TOTP), et la gestion des tokens JWT.

๐Ÿ“‹ Table des matiรจres


Architecture

Le service Auth suit une architecture en couches (layered architecture) :

Request โ†’ Routes โ†’ Controllers โ†’ Services โ†’ Database

Principe de responsabilitรฉ unique

  • Routes : Dรฉfinition des endpoints et validation des inputs (schรฉmas JSON)
  • Controllers : Gestion des requรชtes/rรฉponses HTTP, orchestration
  • Services : Logique mรฉtier (business logic)
  • Database : Accรจs aux donnรฉes (SQLite via better-sqlite3)

Technologies utilisรฉes

Technologie Usage Documentation
Fastify Framework web haute performance Fastify
TypeScript Typage statique TypeScript
better-sqlite3 Base de donnรฉes embarquรฉe npm
bcrypt Hashing de mots de passe npm
@fastify/jwt Gรฉnรฉration et validation JWT GitHub
@fastify/cookie Gestion des cookies GitHub
@fastify/rate-limit Protection contre les abus GitHub
otplib TOTP (Time-based OTP) npm
qrcode Gรฉnรฉration de QR codes pour 2FA npm

Configuration

Variables d'environnement

Fichier : srcs/.env.auth

# Environnement
NODE_ENV=production                    # development | test | production | staging

# Logging
LOG_ENABLED=true
LOG_LEVEL=info                         # debug | info | warn | error

# JWT Configuration - CRITIQUE Sร‰CURITร‰
JWT_SECRET=your-super-secret-key-here-min-32-chars

# Service Configuration
AUTH_SERVICE_PORT=3001
AUTH_SERVICE_NAME=auth-service
AUTH_DB_PATH=/data/auth.db            # Chemin vers la base SQLite

# Redis Configuration
REDIS_HOST=redis-broker
REDIS_PORT=6379
REDIS_PASSWORD=

# User Management Service
UM_SERVICE_NAME=user-service
UM_SERVICE_PORT=3002

# Admin User Configuration (utilisateur crรฉรฉ au dรฉmarrage)
ADMIN_USERNAME=admin
ADMIN_EMAIL=admin@transcendence.local
ADMIN_PASSWORD=Admin123!              # โš ๏ธ ร€ CHANGER EN PRODUCTION

# Invite User Configuration (utilisateur invitรฉ)
INVITE_USERNAME=invite
INVITE_EMAIL=invite@transcendence.local
INVITE_PASSWORD=Invite123!            # โš ๏ธ ร€ CHANGER EN PRODUCTION

# Application Configuration
APP_NAME=Transcendence                # Nom affichรฉ dans l'app d'authentification 2FA

โš ๏ธ CRITIQUE : Le JWT_SECRET doit รชtre :

  • Au minimum 32 caractรจres (validation stricte au dรฉmarrage)
  • Gรฉnรฉrรฉ alรฉatoirement : node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
  • Jamais committรฉ dans Git
  • Diffรฉrent entre dev et production
  • Le service refuse de dรฉmarrer si le secret est invalide ou trop court

Structure du projet

srcs/auth/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ index.ts                    # Point d'entrรฉe, configuration Fastify
โ”‚   โ”œโ”€โ”€ config/
โ”‚   โ”‚   โ”œโ”€โ”€ env.ts                  # Validation des variables d'environnement
โ”‚   โ”‚   โ””โ”€โ”€ logger.config.ts        # Configuration Pino logger
โ”‚   โ”œโ”€โ”€ controllers/
โ”‚   โ”‚   โ””โ”€โ”€ auth.controller.ts      # Handlers HTTP (login, register, 2FA, etc.)
โ”‚   โ”œโ”€โ”€ routes/
โ”‚   โ”‚   โ””โ”€โ”€ auth.routes.ts          # Dรฉfinition des routes et rate limiting
โ”‚   โ”œโ”€โ”€ services/
โ”‚   โ”‚   โ”œโ”€โ”€ auth.service.ts         # Logique mรฉtier authentification
โ”‚   โ”‚   โ”œโ”€โ”€ jwt.service.ts          # Gรฉnรฉration et validation JWT
โ”‚   โ”‚   โ”œโ”€โ”€ totp.service.ts         # Service TOTP/2FA
โ”‚   โ”‚   โ”œโ”€โ”€ database.ts             # Accรจs base de donnรฉes
โ”‚   โ”‚   โ””โ”€โ”€ external/
โ”‚   โ”‚       โ””โ”€โ”€ um.service.ts       # Appels au service Users
โ”‚   โ”œโ”€โ”€ types/
โ”‚   โ”‚   โ”œโ”€โ”€ dto.ts                  # Data Transfer Objects
โ”‚   โ”‚   โ”œโ”€โ”€ errors.ts               # Classes d'erreurs personnalisรฉes
โ”‚   โ”‚   โ”œโ”€โ”€ logger.ts               # Types pour le logger
โ”‚   โ”‚   โ””โ”€โ”€ models.ts               # Modรจles de donnรฉes
โ”‚   โ””โ”€โ”€ utils/
โ”‚       โ”œโ”€โ”€ constants.ts            # Constantes (codes d'erreur, config)
โ”‚       โ”œโ”€โ”€ error-catalog.ts        # Catalogue d'erreurs
โ”‚       โ”œโ”€โ”€ init-users.ts           # Initialisation utilisateurs admin
โ”‚       โ””โ”€โ”€ validation.ts           # Schรฉmas de validation Zod
โ”œโ”€โ”€ Dockerfile                      # Image de production
โ”œโ”€โ”€ Dockerfile.dev                  # Image de dรฉveloppement
โ”œโ”€โ”€ package.json
โ””โ”€โ”€ tsconfig.json

Fonctionnalitรฉs

1. Inscription (Register)

  • Validation des donnรฉes (email, username, password)
  • Vรฉrification de l'unicitรฉ (email et username)
  • Hashing du mot de passe avec bcrypt (10 rounds)
  • Crรฉation de l'utilisateur dans la base
  • Gรฉnรฉration d'un JWT
  • Retour du token dans un cookie HTTP-only

2. Connexion (Login)

  • Vรฉrification des credentials (username/email + password)
  • Support de l'authentification 2FA
    • Si 2FA activรฉ : retour d'un token temporaire
    • Si 2FA dรฉsactivรฉ : connexion directe
  • Gรฉnรฉration du JWT
  • Rate limiting strict (5 tentatives / minute)

3. Dรฉconnexion (Logout)

  • Invalidation du cookie token
  • Retour d'un cookie expirรฉ

4. Authentification ร  deux facteurs (2FA)

Configuration 2FA

  • Gรฉnรฉration d'un secret TOTP unique
  • Crรฉation d'une URL otpauth:// compatible Google Authenticator
  • Gรฉnรฉration d'un QR code (base64)
  • Stockage temporaire du secret (2 minutes)
  • Cookie de session setup

Vรฉrification du setup

  • Validation du code TOTP ร  6 chiffres
  • Activation permanente du 2FA dans le profil
  • Stockage du secret TOTP

Login avec 2FA

  • Aprรจs login initial : token temporaire (2 minutes)
  • Validation du code TOTP
  • Maximum 3 tentatives
  • Gรฉnรฉration du JWT final aprรจs validation

Dรฉsactivation 2FA

  • Par l'utilisateur (nรฉcessite mot de passe)
  • Par un admin (endpoint dรฉdiรฉ)

5. Vรฉrification de token (Verify)

  • Endpoint pour vรฉrifier la validitรฉ d'un JWT
  • Utilisรฉ par les autres services

6. Administration

  • Liste de tous les utilisateurs
  • Crรฉation d'utilisateurs
  • Mise ร  jour d'utilisateurs
  • Suppression d'utilisateurs
  • Dรฉsactivation forcรฉe du 2FA

API Endpoints

Important

Tous les endpoints sont prรฉfixรฉs par /api/auth

Exemple complet : https://URL:PORT/api/auth/login

Endpoints publics (non protรฉgรฉs)

Mรฉthode Route Description Rate Limit Body
GET / Health check basique - -
GET /health Health check dรฉtaillรฉ - -
POST /register Inscription d'un nouvel utilisateur 5/5min (dev: 100) { username, email, password }
POST /login Connexion 5/5min (dev: 100) { username ou email, password }

Dรฉtails /register:

  • Validation stricte avec Zod (username 4-20 chars alphanumรฉriques + _, email valide, password 8+ chars avec maj, min, chiffre et caractรจre spรฉcial !@#$%^&*)
  • Vรฉrifie l'unicitรฉ du username et de l'email
  • Hash du password avec bcrypt (10 rounds)
  • Crรฉe le profil utilisateur via le service UM (User Management)
  • Retourne les infos utilisateur (pas de token automatique - nรฉcessite login)
  • Codes de retour:
    • 201 CREATED : Utilisateur crรฉรฉ
    • 400 BAD_REQUEST : Validation รฉchouรฉe
    • 409 CONFLICT : Username ou email dรฉjร  pris
    • 500 INTERNAL_SERVER_ERROR : Erreur serveur

Dรฉtails /login:

  • Accepte username OU email + password
  • Si 2FA activรฉ : retourne require2FA: true + cookie temporaire 2fa_login_token (2 min)
  • Si 2FA dรฉsactivรฉ : retourne JWT dans cookie token (1h)
  • Codes de retour:
    • 200 OK : Login rรฉussi (avec ou sans 2FA)
    • 400 BAD_REQUEST : Validation รฉchouรฉe
    • 401 UNAUTHORIZED : Credentials invalides
    • 500 INTERNAL_SERVER_ERROR : Erreur serveur

Endpoints protรฉgรฉs (JWT requis)

Ces endpoints nรฉcessitent un token JWT valide dans le cookie token OU le header Authorization: Bearer <token>. Le JWT est validรฉ et les headers x-user-id et x-user-name sont injectรฉs par la Gateway.

Mรฉthode Route Description Headers injectรฉs
POST /logout Dรฉconnexion (efface le cookie) x-user-name
GET /me Rรฉcupรฉrer infos utilisateur connectรฉ x-user-id, x-user-name
POST /heartbeat Met ร  jour le statut "en ligne" dans Redis (TTL: 30s) x-user-id

Dรฉtails /me:

  • Retourne : { id, username, email, role, is2FAEnabled }
  • Note: Endpoint marquรฉ "DEV ONLY" - ร  supprimer en production

Dรฉtails /heartbeat:

  • Rate limit: 10/10s
  • Utilisรฉ par le frontend pour maintenir le statut "en ligne"
  • Stocke un heartbeat dans Redis avec TTL de 30 secondes
  • Si pas de heartbeat pendant 30s, l'utilisateur est considรฉrรฉ offline

Endpoints 2FA

Mรฉthode Route Description Rate Limit Cookies requis
POST /2fa/setup Dรฉmarrer la configuration 2FA (gรฉnรจre QR code) 5/5min (dev: 100) token
POST /2fa/setup/verify Valider le code et activer la 2FA 5/5min (dev: 100) 2fa_setup_token
POST /2fa/verify Valider le code lors du login 5/5min (dev: 100) 2fa_login_token
POST /2fa/disable Dรฉsactiver la 2FA - token

Flow complet 2FA Setup:

  1. POST /2fa/setup (JWT requis)

    • Vรฉrifie que 2FA n'est pas dรฉjร  activรฉe
    • Gรฉnรจre un secret TOTP unique
    • Crรฉe une URL otpauth://totp/Transcendence:username?secret=...
    • Gรฉnรจre un QR code en base64
    • Stocke le secret temporairement (2 min) avec un token de session
    • Retourne : { qrCode: "data:image/png;base64,...", expiresIn: 120 }
    • Cookie 2fa_setup_token crรฉรฉ (secure, httpOnly, 2 min)
  2. POST /2fa/setup/verify (cookie 2fa_setup_token requis)

    • Body: { code: "123456" } (6 chiffres)
    • Valide le code TOTP avec le secret temporaire
    • Max 3 tentatives, sinon doit recommencer le setup
    • Si valide : active la 2FA dรฉfinitivement (stocke secret dans DB)
    • Gรฉnรจre un nouveau JWT et retourne cookie token
    • Supprime le cookie 2fa_setup_token

Flow complet 2FA Login:

  1. POST /login โ†’ retourne require2FA: true + cookie 2fa_login_token (2 min)

  2. POST /2fa/verify (cookie 2fa_login_token requis)

    • Body: { code: "123456" } (6 chiffres)
    • Valide le code TOTP avec le secret permanent
    • Max 3 tentatives, sinon doit se reconnecter
    • Si valide : gรฉnรจre JWT final et retourne cookie token
    • Supprime le cookie 2fa_login_token

Dรฉtails /2fa/disable:

  • Nรฉcessite JWT valide
  • Vรฉrifie que 2FA est activรฉe
  • Supprime le secret TOTP de la DB
  • Dรฉsactive is_2fa_enabled dans le profil utilisateur

Endpoints admin (rรดle ADMIN requis)

Tous ces endpoints nรฉcessitent un JWT valide + rรดle ADMIN. La vรฉrification est faite via le prรฉ-handler verifyAdminRole qui lit les headers x-user-id et x-user-name.

Mรฉthode Route Description Rate Limit Body
GET /admin/users Liste tous les utilisateurs 50/min -
PUT /admin/users/:id Modifier un utilisateur 20/min { username, email, role }
DELETE /admin/users/:id Supprimer un utilisateur 10/min -
POST /admin/users/:id/disable-2fa Forcer dรฉsactivation 2FA 10/min -

Dรฉtails /admin/users (GET):

  • Retourne tous les utilisateurs avec :
    • Infos de base : id, username, email, role
    • ร‰tat 2FA : is2FAEnabled
    • Statut en ligne : online (via Redis bulk check)
  • Response: { users: [{ id, username, email, role, is2FAEnabled, online }, ...] }

Dรฉtails /admin/users/:id (PUT):

  • Permet de modifier username, email et role
  • Vรฉrifie l'unicitรฉ du username et de l'email
  • Role doit รชtre user ou admin
  • Codes de retour:
    • 200 OK : Utilisateur modifiรฉ
    • 400 BAD_REQUEST : Donnรฉes invalides
    • 403 FORBIDDEN : Non admin
    • 404 NOT_FOUND : Utilisateur inexistant
    • 409 CONFLICT : Username ou email dรฉjร  utilisรฉ

Dรฉtails /admin/users/:id (DELETE):

  • Empรชche l'auto-suppression (admin ne peut pas se supprimer)
  • Supprime l'utilisateur de la DB Auth + appelle le service UM pour cleanup
  • Codes de retour:
    • 200 OK : Utilisateur supprimรฉ
    • 400 BAD_REQUEST : Tentative d'auto-suppression
    • 403 FORBIDDEN : Non admin
    • 404 NOT_FOUND : Utilisateur inexistant

Dรฉtails /admin/users/:id/disable-2fa (POST):

  • Dรฉsactive la 2FA d'un utilisateur (pour dรฉblocage)
  • Vรฉrifie que la 2FA est activรฉe avant de la dรฉsactiver
  • Codes de retour:
    • 200 OK : 2FA dรฉsactivรฉe
    • 400 BAD_REQUEST : 2FA dรฉjร  dรฉsactivรฉe
    • 403 FORBIDDEN : Non admin
    • 404 NOT_FOUND : Utilisateur inexistant

Endpoint de vรฉrification de statut

Mรฉthode Route Description Rate Limit Params
GET /is-online/:name Vรฉrifie si un utilisateur est en ligne 200/min (dev: 1000) name (username)

Dรฉtails /is-online/:name:

  • Retourne : { username: "...", isOnline: true/false }
  • Vรฉrifie dans Redis si un heartbeat existe (< 30s)
  • Codes de retour:
    • 200 OK : Status retournรฉ
    • 400 BAD_REQUEST : Username manquant
    • 404 NOT_FOUND : Utilisateur inexistant

Authentification JWT

Structure du token

Le JWT contient les informations suivantes dans le payload :

{
  "sub": 123,              // User ID (subject)
  "username": "alice",     // Nom d'utilisateur
  "role": "user",          // Rรดle (user | admin)
  "iat": 1640000000,       // Issued at (timestamp)
  "exp": 1640003600        // Expiration (timestamp)
}

Gรฉnรฉration du JWT

Cycle de vie

  1. Gรฉnรฉration : Lors du login rรฉussi ou aprรจs validation 2FA
  2. Stockage : Cookie HTTP-only nommรฉ token
  3. Validation : Par la Gateway avant chaque requรชte protรฉgรฉe
  4. Headers injectรฉs : x-user-id et x-user-name ajoutรฉs par la Gateway aprรจs validation
  5. Expiration : 1 heure (dรฉfini dans AUTH_CONFIG.JWT_EXPIRATION = '1h')

Configuration du cookie

{
  httpOnly: true,      // Pas accessible en JavaScript (protection XSS)
  secure: true,        // HTTPS uniquement (production, ou si FORCE_SECURE_COOKIE=true)
  sameSite: 'strict',  // Protection CSRF (note: pas 'lax')
  path: '/',           // Disponible sur toutes les routes
  maxAge: 3600         // 1 heure (sync avec JWT expiration)
}

Note: En dรฉveloppement, secure: false est autorisรฉ si NODE_ENV=development ET FORCE_SECURE_COOKIE n'est pas dรฉfini.

Validation du token

La Gateway valide les JWT pour des raisons de performance :

// La Gateway :
// 1. Extrait le token du cookie 'token' ou du header 'Authorization: Bearer <token>'
// 2. Vรฉrifie la signature JWT avec JWT_SECRET (identique cรดtรฉ Auth et Gateway)
// 3. Dรฉcode le payload
// 4. Injecte les headers x-user-id, x-user-name, x-user-role
// 5. Proxyfy la requรชte vers le service cible

Sรฉcuritรฉ JWT

โœ… Bonnes pratiques implรฉmentรฉes :

  • Secret d'au moins 32 caractรจres (validation stricte au dรฉmarrage avec envalid)
  • Algorithme HS256 (HMAC + SHA256) via @fastify/jwt
  • Validation stricte du format et de la signature
  • Vรฉrification de l'expiration automatique par @fastify/jwt
  • Cookie HTTP-only (protection XSS)
  • Cookie SameSite=strict (protection CSRF)
  • Cookie Secure en production (HTTPS only)
  • Validation locale par la Gateway

โš ๏ธ ร€ implรฉmenter (roadmap) :

  • Refresh tokens : pour prolonger la session sans re-login complet
  • Rรฉvocation de tokens : blacklist Redis pour logout forcรฉ/invalidation immรฉdiate
  • Rotation du secret JWT : permettre le changement du secret sans casser toutes les sessions
  • Token version/jti : invalider tous les tokens d'un utilisateur en une fois (ex: aprรจs changement de password)

2FA / TOTP

Fonctionnement du TOTP

TOTP (Time-based One-Time Password) gรฉnรจre des codes ร  6 chiffres basรฉs sur :

  • Un secret partagรฉ (stockรฉ dans la DB Auth et dans l'app authenticator de l'utilisateur)
  • Le temps actuel (fenรชtres de 30 secondes)
Code = HMAC-SHA1(secret, floor(timestamp / 30))

Paramรจtres TOTP:

  • Algorithme : SHA1 (standard TOTP)
  • Pรฉriode : 30 secondes (dรฉfini dans AUTH_CONFIG.TOTP_STEP = 30)
  • Digits : 6 chiffres (dรฉfini dans AUTH_CONFIG.TOTP_DIGITS = 6)
  • Window : ยฑ1 pรฉriode (ยฑ30s, dรฉfini dans AUTH_CONFIG.TOTP_WINDOW = 1)
  • Issuer : "Transcendence" (dรฉfini dans AUTH_CONFIG.TOTP_ISSUER via APP_NAME)

Configuration TOTP

// utils/constants.ts
TOTP_WINDOW: 1,          // ยฑ30 secondes de tolรฉrance
TOTP_STEP: 30,           // Pรฉriode de rotation (standard)
TOTP_DIGITS: 6,          // Code ร  6 chiffres
TOTP_ISSUER: 'Transcendence',
TOTP_SETUP_EXPIRATION_SECONDS: 120,  // Expiration du secret temporaire

Flow complet 2FA

1. Setup (activation initiale)

sequenceDiagram
    participant User
    participant Frontend
    participant Auth Service
    participant SQLite DB
    participant Authenticator App

    User->>Frontend: Clique "Activer 2FA"
    Frontend->>Auth Service: POST /2fa/setup (JWT token)
    Auth Service->>Auth Service: Vรฉrifie que 2FA pas dรฉjร  activรฉe
    Auth Service->>Auth Service: Gรฉnรจre secret TOTP alรฉatoire
    Auth Service->>Auth Service: Crรฉe URL otpauth://totp/...
    Auth Service->>Auth Service: Gรฉnรจre QR code (base64)
    Auth Service->>SQLite DB: INSERT INTO totp_setup_secrets (token, user_id, secret, expires_at)
    Auth Service-->>Frontend: Cookie 2fa_setup_token (2 min) + QR code
    Frontend->>User: Affiche QR code
    User->>Authenticator App: Scanne QR code
    Authenticator App->>Authenticator App: Stocke secret localement
    Authenticator App->>User: Affiche code 6 chiffres
    User->>Frontend: Entre code
    Frontend->>Auth Service: POST /2fa/setup/verify + code
    Auth Service->>SQLite DB: Rรฉcupรจre secret temporaire via token
    Auth Service->>Auth Service: Valide code TOTP (max 3 tentatives)
    Auth Service->>SQLite DB: UPDATE users SET is_2fa_enabled=1, totp_secret=...
    Auth Service->>SQLite DB: DELETE FROM totp_setup_secrets
    Auth Service->>Auth Service: Gรฉnรจre nouveau JWT
    Auth Service-->>Frontend: Cookie token (1h)
    Frontend->>User: "2FA activรฉe โœ“"
Loading

2. Login avec 2FA

sequenceDiagram
    participant User
    participant Frontend
    participant Auth Service
    participant SQLite DB
    participant Authenticator App

    User->>Frontend: Entre username + password
    Frontend->>Auth Service: POST /login
    Auth Service->>SQLite DB: Vรฉrifie credentials
    Auth Service->>SQLite DB: Vรฉrifie si is_2fa_enabled=1
    Auth Service->>Auth Service: Gรฉnรจre loginToken temporaire
    Auth Service->>SQLite DB: INSERT INTO login_tokens
    Auth Service-->>Frontend: Cookie 2fa_login_token (2 min) + require2FA: true
    Frontend->>User: Demande code 2FA
    User->>Authenticator App: Ouvre app
    Authenticator App->>User: Affiche code 6 chiffres
    User->>Frontend: Entre code
    Frontend->>Auth Service: POST /2fa/verify + code
    Auth Service->>SQLite DB: Rรฉcupรจre user_id via loginToken
    Auth Service->>SQLite DB: Rรฉcupรจre totp_secret de l'utilisateur
    Auth Service->>Auth Service: Valide code TOTP (max 3 tentatives)
    Auth Service->>SQLite DB: DELETE FROM login_tokens
    Auth Service->>Auth Service: Gรฉnรจre JWT final
    Auth Service-->>Frontend: Cookie token (1h)
    Frontend->>User: Connectรฉ โœ“
Loading

Tables de base de donnรฉes

Table users (extraits colonnes 2FA)

CREATE TABLE users (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  username TEXT UNIQUE NOT NULL,
  email TEXT UNIQUE NOT NULL,
  password_hash TEXT NOT NULL,
  role TEXT DEFAULT 'user',  -- 'user' | 'admin'
  is_2fa_enabled INTEGER DEFAULT 0,  -- Boolean (0 ou 1)
  totp_secret TEXT,                   -- Secret TOTP chiffrรฉ (ou NULL si 2FA dรฉsactivรฉe)
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

Table totp_setup_secrets

Stockage temporaire pendant la configuration (2 minutes) :

CREATE TABLE totp_setup_secrets (
  token TEXT PRIMARY KEY,                       -- Token UUID alรฉatoire
  user_id INTEGER NOT NULL,
  secret TEXT NOT NULL,                         -- Secret TOTP temporaire
  expires_at DATETIME NOT NULL,                 -- Timestamp d'expiration (now + 120s)
  attempts INTEGER DEFAULT 0,                   -- Compteur de tentatives (max 3)
  FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
);

Nettoyage: Supprimรฉ automatiquement aprรจs validation rรฉussie ou expiration.

Table login_tokens

Tokens temporaires pour le login 2FA (2 minutes) :

CREATE TABLE login_tokens (
  token TEXT PRIMARY KEY,                       -- Token UUID alรฉatoire
  user_id INTEGER NOT NULL,
  expires_at DATETIME NOT NULL,                 -- Timestamp d'expiration (now + 120s)
  FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
);

Table login_token_attempts

Compteur de tentatives (max 3) pour les login tokens :

CREATE TABLE login_token_attempts (
  token TEXT PRIMARY KEY,                       -- Rรฉfรฉrence au login_token
  attempts INTEGER DEFAULT 0,                   -- Nombre de tentatives รฉchouรฉes (max 3)
  FOREIGN KEY(token) REFERENCES login_tokens(token) ON DELETE CASCADE
);

Logique de sรฉcuritรฉ:

  • Max 3 tentatives de code TOTP
  • Au-delร  : suppression du login_token โ†’ utilisateur doit se reconnecter avec username/password

Nettoyage automatique

Le service effectue un nettoyage pรฉriodique des sessions expirรฉes :

// index.ts
setInterval(() => {
  totpService.cleanupExpiredSessions();
  authService.cleanupExpiredTokens();
}, AUTH_CONFIG.CLEANUP_INTERVAL_MS);  // 5 minutes

ร‰lรฉments nettoyรฉs:

  • totp_setup_secrets expirรฉs (> 2 min)
  • login_tokens expirรฉs (> 2 min)
  • Entrรฉes orphelines dans login_token_attempts
  • Au dรฉmarrage du service
  • Toutes les 5 minutes (maintenance automatique)

Sรฉcuritรฉ

Mesures de protection implรฉmentรฉes

1. Rate Limiting

Limites adaptรฉes selon l'environnement (dev/test vs production) :

Endpoint Limite Prod Limite Dev/Test Fenรชtre
Global 1000 req 10000 req 1 minute
/register 5 req 100 req 5 minutes
/login 5 req 100 req 5 minutes
/2fa/setup 5 req 100 req 5 minutes
/2fa/setup/verify 5 req 100 req 5 minutes
/2fa/verify 5 req 100 req 5 minutes
/heartbeat 10 req 10 req 10 secondes
/is-online/:name 200 req 1000 req 1 minute
/admin/users (GET) 50 req 50 req 1 minute
/admin/users/:id (PUT) 20 req 20 req 1 minute
/admin/users/:id (DELETE) 10 req 10 req 1 minute
/admin/users/:id/disable-2fa 10 req 10 req 1 minute

Configuration:

// utils/constants.ts
const isTestOrDev = ['test', 'development'].includes(authenv.NODE_ENV);

RATE_LIMIT: {
  LOGIN: {
    max: isTestOrDev ? 100 : 5,
    timeWindow: '5 minutes',
  },
  // ...
}

Implรฉmentation: Plugin @fastify/rate-limit configurรฉ par route.

2. Hashing des mots de passe

  • Algorithme : bcrypt (via bcrypt npm package)
  • Rounds : 10 (dรฉfini dans AUTH_CONFIG.BCRYPT_ROUNDS = 10)
  • Jamais de stockage en clair
  • Salt : gรฉnรฉrรฉ automatiquement par bcrypt (intรฉgrรฉ au hash)
import bcrypt from 'bcrypt';

// Hash lors de l'inscription
const hashedPassword = await bcrypt.hash(password, AUTH_CONFIG.BCRYPT_ROUNDS);

// Vรฉrification lors du login
const isValid = await bcrypt.compare(password, user.password_hash);

3. Validation des entrรฉes

Utilisation de Zod pour valider tous les inputs utilisateur :

// utils/validation.ts
const registerSchema = z.object({
  username: z.string()
    .min(4, 'Username must be at least 4 characters')
    .max(20, 'Username must be at most 20 characters')
    .regex(/^[a-zA-Z0-9_]+$/, 'Username must contain only letters, numbers and underscores')
    .refine(val => !RESERVED_USERNAMES.includes(val.toLowerCase()),
            { message: 'This username is reserved' }),

  email: z.string()
    .email('Invalid email format')
    .max(100, 'Email too long'),

  password: z.string()
    .min(8, 'Password must be at least 8 characters')
    .max(100, 'Password too long')
    .regex(/^(?=.*[a-z])/, 'Must contain lowercase letter')
    .regex(/^(?=.*[A-Z])/, 'Must contain uppercase letter')
    .regex(/^(?=.*\d)/, 'Must contain a number')
    .regex(/^(?=.*[!@#$%^&*])/, 'Must contain special character (!@#$%^&*)')
});

Usernames rรฉservรฉs:

// utils/constants.ts
const RESERVED_USERNAMES = [
  'admin', 'root', 'system', 'administrator',
  'superuser', 'guest', 'support', 'service', 'daemon'
];

4. Protection des donnรฉes sensibles

Champs sensibles jamais loggรฉs en clair:

// utils/constants.ts
const SENSITIVE_FIELDS = [
  'password', 'token', 'secret', 'totp_secret',
  'jwt', 'authorization', 'cookie', 'loginToken',
  '2fa_login_token', '2fa_setup_token',
  'access_token', 'refresh_token',
];

const REDACT_PATHS = [
  'req.headers.authorization',
  'req.headers.cookie',
  ...SENSITIVE_FIELDS.map(field => `*.${field}`),
];

Configuration Pino logger:

// config/logger.config.ts
import pino from 'pino';

const logger = pino({
  redact: {
    paths: REDACT_PATHS,
    censor: '[REDACTED]',
  },
});

5. Cookies sรฉcurisรฉs

Configuration stricte des cookies JWT:

  • httpOnly: true โ†’ Pas accessible en JavaScript (protection XSS)
  • secure: true โ†’ HTTPS uniquement en production
  • sameSite: 'strict' โ†’ Protection CSRF
  • path: '/' โ†’ Disponible sur toutes les routes
  • Pas de cookie persistent โ†’ Expiration synchronisรฉe avec JWT (1h)

6. Headers de sรฉcuritรฉ

Headers automatiquement ajoutรฉs par Fastify (via plugins ou configuration) :

  • X-Content-Type-Options: nosniff โ†’ Empรชche le MIME sniffing
  • X-Frame-Options: DENY โ†’ Empรชche l'iframe (protection clickjacking)
  • X-XSS-Protection: 1; mode=block โ†’ Active la protection XSS du navigateur

7. Protection 2FA

  • Max 3 tentatives de code TOTP (setup et login)
  • Expiration courte des tokens temporaires (2 minutes)
  • Nettoyage automatique des sessions expirรฉes (toutes les 5 minutes)
  • Window TOTP de ยฑ30s pour compenser le dรฉcalage horaire

8. Protection Admin

  • Vรฉrification du rรดle via prรฉ-handler verifyAdminRole
  • Empรชche l'auto-suppression (admin ne peut pas se supprimer)
  • Logs dรฉtaillรฉs de toutes les actions admin (avec user_id et username)

9. Sรฉcuritรฉ de la base de donnรฉes

  • Requรชtes prรฉparรฉes : utilisation de better-sqlite3 avec placeholders (?)
  • Transactions : pour les opรฉrations critiques (register + crรฉation profil UM)
  • Contraintes DB : UNIQUE sur username et email, FOREIGN KEY avec ON DELETE CASCADE
  • Isolation : base SQLite locale, pas d'accรจs direct depuis l'extรฉrieur

Systรจme de statut en ligne (Redis)

Le service Auth gรจre le statut "en ligne" des utilisateurs via Redis.

Architecture

Frontend โ†’ POST /heartbeat (toutes les 10-20s)
         โ†’ Auth Service โ†’ Redis SET user:{userId}:online EX 30

Autre service โ†’ GET /is-online/:name
              โ†’ Auth Service โ†’ Redis GET user:{userId}:online

Heartbeat

Endpoint: POST /heartbeat

// services/online.service.ts
async function recordHeartbeat(userId: number): Promise<void> {
  const key = `user:${userId}:online`;
  await redis.setex(key, 30, Date.now().toString());
  // TTL: 30 secondes
}

Logique:

  • Le frontend envoie un heartbeat toutes les 10-20 secondes
  • Redis stocke une clรฉ avec TTL de 30 secondes
  • Si pas de heartbeat pendant 30s โ†’ clรฉ expirรฉe โ†’ utilisateur offline

Rate limit: 10 requรชtes / 10 secondes (pour รฉviter le spam)

Vรฉrification du statut

Endpoint: GET /is-online/:name

async function isUserOnline(userId: number): Promise<boolean> {
  const key = `user:${userId}:online`;
  const result = await redis.get(key);
  return result !== null;
}

Bulk check (pour /admin/users) :

async function getBulkOnlineStatus(userIds: number[]): Promise<Map<number, boolean>> {
  const pipeline = redis.pipeline();
  userIds.forEach(id => pipeline.get(`user:${id}:online`));

  const results = await pipeline.exec();
  const statusMap = new Map<number, boolean>();

  userIds.forEach((id, index) => {
    statusMap.set(id, results[index][1] !== null);
  });

  return statusMap;
}

Nettoyage

Redis gรจre automatiquement l'expiration des clรฉs (TTL 30s).

Un nettoyage supplรฉmentaire est effectuรฉ toutes les 60 secondes :

// index.ts
setInterval(() => {
  onlineService.cleanupStaleHeartbeats();
}, AUTH_CONFIG.ONLINE_STATUS_CLEANUP_INTERVAL_MS);  // 60 secondes

Configuration Redis

// config/env.ts
REDIS_HOST: str({ default: 'redis-broker' }),
REDIS_PORT: port({ default: 6379 }),
REDIS_PASSWORD: str({ default: '' }),

Connexion:

// services/online.service.ts
import Redis from 'ioredis';

const redis = new Redis({
  host: authenv.REDIS_HOST,
  port: authenv.REDIS_PORT,
  password: authenv.REDIS_PASSWORD,
  lazyConnect: true,
  retryStrategy: (times) => Math.min(times * 50, 2000),
});

Base de donnรฉes (SQLite)

Configuration

  • Moteur : SQLite via better-sqlite3
  • Chemin : /data/auth.db (configurable via AUTH_DB_PATH)
  • Mode : WAL (Write-Ahead Logging) pour de meilleures performances
  • Contraintes : Foreign keys activรฉes
// services/database.ts
import Database from 'better-sqlite3';

const db = new Database(authenv.AUTH_DB_PATH);
db.pragma('journal_mode = WAL');
db.pragma('foreign_keys = ON');

Schรฉma complet

Table users

CREATE TABLE users (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  username TEXT UNIQUE NOT NULL,
  email TEXT UNIQUE NOT NULL,
  password_hash TEXT NOT NULL,
  role TEXT DEFAULT 'user' CHECK(role IN ('user', 'admin')),
  is_2fa_enabled INTEGER DEFAULT 0 CHECK(is_2fa_enabled IN (0, 1)),
  totp_secret TEXT,
  created_at TEXT DEFAULT (datetime('now')),
  updated_at TEXT DEFAULT (datetime('now'))
);

CREATE INDEX idx_users_username ON users(username);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_role ON users(role);

Colonnes:

  • id : ID unique auto-incrรฉmentรฉ
  • username : Nom d'utilisateur unique (4-20 chars)
  • email : Email unique (validรฉ cรดtรฉ app)
  • password_hash : Hash bcrypt du mot de passe (60 chars)
  • role : Rรดle ('user' | 'admin')
  • is_2fa_enabled : Boolean (0 ou 1)
  • totp_secret : Secret TOTP chiffrรฉ (NULL si 2FA dรฉsactivรฉe)
  • created_at : Date de crรฉation (ISO 8601)
  • updated_at : Date de derniรจre modification (ISO 8601)

Table totp_setup_secrets

CREATE TABLE totp_setup_secrets (
  token TEXT PRIMARY KEY,
  user_id INTEGER NOT NULL,
  secret TEXT NOT NULL,
  expires_at TEXT NOT NULL,
  attempts INTEGER DEFAULT 0,
  FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
);

CREATE INDEX idx_totp_setup_expires ON totp_setup_secrets(expires_at);
CREATE INDEX idx_totp_setup_user ON totp_setup_secrets(user_id);

Usage: Stockage temporaire du secret TOTP pendant la configuration (2 min).

Table login_tokens

CREATE TABLE login_tokens (
  token TEXT PRIMARY KEY,
  user_id INTEGER NOT NULL,
  expires_at TEXT NOT NULL,
  FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
);

CREATE INDEX idx_login_tokens_expires ON login_tokens(expires_at);
CREATE INDEX idx_login_tokens_user ON login_tokens(user_id);

Usage: Tokens temporaires pour le login 2FA (2 min).

Table login_token_attempts

CREATE TABLE login_token_attempts (
  token TEXT PRIMARY KEY,
  attempts INTEGER DEFAULT 0,
  FOREIGN KEY(token) REFERENCES login_tokens(token) ON DELETE CASCADE
);

Usage: Compteur de tentatives de validation TOTP (max 3).

Requรชtes prรฉparรฉes

Toutes les requรชtes utilisent des prepared statements pour รฉviter les injections SQL :

// โœ… Bon (prepared statement)
const stmt = db.prepare('SELECT * FROM users WHERE username = ?');
const user = stmt.get(username);

// โŒ Mauvais (injection SQL possible)
const user = db.prepare(`SELECT * FROM users WHERE username = '${username}'`).get();

Transactions

Les opรฉrations critiques utilisent des transactions :

// services/auth.service.ts
function createUser(data: CreateUserInput): number {
  const transaction = db.transaction(() => {
    // 1. Crรฉer utilisateur dans Auth DB
    const result = db.prepare(`
      INSERT INTO users (username, email, password_hash, role)
      VALUES (?, ?, ?, ?)
    `).run(data.username, data.email, data.password_hash, 'user');

    const userId = result.lastInsertRowid as number;

    // 2. Crรฉer profil dans User Management Service
    await umService.createProfile(userId, data.username, data.email);

    return userId;
  });

  return transaction();
}

Initialisation

Au dรฉmarrage, le service :

  1. Crรฉe les tables si elles n'existent pas
  2. Crรฉe un utilisateur admin par dรฉfaut (si pas dรฉjร  existant)
  3. Crรฉe un utilisateur invite par dรฉfaut (si pas dรฉjร  existant)
// utils/init-users.ts
export function initializeDefaultUsers() {
  // Admin user
  if (!authService.findByUsername(authenv.ADMIN_USERNAME)) {
    authService.createUser({
      username: authenv.ADMIN_USERNAME,
      email: authenv.ADMIN_EMAIL,
      password: authenv.ADMIN_PASSWORD,
      role: UserRole.ADMIN,
    });
  }

  // Invite user
  if (!authService.findByUsername(authenv.INVITE_USERNAME)) {
    authService.createUser({
      username: authenv.INVITE_USERNAME,
      email: authenv.INVITE_EMAIL,
      password: authenv.INVITE_PASSWORD,
      role: UserRole.USER,
    });
  }
}

Backup et persistance

  • Volume Docker : /data montรฉ sur l'hรดte (data/database/auth.db)
  • Mode WAL : fichiers -shm et -wal crรฉรฉs automatiquement
  • Pas de migration automatique : schรฉma stable, migrations manuelles si nรฉcessaire

Tests et validation

Scripts de test

Le projet fournit des scripts de test bash :

# Test du service Auth complet
./test-auth.sh

# Test cross-service (Auth + Users)
./test-auth-cross.sh

Tests manuels avec curl

Register

curl -X POST http://localhost:3001/register \
  -H "Content-Type: application/json" \
  -d '{
    "username": "testuser",
    "email": "test@example.com",
    "password": "Test123!@#"
  }'

Login

curl -X POST http://localhost:3001/login \
  -H "Content-Type: application/json" \
  -c cookies.txt \
  -d '{
    "username": "testuser",
    "password": "Test123!@#"
  }'

Heartbeat

curl -X POST http://localhost:3001/heartbeat \
  -b cookies.txt

Admin - List users

curl -X GET http://localhost:3001/admin/users \
  -b cookies.txt

Service Gateway

  • Vรฉrifier le statut en ligne : GET /is-online/:name

Headers injectรฉs par la Gateway aprรจs validation :

  • x-user-id : ID de l'utilisateur
  • x-user-name : Username de l'utilisateur
  • x-user-role : Rรดle de l'utilisateur

Service Redis

Le service Auth utilise Redis pour :

  1. Heartbeats : Statut en ligne des utilisateurs
  2. Futures features : Token blacklist, session store, rate limiting distribuรฉ

Roadmap et amรฉliorations

Prioritรฉ haute

  • Refresh tokens : Prolonger la session sans re-login
  • Token blacklist : Invalider les JWT (logout forcรฉ)
  • Account recovery : Reset password par email
  • Email verification : Valider l'email lors de l'inscription
  • Tests unitaires : Vitest pour chaque service/controller

Prioritรฉ moyenne

  • Rotation JWT secret : Permettre le changement du secret sans casser les sessions
  • Session management : Vue admin des sessions actives
  • IP whitelisting : Restreindre les connexions admin par IP
  • Audit logs : Logs persistรฉs pour compliance
  • CAPTCHA : Protection brute force avancรฉe

Prioritรฉ basse

  • OAuth providers : Login avec Google, GitHub, 42
  • Multi-device 2FA : Backup codes, SMS
  • Rate limiting distribuรฉ : Via Redis pour scale horizontale

Rรฉfรฉrences

Documentation interne

Documentation externe


Contributeurs

Rom9

Derniรจre mise ร  jour: 27 janvier 2026

๐Ÿ—๏ธ Architecture

๐ŸŒ Web Technologies

Backend

Frontend

๐Ÿ”ง Core Technologies

๐Ÿ” Security

โ›“๏ธ Blockchain

๐Ÿ› ๏ธ Dev Tools & Quality


๐Ÿ“ Page model

Clone this wiki locally