api-authentication
Vue d'ensemble
Le service api-authentication est le point d'entrée principal de la plateforme Roomee. Il gère l'authentification, l'autorisation et la gestion des utilisateurs.
Deux Versions Coexistantes
Le projet contient actuellement deux versions du service d'authentification :
- api-authentication (moderne) - TypeScript, architecture modulaire
- api-auth-legacy - JavaScript, structure legacy en cours de migration
Les deux services fonctionnent en parallèle pendant la période de transition.
| Version | Port | Stack | Status |
|---|---|---|---|
| Moderne | 3000 | TypeScript, Modules | ✅ Actif |
| Legacy | 3005 | JavaScript, Flat Structure | 🔄 En migration |
Architecture Comparée
Service Moderne (TypeScript)
graph TB
CLIENT[Client Web/Mobile]
subgraph "api-authentication (Moderne - TS)"
ROUTES[Routes Express]
subgraph "Modules"
AUTH_MOD[Module Auth]
USER_MOD[Module User]
PASS_MOD[Module Password]
end
subgraph "Services"
AUTH_SVC[AuthService]
USER_SVC[UserService]
TOKEN_SVC[TokenService]
end
ROUTES --> AUTH_MOD
AUTH_MOD --> AUTH_SVC
AUTH_SVC --> TOKEN_SVC
end
CLIENT --> ROUTESService Legacy (JavaScript)
graph TB
CLIENT2[Client Web/Mobile]
subgraph "api-auth-legacy (JS)"
ROUTES2[Routes]
subgraph "Controllers"
AUTH_CTRL[AuthController<br/>11 méthodes]
USER_CTRL[UserController<br/>14 méthodes]
end
subgraph "Repositories"
AUTH_REPO[AuthRepository]
USER_REPO[UserRepository]
NOTIF_REPO[NotificationsRepository]
end
ROUTES2 --> AUTH_CTRL
ROUTES2 --> USER_CTRL
AUTH_CTRL --> AUTH_REPO
USER_CTRL --> USER_REPO
end
CLIENT2 --> ROUTES2Structure des Projets
Service Moderne (TypeScript)
api-authentication/
├── src/
│ ├── app.ts # Configuration Express
│ ├── server.ts # Point d'entrée
│ ├── modules/
│ │ └── auth/
│ │ ├── controllers/ # Controllers (5)
│ │ ├── services/ # Business logic
│ │ ├── routes/ # Routes Express
│ │ ├── types/ # Interfaces TypeScript
│ │ └── validators/ # Zod schemas
│ ├── config/
│ ├── prisma-client/
│ └── middleware/
├── prisma/
│ └── schema.prisma
└── __tests/Service Legacy (JavaScript)
api-auth-legacy/
├── controllers/
│ ├── AuthController.js # 11 méthodes auth
│ └── UserController.js # 14 méthodes user
├── repository/
│ ├── AuthRepository.js # Tokens, sessions
│ ├── UserRepository.js # CRUD users
│ ├── NotificationsRepository.js
│ └── EmailRepository.js
├── routes/
│ ├── auth.routes.js # 10 endpoints
│ ├── user.routes.js # 11 endpoints
│ └── docs.routes.js
├── middlewares/
│ ├── auth.js # Legacy JWT middleware
│ ├── error.js
│ └── rateLimiter.js
├── utils/
│ ├── jwt.js # JWT generation (legacy)
│ ├── encryption.js # bcrypt + AES
│ └── hashToken.js
├── amqp/ # Communication AMQP
├── socket/ # Socket.IO
├── gRPC/ # gRPC (non utilisé)
├── config/
│ ├── config.js # Infisical integration
│ └── logger.js # Winston
├── prisma/
│ └── schema.prisma
└── app.jsModèles de Données
Modèles Communs
User
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String @unique
password String
// Flags (spécifiques au Legacy)
isTmpPassword Boolean @default(false) // Mot de passe temporaire
isFirstLogin Boolean @default(false) // Premier login
isForced Boolean @default(false) // Force changement MDP
// Status
isActive Boolean @default(true)
isAdmin Boolean @default(false)
emailVerified Boolean @default(false)
// Dates
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
lastLoginAt DateTime?
// Relations
refreshTokens RefreshToken[]
sessions UserSession[]
invitationsSent InvitationToken[] // Moderne uniquement
passwordRequests RequestCreatePassword[]
ecosystemCredentials EcosystemCredentials[] // Moderne uniquement
}Champs Legacy importants :
isTmpPassword: Indique un mot de passe temporaire (invitation/admin reset)isFirstLogin: Détecte la première connexionisForced: Force le changement de mot de passe à la prochaine connexion
RefreshToken
model RefreshToken {
id String @id @default(auto()) @map("_id") @db.ObjectId
// Moderne
token String @unique // Token JWT en clair
isRevoked Boolean @default(false)
// Legacy
hashedToken String // Token hashé (SHA256)
revoked Boolean @default(false) // Alias de isRevoked
userId String @db.ObjectId
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
expiresAt DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
@@index([token])
@@index([hashedToken])
}UserSession
model UserSession {
id String @id @default(auto()) @map("_id") @db.ObjectId
// Moderne
userId String? @db.ObjectId
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
deviceInfo Json? // { userAgent, ip, device, os }
expiresAt DateTime?
// Legacy
memberId String // ID du membre (api-staff-member)
userAgent String
ip String
appType String // "mobile" ou "dashboard"
timestamp DateTime @default(now())
// Commun
isActive Boolean @default(true)
lastActivity DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
@@index([memberId])
@@index([isActive])
}RequestCreatePassword (Legacy)
model RequestCreatePassword {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String
hashedToken String // Token JWT hashé
couponCode String // Code à 6 chiffres
// Moderne (optionnel)
userId String? @db.ObjectId
user User? @relation(fields: [userId], references: [id])
token String? @unique
isUsed Boolean? @default(false)
expiresAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([email])
@@index([hashedToken])
@@index([couponCode])
@@index([token])
}Usage Legacy : Workflow de reset avec code à 6 chiffres
Modèles Spécifiques Moderne
InvitationToken
model InvitationToken {
id String @id @default(auto()) @map("_id") @db.ObjectId
invitedBy String @db.ObjectId
email String
token String @unique
role String // 'admin', 'member', 'viewer'
workspaceId String?
expiresAt DateTime
isUsed Boolean @default(false)
createdAt DateTime @default(now())
inviter User @relation(fields: [invitedBy], references: [id])
@@index([email])
@@index([token])
}EcosystemCredentials
model EcosystemCredentials {
id String @id @default(auto()) @map("_id") @db.ObjectId
userId String @db.ObjectId
ecosystemId String
apiKey String @unique
apiSecret String // Encrypted
permissions String[]
isActive Boolean @default(true)
createdAt DateTime @default(now())
expiresAt DateTime?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@index([ecosystemId])
@@index([apiKey])
}Modèles Spécifiques Legacy
Administrator
model Administrator {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String @unique
password String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}Usage : Compte super-administrateur système (distinct des users)
UserLog
model UserLog {
id String @id @default(auto()) @map("_id") @db.ObjectId
timestamp DateTime @default(now())
userId String? @db.ObjectId
level String // error, warn, info, debug
message String
meta Json?
}Usage : Logs applicatifs stockés en base (en plus de Winston)
Routes API - Comparaison
Routes Communes
| Endpoint | Moderne (TS) | Legacy (JS) | Différences |
|---|---|---|---|
POST /auth/register | ✅ | ✅ | Legacy : registerTemp avec token membre |
POST /auth/login | ✅ | ✅ /auth/login/:type | Legacy : type mobile/dashboard |
POST /auth/refreshToken | ✅ /auth/refresh | ✅ | Noms différents |
POST /auth/logout | ✅ | - | Moderne uniquement |
POST /auth/verify-email | ✅ | - | Moderne uniquement |
GET /users/me | ✅ /user/profile | ✅ | Paths différents |
PATCH /users/me | ✅ | - | Moderne uniquement |
DELETE /users/me | ✅ | - | Moderne uniquement |
POST /password/forgot | ✅ /user/sentreset/email | ✅ | Paths différents |
POST /password/reset | ✅ /user/change/Password | ✅ | Paths différents |
POST /password/change | ✅ /user/changePassword | ✅ | Moderne : nécessite auth |
Routes Spécifiques Legacy
POST /auth/loginAsAdmin
Connexion en tant qu'administrateur système.
Request :
{
"email": "admin@roomee.io",
"password": "AdminPassword"
}Response :
{
"accessToken": "eyJhbGciOiJI...",
"refreshToken": "8f7d6c5b4a3...",
"admin": {
"id": "65f1234567890abcdef12345",
"email": "admin@roomee.io"
}
}POST /auth/invitation
Création ou mise à jour d'un compte avec mot de passe temporaire.
Request :
{
"email": "newuser@hotel.com",
"password": "TempP@ss123",
"memberId": "65f9876543210fedcba98765"
}Response :
{
"success": true,
"user": {
"id": "65f1234567890abcdef12345",
"email": "newuser@hotel.com",
"isTmpPassword": true,
"isFirstLogin": true
}
}Logique :
- Création ou mise à jour du compte User
- Flag
isTmpPassword: true - L'utilisateur doit changer son mot de passe lors de la première connexion
POST /auth/set-login-data
Configuration des données de connexion (flags système).
Request :
{
"userId": "65f1234567890abcdef12345",
"isTmpPassword": false,
"isFirstLogin": false,
"isForced": false
}DELETE /auth/delete/:email
Suppression d'un utilisateur par email (admin).
PUT /auth/change_mail
Changement d'email utilisateur.
Request :
{
"oldEmail": "old@example.com",
"newEmail": "new@example.com"
}POST /auth/revokeRefreshTokens
Révocation de tous les refresh tokens d'un utilisateur.
Request :
{
"userId": "65f1234567890abcdef12345"
}POST /user/check/reset/code
Vérification du code de reset à 6 chiffres.
Request :
{
"email": "user@example.com",
"code": "123456"
}Response :
{
"success": true,
"data": {
"hashedToken": "...",
"couponCode": "123456"
}
}POST /user/change/Password/by/admin
Reset de mot de passe par un administrateur.
Request :
{
"email": "user@example.com"
}Response :
{
"success": true,
"temporaryPassword": "TempP@ss789",
"message": "Temporary password sent by email"
}Logique :
- Génération d'un mot de passe temporaire aléatoire
- Hash et stockage avec
isTmpPassword: true - Envoi d'email avec le mot de passe temporaire
PUT /user/changePassword
Changement de mot de passe dans l'application.
Headers :
Authorization: Bearer eyJhbGciOiJI...Request :
{
"oldPassword": "OldP@ss123",
"newPassword": "NewP@ss456"
}Response :
{
"success": true,
"msg": "password updated!",
"user": {
"isTmpPassword": false,
"isFirstLogin": false,
"isForced": false
}
}POST /user/reset_password/request
Demande de réinitialisation avec génération de code.
Request :
{
"email": "user@example.com"
}Response :
{
"email": "user@example.com",
"generatedcode": "123456",
"tokenFrontRoute": "https://app.roomee.io/reset-password?token=..."
}Logique :
- Génération d'un code à 6 chiffres aléatoire
- Génération d'un token JWT chiffré
- Stockage dans
RequestCreatePassword - Envoi d'email avec code et lien
- Expiration : 24h
POST /user/reset_password/request/check
Vérification de la demande de reset avec code.
DELETE /user/ecosystem/delete
Suppression complète d'un écosystème (transaction).
Request :
{
"ecosystemId": "65f1111111111111111111111"
}Logique :
- Suppression de tous les utilisateurs de l'écosystème
- Révocation de tous les tokens
- Suppression des sessions
- Transaction Prisma pour garantir l'atomicité
DELETE /user/tokens/:userId
Révocation de tous les tokens d'un user par ID.
POST /user/tokens/revoke
Révocation de tous les tokens d'un user par email.
GET /user/profileAsAdmin
Récupération du profil administrateur.
Routes Spécifiques Moderne
POST /invitations/send
Envoyer une invitation structurée.
POST /invitations/accept
Accepter une invitation avec création de compte.
GET /invitations
Lister les invitations envoyées.
POST /ecosystem/credentials
Générer des credentials API pour intégrations.
GET /ecosystem/credentials/:ecosystemId
Lister les credentials d'un écosystème.
DELETE /ecosystem/credentials/:id
Révoquer des credentials.
Workflow de Reset Password
Version Legacy (Code à 6 Chiffres)
sequenceDiagram
participant User
participant API as api-auth-legacy
participant Email as api-notification
participant DB as MongoDB
User->>API: POST /user/sentreset/email
API->>API: Generate 6-digit code
API->>API: Generate JWT token
API->>DB: Store RequestCreatePassword
API->>Email: Send email with code + link
Email-->>User: Email reçu
User->>API: POST /user/check/reset/code
Note over API: Validate code + token
API-->>User: { hashedToken, couponCode }
User->>API: PUT /user/change/Password
API->>DB: Update password
API->>DB: Set isTmpPassword=false
API-->>User: SuccessAvantages :
- Code court facile à saisir manuellement
- Double vérification (code + token JWT)
- Expiration 24h
Version Moderne (Token uniquement)
sequenceDiagram
participant User
participant API as api-authentication
participant Email as Service Email
participant DB as MongoDB
User->>API: POST /password/forgot
API->>DB: Create RequestCreatePassword
API->>API: Generate unique token
API->>Email: Send email with link
Email-->>User: Email reçu
User->>API: POST /password/reset
Note over API: Validate token (not used, not expired)
API->>DB: Update password
API->>DB: Mark token as used
API->>DB: Revoke all refresh tokens
API-->>User: SuccessAvantages :
- Plus simple (pas de code à saisir)
- Expiration 1h (plus court = plus sécurisé)
Génération de Tokens JWT
Service Moderne (@roomee/shared)
import { authMiddleware } from '@roomee/shared'
authMiddleware.initialize({
jwtAccessSecret: process.env.JWT_ACCESS_SECRET
})
// Génération de token
const token = authMiddleware.generateToken({
userId: user.id,
email: user.email,
isAdmin: user.isAdmin,
workspaceId: memberData.workspaceId,
roles: memberData.roles,
permissions: memberData.permissions
})
// Token chiffré avec AES-256-GCM
// Format : {encryptedText}.{iv}.{tag}Service Legacy (utils/jwt.js + @roomee/shared)
// Payload complet Legacy
const payload = {
userId: user.id,
email: user.email,
isAdmin: user.isAdmin || false,
// Données membre (api-staff-member)
ecosystemId: memberData.hotelId,
hotelId: memberData.hotelId,
memberId: memberData.id,
chatApiKey: memberData.sendBirdAccessToken,
firstname: memberData.firstname,
lastname: memberData.lastname,
picture: memberData.picture,
// Workspace
workspaceId: memberData.workspaces[0]?.id,
workspace_id: memberData.workspaces[0]?.id,
// RBAC
roles: ['user', 'admin', 'superadmin', 'member'],
permissions: ['member.management', 'page.management'],
// Flags
asAdminRole: false
}
// Génération via @roomee/shared
const accessToken = authMiddleware.generateToken(payload)
// Refresh token
const refreshToken = authMiddleware.generateRefreshToken({
userId: user.id,
jti: uuidv4() // JWT ID unique
})Différence majeure : Le legacy inclut beaucoup plus de données membre dans le payload JWT
Expiration des Tokens
| Token | Moderne | Legacy | Recommandation |
|---|---|---|---|
| Access Token | 15 min | 2400h (100 jours) ⚠️ | 15-60 min |
| Refresh Token | 7 jours | 7 jours | 7-30 jours |
| Reset Token | 1h | 24h | 1-24h |
| Invitation Token | 7 jours | 4h (create password) | 7 jours |
Problème de Sécurité Legacy
L'access token legacy expire dans 2400h (100 jours), ce qui est extrêmement long et pose un risque de sécurité majeur. Il devrait être réduit à 15-60 minutes maximum.
Communication AMQP
Service Legacy
Configuration :
{
rabbitMQUrl: config.amqpGatewayUrl,
exchangeName: config.amqpExchangeName,
queueName: 'authentication_queue',
routingKeyBase: 'roomee.authentication'
}Routing Keys :
roomee.authentication.*- Événements internesroomee.notification.socket- Notifications Socket.IOroomee.webhook.report-error- Rapports d'erreurs
Mode : Émission uniquement (pas de consommation)
Service Moderne
Configuration similaire avec routing keys modernes.
Intégration avec api-staff-member
Les deux services communiquent avec api-staff-member pour :
Récupération de Profil Membre
// Legacy
GET /members/get/light/:email
// Moderne
GET /members/get/with/:emailResponse :
{
"id": "65f9876543210fedcba98765",
"firstname": "John",
"lastname": "Doe",
"picture": "https://...",
"hotelId": "65f1111111111111111111111",
"workspaces": [
{
"id": "65f2222222222222222222222",
"name": "Grand Hotel Paris",
"role": "admin"
}
],
"roles": ["admin", "member"],
"permissions": ["manage_members", "create_content"],
"sendBirdAccessToken": "sendbird-token-abc123",
"deleted_at": null
}Marquage Registered (Legacy)
PUT /members/registered/update
Body: {
memberId: "65f9876543210fedcba98765"
}Marque le membre comme "registered" dans api-staff-member lors de la première connexion.
Création de Session (Legacy)
POST /sessions
Body: {
memberId: "65f9876543210fedcba98765",
userAgent: "Mozilla/5.0...",
ip: "192.168.1.1",
appType: "mobile" | "dashboard",
isActive: true
}Sécurité
Encryption AES-256-GCM (@roomee/shared)
import { encryptionService } from '@roomee/shared'
encryptionService.initialize(process.env.PASSWORD_ENCRYPTION_KEY)
// Encryption
const encrypted = await encryptionService.encrypt(plainText)
// Format : {encryptedText}.{iv}.{tag}
// Décryption
const decrypted = await encryptionService.decrypt(encrypted)
// Comparaison sécurisée (passwords)
const isValid = await encryptionService.compare(plain, encrypted)Hashing bcrypt (Passwords)
import bcrypt from 'bcrypt'
// Hash
const hashedPassword = await bcrypt.hash(plainPassword, 10)
// Vérification
const isMatch = await bcrypt.compare(plainPassword, hashedPassword)Hashing SHA256 (Tokens Legacy)
// utils/hashToken.js
import crypto from 'crypto'
function hashToken(token) {
return crypto.createHash('sha256').update(token).digest('hex')
}Usage : Les refresh tokens legacy sont hashés avant stockage en base.
Codes de Messages (Legacy)
Le service legacy utilise des codes de messages standardisés :
| Code | Description |
|---|---|
NO_MSG | Pas de message |
MSG_600 | Email et mot de passe requis |
MSG_601 | Identifiants invalides |
MSG_602 | Membre introuvable |
MSG_603 | Pas d'accès mobile |
MSG_604 | Pas d'accès web |
MSG_605 | Code de reset envoyé |
MSG_606 | Échec reset mot de passe |
MSG_620 | Mot de passe modifié avec succès |
MSG_621 | Erreur mise à jour mot de passe |
MSG_622 | Ancien mot de passe incorrect |
MSG_623 | Utilisateur inexistant |
MSG_624 | Session active en cours |
Usage :
res.status(401).json({
msg: ResponseMessage.MSG_601, // "Identifiants invalides"
success: false
})Logging
Service Moderne
Winston logger standard avec console + fichiers.
Service Legacy
Winston logger avancé avec multiples transports :
const transports = [
new winston.transports.Console(),
new winston.transports.DailyRotateFile({
filename: 'logs/error-%DATE%.log',
level: 'error',
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '14d'
}),
new winston.transports.DailyRotateFile({
filename: 'logs/combined-%DATE%.log',
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '14d'
}),
new winston.transports.MongoDB({
db: DATABASE_URL,
collection: 'UserLog',
level: 'info'
}),
new winston.transports.Http({
host: 'discord-webhook-url',
level: 'error' // Alertes critiques vers Discord
})
]Configuration
Variables Communes
# Server
PORT=3000 # Moderne
PORT=3005 # Legacy
NODE_ENV=development|production|staging
# Database
DATABASE_URL=mongodb://localhost:27017/roomee_authentication
# JWT
JWT_ACCESS_SECRET=your-jwt-secret
JWT_REFRESH_SECRET=your-refresh-secret
# Encryption
PASSWORD_ENCRYPTION_KEY=your-32-byte-encryption-key
# API URLs
MEMBER_API_URL=http://localhost:3001
NOTIFICATION_API_URL=http://localhost:3005 # Legacy uniquement
# Frontend
FRONT_BASE_URL=http://localhost:3000
FRONT_RESET_PASSWORD_SUFFIX=/reset-password?token=
# AMQP
AMQP_GATEWAY_URL=amqp://localhost:5672
AMQP_EXCHANGE_NAME=roomee_events
AMQP_QUEUE_NAME=authentication_queue
AMQP_ROUTING_KEY_BASE=roomee.authenticationVariables Spécifiques Legacy
# Infisical Secrets Management
INFISICAL_SITE_URL=https://app.infisical.com
INFISICAL_CLIENT_ID=your-client-id
INFISICAL_CLIENT_SECRET=your-client-secret
INFISICAL_PROJECT_ID=your-project-id
# Email (via api-notification)
API_NOTIFICATION_SUFFIX=:3005Migration en Cours
Stratégie de Migration
graph LR
LEGACY[Legacy JS] -->|Phase 1| COEXIST[Coexistence]
COEXIST -->|Phase 2| MODERNE[Moderne TS]
COEXIST -->|Migration| DATA[Migration Données]
COEXIST -->|Refactor| ROUTES[Standardisation Routes]
COEXIST -->|Update| CLIENTS[Mise à jour Clients]Phase Actuelle : Coexistence
- ✅ Les deux services sont opérationnels
- ✅ Service moderne implémente les fonctionnalités de base
- 🔄 Service legacy continue d'être utilisé en production
- 🔄 Migration progressive des clients vers le service moderne
Différences Clés
| Aspect | Moderne | Legacy |
|---|---|---|
| Langage | TypeScript | JavaScript |
| Architecture | Modulaire (modules/) | Flat (controllers/) |
| JWT | 100% @roomee/shared | Mix custom + @roomee/shared |
| Tokens | En clair en base | Hashés (SHA256) |
| Reset Password | Token uniquement | Token + Code 6 chiffres |
| Invitation | Système structuré | Endpoint /auth/invitation |
| Access Token TTL | 15 min | 2400h ⚠️ |
| Validation | Zod schemas | Joi schemas |
| Configuration | ENV direct | Infisical integration |
| Logs | Console + files | Multi-transport (MongoDB, Discord) |
Recommandations de Migration
- Priorité 1 : Réduire TTL access token legacy de 2400h à 15-60 min
- Priorité 2 : Migrer tous les clients vers endpoints modernes
- Priorité 3 : Déprécier progressivement les routes legacy
- Priorité 4 : Migration complète des données
- Priorité 5 : Décommissionnement du service legacy
Tests
Service Moderne
// __tests__/auth.service.test.ts
describe('AuthService', () => {
it('should create a new user', async () => {
const user = await authService.register({
email: 'test@example.com',
password: 'SecureP@ss123'
})
expect(user).toHaveProperty('id')
expect(user.isTmpPassword).toBe(false)
})
})Service Legacy
Tests à implémenter (actuellement manquants).
Commandes
Service Moderne
npm run dev # Développement
npm run build # Build TypeScript
npm run test # Tests Jest
npm run prisma:generate # Génération client PrismaService Legacy
npm run dev # Développement avec nodemon
npm run swagger-autogen # Génération Swagger
npm run prisma:migrate # Migration Prisma
npm run bot # Bot de logs DiscordDocumentation API
Service Moderne
À venir (Swagger à implémenter)
Service Legacy
Swagger UI : http://localhost:3005/docs/
- Disponible en développement uniquement
- Auto-généré avec
npm run swagger-autogen - Fichier :
docs/authenticate-swagger.json
Bonnes Pratiques
1. Toujours utiliser @roomee/shared pour JWT
// ✅ BON
import { authMiddleware } from '@roomee/shared'
const token = authMiddleware.generateToken(payload)
// ❌ ÉVITER (legacy custom JWT)
import { generateAccessToken } from './utils/jwt'2. Valider les entrées
// Moderne : Zod
const schema = z.object({
email: z.string().email(),
password: z.string().min(8)
})
// Legacy : Joi
const schema = Joi.object({
email: Joi.string().email().required(),
password: Joi.string().min(8).required()
})3. Logger les actions importantes
logger.info('User logged in', {
userId: user.id,
email: user.email,
ip: req.ip,
appType: req.params.type
})4. Gérer les flags correctement (Legacy)
// Lors de la première connexion
if (user.isTmpPassword || user.isFirstLogin) {
// Force changement de mot de passe
return {
...tokens,
isTmpPassword: user.isTmpPassword,
isFirstLogin: user.isFirstLogin,
requirePasswordChange: true
}
}Limitations et Améliorations
Limitations Actuelles
Service Moderne :
- ❌ Pas d'OAuth (Google, GitHub)
- ❌ Pas de 2FA
- ❌ Pas de rate limiting distribué (Redis)
- ❌ Swagger documentation à créer
Service Legacy :
- ⚠️ Access token TTL trop long (2400h)
- ❌ Pas de tests
- ❌ Code mort et commenté à nettoyer
- ❌ gRPC configuré mais non utilisé
- ❌ Socket.IO présent mais non activé
Roadmap
Court Terme :
- [ ] Réduire TTL access token legacy à 15-60 min
- [ ] Implémenter tests pour les deux services
- [ ] Ajouter Swagger au service moderne
- [ ] Nettoyer code legacy
Moyen Terme :
- [ ] Migration complète vers service moderne
- [ ] Implémenter OAuth 2.0
- [ ] Ajouter 2FA (TOTP)
- [ ] Cache Redis pour sessions
- [ ] Décommissionner service legacy
Long Terme :
- [ ] WebAuthn / Passkeys
- [ ] Authentification biométrique
- [ ] SSO pour entreprises
- [ ] Architecture zero-trust
Résumé
Le projet dispose de deux services d'authentification :
- api-authentication (Moderne) : TypeScript, modulaire, en développement actif
- api-auth-legacy : JavaScript, flat structure, en production mais en cours de migration
Points clés :
- Les deux services sont fonctionnels et coexistent
- Le legacy a plus de fonctionnalités spécifiques (codes 6 chiffres, flags système)
- Le moderne est plus propre et maintenable
- Migration progressive en cours vers le service moderne
- Attention : Access token legacy expire dans 2400h (à corriger en priorité)
Pour les nouveaux développements, utilisez le service moderne (TypeScript) et suivez les patterns @roomee/shared.