WakaStart
Sécurité

Contrôle d'accès — Feature Flags

Cascade de features App/Network/Customer, activation conditionnelle et intégration dans les guards.

Version v1.04 min de lecture

Contrôle d'accès — Feature Flags

Ce chapitre couvre la cascade de features et leur utilisation concrète. Lisez Modèle de droits et Guards & Hooks en premier.


Modèle de cascade

Une feature est le résultat de trois activations indépendantes. Si une seule est false, la feature est indisponible pour l'utilisateur :

Chargement du diagramme…

Conséquence pratique : un Partner peut activer une feature globalement au niveau App, mais un Customer spécifique peut la désactiver pour ses utilisateurs. Ce mécanisme permet une granularité fine sans code custom.


Features standard de la plateforme

FeatureDescription
invitationsFlux d'invitations utilisateur (envoi email, acceptation, provisioning)
antivirusScan antivirus des fichiers uploadés (ClamAV via ws-mod-antivirus)
hdsFonctionnalités Healthcare Data Security (ws-mod-hds)
ai-translationTraduction automatique IA (consomme des crédits)
exportExport Stellar + WORM archiving
auditConsultation des logs d'audit
billingModule de facturation et gestion des crédits

Utilisation côté frontend

Vérification simple

typescript
import { useAppRights } from "@/hooks/use-app-rights"; function InvitationsSection() { const { hasFeature } = useAppRights(); if (!hasFeature("invitations")) { return null; // Masquer complètement la section } return <InvitationsList />; }

Combinaison feature + droit

tsx
// Toujours vérifier la feature AVANT le droit <Protected feature="invitations" right="invitations.create"> <InviteUserButton /> </Protected>

Ordre recommandé : vérifier la feature en premier. Si la feature est désactivée, inutile de tester les droits — l'API retournera 403 Feature désactivée de toute façon.


Activation via l'API

Pour activer ou désactiver une feature sur un Customer :

http
PATCH /api/config/customers/{customerWid}/features/{featureName} Authorization: Bearer <wakaToken> Content-Type: application/json { "isEnabled": true }

Pour consulter les features actives d'un Customer :

http
GET /api/config/customers/{customerWid}/features Authorization: Bearer <wakaToken>

Réponse :

json
{ "data": [ { "name": "invitations", "isEnabled": true }, { "name": "antivirus", "isEnabled": false }, { "name": "hds", "isEnabled": true } ] }

Activation au niveau App

Les features disponibles pour une App sont définies par le Partner :

http
GET /api/config/apps/{appWid}/features Authorization: Bearer <wakaToken>

Un Partner peut désactiver une feature au niveau App pour tous ses Customers en une seule opération :

http
PATCH /api/config/apps/{appWid}/features/{featureName} Authorization: Bearer <wakaToken> Content-Type: application/json { "isEnabled": false }

Guard backend NestJS — RequireFeature

Si votre Wakapp expose des endpoints qui nécessitent une feature active :

typescript
// decorators/require-feature.decorator.ts import { SetMetadata } from "@nestjs/common"; export const REQUIRED_FEATURE_KEY = "requiredFeature"; export const RequireFeature = (feature: string) => SetMetadata(REQUIRED_FEATURE_KEY, feature);
typescript
// guards/feature.guard.ts import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common"; import { Reflector } from "@nestjs/core"; @Injectable() export class FeatureGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const requiredFeature = this.reflector.getAllAndOverride<string>( REQUIRED_FEATURE_KEY, [context.getHandler(), context.getClass()] ); if (!requiredFeature) return true; const request = context.switchToHttp().getRequest(); const enrichedPayload = request.enrichedPayload; // mis à jour par AuthMiddleware const features: string[] = enrichedPayload?.features ?? []; if (!features.includes(requiredFeature)) { throw new ForbiddenException( `La fonctionnalité ${requiredFeature} n'est pas activée pour ce customer` ); } return true; } }

Usage :

typescript
@Controller("invitations") @UseGuards(FeatureGuard, AppRightGuard) export class InvitationsController { @Post() @RequireFeature("invitations") @RequireAppRight("invitations.create") async createInvitation(@Body() dto: CreateInvitationDto) { // ... } }

Bonnes pratiques

  • Vérifier la feature avant d'afficher l'UI : ne pas attendre le 403 de l'API pour masquer un bouton.
  • Pas de feature par défaut : si une feature est absente de me.features, considérez-la désactivée.
  • Logging : loggez les tentatives d'accès à des features désactivées pour identifier les bugs d'UI.

Pièges classiques

  • Feature absente ≠ erreur : si features est un tableau vide, ce n'est pas un bug — l'utilisateur n'a simplement aucune feature activée.
  • Ne pas confondre feature et AppRight : une feature active ne garantit pas le droit — il faut les deux.
  • Cache features : les features font partie du payload /me, elles sont donc en cache. Une activation côté admin ne sera visible qu'après le prochain appel /me.

Aller plus loin