Trois associés, trois domaines de responsabilité, et une seule base WordPress headless exposant dossiers clients, factures et échéances fiscales via l’API REST : le cabinet Ferrand & Associés avait besoin que chacun ne voie, depuis le front qu’il utilise au quotidien, que ce qui relève de son propre périmètre. Pas de rôle « administrateur » unique pour tous, pas de front unique non plus qui affiche tout puis masque ce qui ne devrait pas apparaître : la séparation devait se faire au niveau de l’API elle-même.
Le découpage des périmètres
Le cabinet gère trois familles de contenus distinctes, chacune exposée par sa propre route REST : les dossiers clients, les factures émises, et les échéances fiscales à venir. Chaque associé n’intervient réellement que sur l’un de ces trois périmètres au quotidien, même si tous ont techniquement accès à l’ensemble des données depuis l’administration WordPress classique. Le front headless, lui, devait refléter cette séparation de façon stricte : un associé spécialisé sur les échéances fiscales ne devait jamais recevoir, même par erreur d’affichage, une donnée de facturation d’un autre associé.
Trois capacités personnalisées

add_action( 'init', function () {
$role_associe = get_role( 'associe_comptable' );
$role_associe->add_cap( 'voir_dossiers_clients' );
$role_associe->add_cap( 'voir_factures' );
$role_associe->add_cap( 'voir_echeances' );
} );
// Attribution fine par utilisateur, au-delà du rôle générique
$utilisateur = get_user_by( 'login', 'a.ferrand' );
$utilisateur->add_cap( 'voir_echeances' );
$utilisateur->remove_cap( 'voir_factures' );
Plutôt que de créer trois rôles totalement distincts, ce qui aurait multiplié les cas particuliers dès qu’un associé intervient occasionnellement sur un autre périmètre, le choix s’est porté sur un rôle commun associe_comptable doté des trois capacités par défaut, puis affiné individuellement par utilisateur avec add_cap() et remove_cap(). Cette granularité permet à un associé polyvalent de conserver un accès élargi sans devoir changer de rôle.
Vérification côté contrôleur REST
register_rest_route( 'cabinet/v1', '/echeances', array(
'methods' => 'GET',
'callback' => 'lister_echeances',
'permission_callback' => function () {
return current_user_can( 'voir_echeances' );
},
) );
register_rest_route( 'cabinet/v1', '/factures', array(
'methods' => 'GET',
'callback' => 'lister_factures',
'permission_callback' => function () {
return current_user_can( 'voir_factures' );
},
) );
Chaque route de chaque périmètre vérifie sa propre capacité, sans jamais partager de logique de permission avec les autres routes. Ce choix, un peu plus verbeux qu’une vérification générique unique, a l’avantage de rendre chaque contrôleur lisible indépendamment des autres : ouvrir le fichier de la route des échéances suffit à comprendre exactement quelle capacité conditionne son accès, sans devoir remonter dans une fonction de permission partagée qui gérerait tous les cas à la fois.
Ce que ce découpage a évité
- Un front unique qui recevrait toutes les données puis masquerait certaines sections selon l’utilisateur connecté, avec le risque qu’une donnée sensible transite malgré tout jusqu’au navigateur
- Une logique de permission centralisée devenue, avec le temps, un empilement de conditions difficile à auditer
- Un rôle unique « associé » qui aurait donné accès à tout par défaut, en s’appuyant sur la seule discipline du front pour restreindre l’affichage
La limite de l’exercice
Ce cloisonnement ne dit rien de la façon dont un associé s’authentifie auprès de l’API — mot de passe applicatif, cookie de session ou tout autre mécanisme reste une question distincte. Il porte uniquement sur ce qu’un utilisateur déjà identifié a le droit de voir, une fois son identité établie. Sur ce projet, la réflexion sur l’authentification elle-même a fait l’objet d’un chantier séparé, mené une fois le découpage des capacités stabilisé.
En résumé
Séparer trois scopes REST pour trois associés d’un cabinet comptable n’a nécessité ni extension tierce, ni architecture complexe : trois capacités personnalisées, ajoutées finement par utilisateur, et une vérification systématique via current_user_can() dans chaque permission_callback ont suffi à garantir qu’aucune donnée d’un périmètre ne fuite vers un front qui n’en avait pas la responsabilité.