npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@falandyjean/lockbox-auth

v2.0.1

Published

🔐 Bibliothèque d'authentification complète avec système de rôles avancé (RBAC) pour Node.js/Express

Downloads

6

Readme

🔐 @falandy/lockbox-auth

Bibliothèque d'authentification moderne avec système de rôles basé sur des modèles pour Node.js/Express

npm version License: MIT

🎯 Comment ça marche ? (Explication Complète)

🏗️ Architecture du Système

Lockbox-auth est un système d'authentification complet qui s'intègre dans votre application web comme une couche de sécurité intelligente. Voici comment ça fonctionne dans la vraie vie :

1. Dans votre site web

  • Un utilisateur arrive sur votre site
  • Il essaie d'accéder à une page protégée (ex: /admin, /write-article)
  • Lockbox-auth intercepte la requête AVANT qu'elle n'atteigne votre code
  • Il vérifie : "Est-ce que cet utilisateur a le bon rôle ?"
  • Autorisé → L'utilisateur voit la page
  • Refusé → Erreur 403 ou redirection vers login

2. Cycle de vie complet d'un utilisateur

👤 VISITEUR
    ↓ S'inscrit sur /auth/register
👤 UTILISATEUR CONNECTÉ (role: USER)
    ↓ Admin lui donne le rôle BLOG_WRITER
👤 RÉDACTEUR (peut écrire des articles)
    ↓ Promotion → rôle BLOG_EDITOR  
👤 ÉDITEUR (peut écrire + modifier + supprimer)
    ↓ Promotion → rôle BLOG_CHIEF_EDITOR
👤 RÉDACTEUR EN CHEF (contrôle total du blog)

3. Système de hiérarchie automatique

BLOG_CHIEF_EDITOR (niveau 15) 
    ↓ peut faire tout ce que fait...
BLOG_EDITOR (niveau 10)
    ↓ peut faire tout ce que fait...
BLOG_WRITER (niveau 5)
    ↓ peut faire tout ce que fait...
BLOG_READER (niveau 1)

💡 Magie de lockbox-auth : Si quelqu'un a le rôle BLOG_EDITOR, il peut automatiquement accéder aux pages BLOG_WRITER et BLOG_READER !

🔄 Workflow typique dans une vraie application

Étape 1 : Inscription

Utilisateur remplit un formulaire → POST /auth/register
↓
Lockbox-auth crée le compte + hash le mot de passe
↓
Utilisateur reçoit un email de confirmation (optionnel)
↓
Utilisateur peut se connecter

Étape 2 : Connexion

Utilisateur remplit login/password → POST /auth/login
↓
Lockbox-auth vérifie les identifiants
↓
Si OK : génère un JWT token (expire dans 1h)
↓
Renvoie { accessToken, refreshToken, user: {...} }
↓
Frontend stocke le token (localStorage/cookie)

Étape 3 : Navigation protégée

Utilisateur clique sur "Écrire un article"
↓
Frontend fait GET /write-article
↓
Lockbox-auth intercepte et lit le token JWT
↓
Vérifie : "A-t-il le rôle BLOG_WRITER ou supérieur ?"
↓
OUI → Page s'affiche | NON → Erreur 403

Étape 4 : Renouvellement automatique

Token expire (1h) → Frontend reçoit erreur 401
↓
Frontend utilise automatiquement le refreshToken
↓
POST /auth/refresh avec le refreshToken
↓
Lockbox-auth génère un nouveau accessToken
↓
Navigation continue sans interruption

🎯 Pourquoi Choisir lockbox-auth ?

Révolutionnaire : Système de Modèles

  • Fini les centaines de rôles ! Choisissez seulement les modèles dont vous avez besoin
  • Package plus léger : Importez uniquement ce que vous utilisez
  • Plus simple à comprendre : Logique claire et modèles sectoriels
  • Totalement modulaire : Créez vos propres modèles facilement

Sécurité de niveau entreprise

  • Hachage Argon2 (résistant aux GPU/ASIC)
  • JWT + Refresh Tokens avec expiration automatique
  • Protection CSRF intégrée
  • Rate limiting sur les tentatives de connexion
  • Mots de passe temporaires sécurisés
  • Validation stricte des entrées utilisateur

Performance optimisée

  • Middleware ultra-rapide (< 1ms de latence)
  • Cache intelligent des rôles en mémoire
  • Base de données optimisée avec Prisma
  • Lazy loading des modèles de rôles

🎯 Utilisé dans la vraie vie

  • Sites e-commerce : Gérer clients, vendeurs, admins
  • Plateformes de contenu : Lecteurs, rédacteurs, éditeurs
  • Applications SaaS : Utilisateurs gratuits, premium, support
  • Intranets d'entreprise : Employés, managers, direction

�🔥 Comparaison : Ancien vs Nouveau

❌ Ancien système (statique) :

// Vous étiez forcé d'avoir TOUS ces rôles même si vous n'en utilisiez que 5
import { USER_ROLES } from 'lockbox-auth'; // 500+ rôles importés !
// BLOG_READER, HOSPITAL_SURGEON, BANK_TRADER, SCHOOL_STUDENT, etc.

✅ Nouveau système (modèles) :

// Vous choisissez EXACTEMENT ce dont vous avez besoin
import { deployTemplate, BLOG_TEMPLATE } from 'lockbox-auth';

// En une ligne, vous déployez seulement les rôles blog
deployTemplate('BLOG', BLOG_TEMPLATE);

🚀 Installation Ultra-Rapide

npm install @falandy/lockbox-auth
npx lockbox-init

🏗️ Implémentation complète dans un site réel

Étape 1 : Configuration de base (2 minutes)

// server.js - Configuration Express + Lockbox
import express from 'express';
import { 
  deployTemplate, 
  BLOG_TEMPLATE, 
  createAuthRouter, 
  requireAuth, 
  requireRole 
} from '@falandy/lockbox-auth';

const app = express();
app.use(express.json());

// 1. Déployer vos modèles de rôles
deployTemplate('BLOG', BLOG_TEMPLATE);

// 2. Configuration auth
const authConfig = {
  jwtSecret: process.env.JWT_SECRET || 'your-secret-key',
  refreshTTL: 7, // 7 jours
  temporaryPassword: {
    enabled: true,
    length: 12,
    expiryHours: 24,
  },
};

// 3. Routes d'authentification automatiques
app.use('/auth', createAuthRouter(authConfig));
// Crée automatiquement : /auth/register, /auth/login, /auth/refresh, /auth/logout

app.listen(3000);

Étape 2 : Pages frontend avec protection (5 minutes)

<!-- login.html - Page de connexion -->
<!DOCTYPE html>
<html>
<head>
    <title>Connexion</title>
</head>
<body>
    <form id="loginForm">
        <input type="email" placeholder="Email" required>
        <input type="password" placeholder="Mot de passe" required>
        <button type="submit">Se connecter</button>
    </form>

    <script>
        document.getElementById('loginForm').onsubmit = async (e) => {
            e.preventDefault();
            const formData = new FormData(e.target);
            
            try {
                const response = await fetch('/auth/login', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        email: formData.get('email'),
                        password: formData.get('password')
                    })
                });
                
                const data = await response.json();
                
                if (response.ok) {
                    // Stocker les tokens
                    localStorage.setItem('accessToken', data.accessToken);
                    localStorage.setItem('refreshToken', data.refreshToken);
                    
                    // Rediriger selon le rôle
                    if (data.user.role.includes('BLOG_WRITER')) {
                        window.location.href = '/write-article';
                    } else {
                        window.location.href = '/dashboard';
                    }
                } else {
                    alert('Erreur: ' + data.message);
                }
            } catch (error) {
                alert('Erreur de connexion');
            }
        };
    </script>
</body>
</html>

Étape 3 : Routes protégées par rôle

// server.js - Ajout des routes protégées

// 🌍 Pages publiques (accessibles à tous)
app.get('/', (req, res) => {
    res.send('<h1>Accueil</h1><a href="/login.html">Se connecter</a>');
});

// 🔒 Pages protégées (connexion requise)
app.get('/dashboard', requireAuth(authConfig.jwtSecret), (req, res) => {
    res.json({ 
        message: `Bienvenue ${req.user.email}`,
        role: req.user.role,
        canWrite: req.user.role.includes('BLOG_WRITER')
    });
});

// ✍️ Zone rédaction (rôle BLOG_WRITER minimum)
app.get('/write-article', requireRole('BLOG_WRITER'), (req, res) => {
    res.send(`
        <h1>Zone de rédaction</h1>
        <p>Bonjour ${req.user.email}, vous pouvez écrire des articles</p>
        <form method="POST" action="/api/articles">
            <textarea name="content" placeholder="Votre article..."></textarea>
            <button type="submit">Publier</button>
        </form>
    `);
});

// 🛠️ Zone d'édition (rôle BLOG_EDITOR minimum)
app.get('/manage-articles', requireRole('BLOG_EDITOR'), (req, res) => {
    res.send(`
        <h1>Gestion des articles</h1>
        <p>Vous pouvez modifier et supprimer tous les articles</p>
        <ul>
            <li>Article 1 <button onclick="deleteArticle(1)">Supprimer</button></li>
            <li>Article 2 <button onclick="deleteArticle(2)">Supprimer</button></li>
        </ul>
    `);
});

// 👑 Zone admin (rôle BLOG_CHIEF_EDITOR)
app.get('/admin', requireRole('BLOG_CHIEF_EDITOR'), (req, res) => {
    res.send(`
        <h1>Administration</h1>
        <p>Contrôle total du système</p>
        <button onclick="promoteUser()">Promouvoir un utilisateur</button>
        <button onclick="viewStats()">Voir les statistiques</button>
    `);
});

// 📝 API pour créer des articles
app.post('/api/articles', requireRole('BLOG_WRITER'), async (req, res) => {
    // req.user contient automatiquement les infos de l'utilisateur connecté
    const article = {
        title: req.body.title,
        content: req.body.content,
        authorId: req.user.id,
        authorEmail: req.user.email,        createdAt: new Date()
    };
    
    res.json({ message: 'Article créé !', article });
});

Étape 4 : Middleware JavaScript pour le frontend

// auth.js - Middleware côté client
class AuthManager {
    constructor() {
        this.token = localStorage.getItem('accessToken');
        this.refreshToken = localStorage.getItem('refreshToken');
    }
    
    // Faire une requête authentifiée
    async fetch(url, options = {}) {
        const headers = {
            'Content-Type': 'application/json',
            ...options.headers
        };
        
        if (this.token) {
            headers['Authorization'] = `Bearer ${this.token}`;
        }
          let response = await fetch(url, { ...options, headers });
        
        if (response.status === 401 && this.refreshToken) {
            const renewed = await this.renewToken();
            if (renewed) {
                headers['Authorization'] = `Bearer ${this.token}`;
                response = await fetch(url, { ...options, headers });
            }
        }
        
        return response;
    }
    
    // Renouveler le token automatiquement
    async renewToken() {
        try {
            const response = await fetch('/auth/refresh', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ refreshToken: this.refreshToken })
            });
            
            if (response.ok) {
                const data = await response.json();
                this.token = data.accessToken;
                localStorage.setItem('accessToken', data.accessToken);
                return true;
            }
        } catch (error) {            console.error('Erreur renouvellement token:', error);
        }
        
        this.logout();
        return false;
    }
    
    // Déconnexion
    logout() {
        localStorage.removeItem('accessToken');
        localStorage.removeItem('refreshToken');
        window.location.href = '/login.html';
    }
    
    // Vérifier si l'utilisateur a un rôle
    async hasRole(role) {
        try {
            const response = await this.fetch('/auth/me');
            if (response.ok) {
                const user = await response.json();
                return user.role.includes(role);
            }
        } catch (error) {
            console.error('Erreur vérification rôle:', error);
        }
        return false;
    }
}

// Instance globale
const auth = new AuthManager();

// Utilisation dans vos pages
document.addEventListener('DOMContentLoaded', async () => {
    // Cacher/afficher des éléments selon le rôle
    if (await auth.hasRole('BLOG_WRITER')) {
        document.getElementById('writeButton').style.display = 'block';
    }
    
    if (await auth.hasRole('BLOG_EDITOR')) {
        document.getElementById('adminPanel').style.display = 'block';
    }
});

Étape 5 : Gestion avancée des utilisateurs

// admin-routes.js - Routes pour gérer les utilisateurs
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();

// Promouvoir un utilisateur
app.post('/admin/promote-user', requireRole('BLOG_CHIEF_EDITOR'), async (req, res) => {
    try {
        const { userId, newRole } = req.body;
        
        const updatedUser = await prisma.user.update({
            where: { id: userId },
            data: { role: newRole }
        });
        
        res.json({ 
            message: `Utilisateur promu au rôle ${newRole}`,
            user: updatedUser 
        });
    } catch (error) {
        res.status(500).json({ error: 'Erreur promotion utilisateur' });
    }
});

// Lister tous les utilisateurs avec leurs rôles
app.get('/admin/users', requireRole('BLOG_EDITOR'), async (req, res) => {
    try {
        const users = await prisma.user.findMany({
            select: {
                id: true,
                email: true,
                role: true,
                createdAt: true,
                lastLoginAt: true
            }
        });
        
        res.json({ users });
    } catch (error) {
        res.status(500).json({ error: 'Erreur récupération utilisateurs' });
    }
});

// Statistiques du site
app.get('/admin/stats', requireRole('BLOG_CHIEF_EDITOR'), async (req, res) => {
    try {
        const stats = {
            totalUsers: await prisma.user.count(),
            usersByRole: await prisma.user.groupBy({
                by: ['role'],
                _count: { role: true }
            }),
            totalArticles: await prisma.article?.count() || 0,
            recentLogins: await prisma.user.count({
                where: {
                    lastLoginAt: {
                        gte: new Date(Date.now() - 24 * 60 * 60 * 1000) // 24h
                    }
                }
            })
        };
        
        res.json({ stats });
    } catch (error) {
        res.status(500).json({ error: 'Erreur récupération statistiques' });
    }
});

🎨 Usage par Secteur

📝 Blog/Média

import { deployTemplate, BLOG_TEMPLATE, requireAuth } from '@falandy/lockbox-auth';

// Déployer le modèle blog
deployTemplate('BLOG', BLOG_TEMPLATE);

// Utiliser les rôles
app.get('/write-article', requireRole('BLOG_WRITER'), (req, res) => {
  // Seuls les rédacteurs peuvent écrire
});

🛒 E-commerce

import { deployTemplate, ECOMMERCE_TEMPLATE } from '@falandy/lockbox-auth';

// Déployer le modèle e-commerce
deployTemplate('SHOP', ECOMMERCE_TEMPLATE);

🏥 Hôpital

import { deployTemplate, HOSPITAL_TEMPLATE } from '@falandy/lockbox-auth';

deployTemplate('HOSPITAL', HOSPITAL_TEMPLATE);

🎓 École/Université

import { deployTemplate, SCHOOL_TEMPLATE } from '@falandy/lockbox-auth';

deployTemplate('SCHOOL', SCHOOL_TEMPLATE);

📦 Modèles Disponibles

| Secteur | Modèle | Rôles Inclus | |---------|--------|--------------| | 📝 Média | BLOG_TEMPLATE | READER, WRITER, EDITOR, CHIEF_EDITOR | | 📺 Journalisme | JOURNALISM_TEMPLATE | READER, JOURNALIST, PHOTOGRAPHER, EDITOR_IN_CHIEF | | 🎥 Streaming | STREAMING_TEMPLATE | VIEWER, STREAMER, MODERATOR, PLATFORM_ADMIN | | 🛒 E-commerce | ECOMMERCE_TEMPLATE | CUSTOMER, VENDOR, STORE_MANAGER, MARKETPLACE_ADMIN | | 🏪 Marketplace | MARKETPLACE_TEMPLATE | BUYER, SELLER, AFFILIATE, PLATFORM_OWNER | | 🎓 École | SCHOOL_TEMPLATE | STUDENT, TEACHER, PRINCIPAL, SUPERINTENDENT | | 🏛️ Université | UNIVERSITY_TEMPLATE | STUDENT, PROFESSOR, DEAN, CHANCELLOR | | 🏥 Hôpital | HOSPITAL_TEMPLATE | PATIENT, NURSE, DOCTOR, MEDICAL_DIRECTOR | | 🍽️ Restaurant | RESTAURANT_TEMPLATE | CUSTOMER, SERVER, CHEF, OWNER | | 🏨 Hôtel | HOTEL_TEMPLATE | GUEST, RECEPTIONIST, MANAGER, OWNER | | 🚚 Livraison | DELIVERY_TEMPLATE | CUSTOMER, DRIVER, DISPATCHER, OPERATIONS_MANAGER | | 💻 SaaS | SAAS_TEMPLATE | FREE_USER, PREMIUM_USER, SUPPORT_AGENT, PLATFORM_ADMIN | | 🎮 Gaming | GAMING_TEMPLATE | PLAYER, STREAMER, MODERATOR, GAME_MASTER | | 🏦 Banque | BANK_TEMPLATE | CUSTOMER, ADVISOR, MANAGER, DIRECTOR | | 🏛️ Gouvernement | GOVERNMENT_TEMPLATE | CITIZEN, EMPLOYEE, MANAGER, DIRECTOR |

💡 Utilisation Avancée

🔧 Créer un Modèle Personnalisé

import { CustomRole, deployTemplate } from '@falandy/lockbox-auth';

// Définir vos rôles personnalisés
const myCustomRoles: CustomRole[] = [
  { key: 'VISITOR', value: 'VISITOR', level: 1, description: 'Visiteur' },
  { key: 'MEMBER', value: 'MEMBER', level: 5, description: 'Membre' },
  { key: 'PREMIUM', value: 'PREMIUM', level: 8, description: 'Membre premium' },
  { key: 'ADMIN', value: 'ADMIN', level: 15, description: 'Administrateur' },
];

// Déployer votre modèle
deployTemplate('MYAPP', myCustomRoles);

🔄 Combiner Plusieurs Modèles

// Déployer plusieurs modèles pour une app complexe
deployTemplate('BLOG', BLOG_TEMPLATE);
deployTemplate('SHOP', ECOMMERCE_TEMPLATE);
deployTemplate('SUPPORT', SAAS_TEMPLATE);

// Maintenant vous avez tous ces rôles :
// BLOG_WRITER, SHOP_VENDOR, SUPPORT_AGENT, etc.

📊 Gestion Avancée des Rôles

import { 
  getDeployedRoles, 
  getActiveTemplates, 
  getRoleStats, 
  hasRolePermission 
} from '@falandy/lockbox-auth';

// Voir tous les rôles déployés
console.log(getDeployedRoles());

// Voir les modèles actifs
console.log(getActiveTemplates());

console.log(getRoleStats());

const canEdit = hasRolePermission('BLOG_WRITER', 'BLOG_READER');

🔐 Authentification Complète

import express from 'express';
import {
  createAuthRouter,
  requireAuth,
  requireRole,
  deployTemplate,
  BLOG_TEMPLATE,
} from '@falandy/lockbox-auth';

const app = express();

// 1. Déployer vos modèles
deployTemplate('BLOG', BLOG_TEMPLATE);

// 2. Configuration auth
const authConfig = {
  jwtSecret: process.env.JWT_SECRET!,
  refreshTTL: 7, // 7 jours
  temporaryPassword: {
    enabled: true,
    length: 12,
    expiryHours: 24,
  },
};

// 3. Routes d'authentification
app.use('/auth', createAuthRouter(authConfig));

// 4. Routes protégées
app.get('/profile', requireAuth(authConfig.jwtSecret), (req, res) => {
  res.json({ user: req.user });
});

app.get('/write', requireRole('BLOG_WRITER'), (req, res) => {
  res.json({ message: 'Zone de rédaction' });
});

app.get('/admin', requireRole('BLOG_CHIEF_EDITOR'), (req, res) => {
  res.json({ message: 'Zone d\'administration' });
});

Configuration Rapide

📋 Variables d'Environnement

# .env
JWT_SECRET=your-super-secret-key
LOCKBOX_COMPANY_NAME="Mon Entreprise"
LOCKBOX_EMAIL_FROM="[email protected]"

# Email (optionnel)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
[email protected]
SMTP_PASSWORD=your-app-password

🗄️ Base de Données (Prisma)

# Initialiser Prisma
npx prisma init
npx prisma migrate dev

�️ Interface CLI

Lockbox Auth inclut un CLI puissant pour gérer vos modèles et configurations depuis le terminal :

# Voir l'état de vos rôles
lockbox-auth status

# Afficher les informations du package
lockbox-auth info

# Exporter votre configuration
lockbox-auth export ./backup-roles.json

# Synchroniser la base de données
lockbox-auth db-sync

# Réinitialiser tous les rôles
lockbox-auth reset-all

🎯 Avantages du CLI

  • Surveillance en temps réel de vos rôles déployés
  • Sauvegarde/restauration facile de vos configurations
  • Debug rapide pour voir l'état du système
  • Synchronisation DB en une commande

👉 Guide CLI Complet

�📚 Documentation Complète

🌟 Avantages du Système de Modèles

Avant lockbox-auth

  • ❌ Créer un système de rôles from scratch
  • ❌ Gérer manuellement les hiérarchies
  • ❌ Coder les middlewares d'autorisation
  • ❌ Écrire les fonctions de validation
  • Temps : 2-3 semaines

🚀 Avec lockbox-auth

  • deployTemplate('BLOG', BLOG_TEMPLATE) - 1 ligne
  • ✅ Hiérarchies automatiques
  • ✅ Middlewares inclus
  • ✅ Validation intégrée
  • Temps : 5 minutes

🛠️ Migration depuis l'Ancien Système

Si vous utilisiez l'ancienne version avec USER_ROLES statique :

// ❌ Ancien (ne marche plus)
import { USER_ROLES } from '@falandy/lockbox-auth';

// ✅ Nouveau (recommandé)
import { deployTemplate, BLOG_TEMPLATE } from '@falandy/lockbox-auth';
deployTemplate('BLOG', BLOG_TEMPLATE);

🤝 Contribuer

git clone https://github.com/falandy/lockbox-auth
cd lockbox-auth
npm install
npm run dev

📄 Licence

MIT © Falandy


🎉 Application Blog Complète - Configuration Rapide

import express from 'express';
import { deployTemplate, BLOG_TEMPLATE, createAuthRouter, requireRole } from '@falandy/lockbox-auth';

const app = express();
app.use(express.json());

// 1. Déployer le modèle blog
deployTemplate('BLOG', BLOG_TEMPLATE);

// 2. Configuration
const authConfig = { jwtSecret: 'secret', refreshTTL: 7 };

// 3. Routes
app.use('/auth', createAuthRouter(authConfig));

// 4. Routes protégées
app.get('/articles', requireRole('BLOG_READER'), (req, res) => {
  res.json({ articles: ['Article 1', 'Article 2'] });
});

app.post('/articles', requireRole('BLOG_WRITER'), (req, res) => {
  res.json({ message: 'Article créé !' });
});

app.delete('/articles/:id', requireRole('BLOG_EDITOR'), (req, res) => {
  res.json({ message: 'Article supprimé !' });
});

app.listen(3000);
console.log('🚀 App blog avec système de rôles en quelques lignes !');

Vous obtenez une authentification complète avec système de rôles hiérarchiques rapidement et efficacement. 🎉