La création d'applications de santé par-dessus un serveur FHIR est devenue une pratique courante. Les serveurs FHIR offrent un stockage normalisé et des API pour les ressources cliniques, permettant aux développeurs de se concentrer sur la création de fonctionnalités plutôt que sur la gestion de l'infrastructure des données de santé. Cela soulève toutefois une question importante : comment s'assurer que les différents utilisateurs ne voient que les données auxquelles ils ont accès?
Par exemple, un médecin devrait avoir accès à l'ensemble des observations d'un patient — tant les résultats de laboratoire que les signes vitaux. Un technicien de laboratoire, quant à lui, ne devrait consulter que les résultats de laboratoire finalisés qui concernent son travail. La mise en œuvre de ce type de contrôle d'accès granulaire exige traditionnellement une logique complexe au niveau de l'application, des règles d'autorisation personnalisées et des tests approfondis.
Il existe une meilleure approche : une méthode fondée sur des normes qui tire parti de votre fournisseur d'identité existant et ne nécessite aucun code d'autorisation personnalisé dans votre application.
Dans cet article, nous verrons comment mettre en œuvre un contrôle d'accès basé sur les rôles (RBAC) sophistiqué pour les ressources FHIR en utilisant les rôles Keycloak (une solution de gestion des identités et des accès à code source ouvert) mappés aux portées SMART on FHIR V2, avec l'application automatique des règles par Aidbox. Le meilleur dans tout ça? Le même point de terminaison d'API retourne des données différentes selon qui pose la question — de façon totalement transparente.
Le problème : un seul point de terminaison, plusieurs niveaux d'accès
Imaginons que vous construisez une application de santé par-dessus le serveur FHIR avec deux types d'utilisateurs :
Dre Sarah (médecin)
- Doit consulter toutes les observations des patients
- Examine à la fois les résultats de laboratoire et les signes vitaux
- Prend des décisions cliniques basées sur des données patient complètes
Michel (technicien de laboratoire)
- Travaille uniquement avec les résultats de laboratoire finalisés
- Ne devrait pas voir les signes vitaux ni les données de laboratoire préliminaires
- Nécessite un accès limité à sa fonction spécifique
Les deux utilisateurs appellent le même point de terminaison d'API : GET /fhir/Observation. Mais ils devraient voir des résultats complètement différents.
Traditionnellement, les développeurs résoudraient ce problème en ajoutant :
- Un intergiciel personnalisé pour vérifier les rôles des utilisateurs
- Des filtres de requête complexes dans le code de l'application
- Des points de terminaison d'API ou des paramètres distincts pour différents types d'utilisateurs
- De nombreux tests unitaires pour chaque scénario d'autorisation
- Des maux de tête de maintenance lorsque les exigences changent
Cette approche fonctionne, mais elle est difficile à maintenir et sujette aux erreurs à mesure que les exigences évoluent. Il existe une meilleure façon de faire.
La solution : les portées SMART rencontrent les rôles Keycloak
SMART on FHIR V2 introduit un concept puissant : les portées avec paramètres de requête. Plutôt que d'accorder un accès général comme « cet utilisateur peut lire les Observations », vous pouvez spécifier des règles précises, telles que :
user/Observation.rs?category=laboratory&status=final
Cette portée signifie : « L'utilisateur peut lire et rechercher des Observations, mais uniquement les observations de laboratoire ayant le statut final. »
En combinant cela avec le système de rôles composites de Keycloak, nous pouvons :
- Définir des permissions granulaires sous forme de rôles Keycloak de base (chacun représentant une portée SMART)
- Les regrouper en fonctions professionnelles à l'aide de rôles composites (comme « physician » ou « lab_technician »)
- Résoudre automatiquement les rôles composites en portées SMART dans le jeton d'accès
- Laisser Aidbox appliquer les règles automatiquement — aucun code personnalisé requis
Voici le déroulement du flux :
User Login → Keycloak resolves roles → Token with SMART scopes →
Aidbox validates token → Automatic data filtering → Correct results
Mise en œuvre : construction de la structure des rôles
Étape 1 : Définir les rôles de base (portées SMART granulaires)
Dans Keycloak, nous créons des rôles de base qui correspondent directement aux portées SMART on FHIR V2 :
Rôles de base :
user/Patient.rs- Lire et rechercher des données patientuser/Encounter.rs- Lire et rechercher des rencontresuser/Observation.rs- Lire et rechercher TOUTES les observationsuser/Observation.rs?category=laboratory&status=final- Lire et rechercher UNIQUEMENT les résultats de laboratoire finalisés
Ces rôles de base sont les éléments constitutifs. Chacun représente une permission spécifique avec des restrictions optionnelles sur les paramètres de requête.
Étape 2 : Créer des rôles composites (fonctions professionnelles)
Ensuite, combinons les rôles de base en rôles composites qui reflètent de vraies fonctions professionnelles :
Rôle Médecin (accès clinique complet) :
user/Patient.rsuser/Encounter.rsuser/Observation.rs
Rôle Technicien de laboratoire (limité aux résultats de laboratoire) :
user/Patient.rsuser/Observation.rs?category=laboratory&status=final
Notez que le rôle Technicien de laboratoire utilise la portée Observation restreinte — empêchant l'accès aux signes vitaux ou aux résultats préliminaires.
Étape 3 : Configurer le mappeur de jetons
C'est ici que la magie opère. Keycloak transforme les rôles en jetons d'accès. Le jeton est un petit fichier sécurisé (JSON Web Token — JWT) qui accompagne chaque requête d'API et indique à Aidbox ce que l'utilisateur est autorisé à faire. Nous avons besoin que Keycloak :
- Résolve les rôles composites (p. ex., « physician ») en leurs rôles de base constitutifs
- Inclue ces rôles de base dans la revendication
scopedu jeton sous forme de portées SMART - Ajoute la revendication
atv: "2"pour indiquer qu'il y a des portées SMART on FHIR dans le jeton à traiter
Nous y parvenons à l'aide d'un mappeur de protocole personnalisé basé sur un script. Lorsqu'un technicien de laboratoire se connecte, son jeton d'accès ressemble à ceci :
{
"sub": "lab_technician",
"scope": "user/Patient.rs
user/Observation.rs?category=laboratory&status=final",
"atv": "2"
}
Étape 4 : Configurer Aidbox pour l'application automatique des règles
Voici la partie la plus élégante : Aidbox applique automatiquement les règles d'accès définies dans le jeton. Tout ce dont vous avez besoin est :
- TokenIntrospector — Vérifie que le jeton est valide et émis par votre instance Keycloak :
{
"resourceType": "TokenIntrospector",
"jwt": {
"iss": "http://localhost:8888/realms/master"
},
"type": "jwt",
"jwks_uri":
"http://keycloak:8888/realms/master/protocol/openid-connect/certs",
"id": "external-auth-server",
"resourceType": "TokenIntrospector"
}
- AccessPolicy — Autorise les requêtes avec des jetons validés :
{
"resourceType": "AccessPolicy",
"id": "keycloak-access-policy",
"engine": "matcho",
"matcho": {
"jwt": {
"iss": "http://localhost:8888/realms/master"
}
}
}
C'est tout. Lorsque Aidbox reçoit une requête avec un jeton contenant atv: "2" et des portées SMART, il :
- Analyse les portées pour déterminer les types de ressources autorisés
- Applique les filtres de paramètres de requête provenant des portées
- Retourne uniquement les données correspondant aux contraintes de portée
- Rejette les requêtes pour les types de ressources non autorisés
Aucun code d'autorisation personnalisé. Aucune logique de contrôle d'accès complexe. Ça fonctionne tout simplement.
Voir le tout en action
Parcourons les deux scénarios utilisateurs pour voir comment le même appel d'API retourne des résultats différents. Imaginons que vous avez configuré votre système avec Aidbox connecté à Keycloak, et que la Dre Sarah (le médecin) et Michel (le technicien de laboratoire) utilisent tous deux la même application.
Dans le système, il y a deux ressources Observation :
- Hémoglobine — une observation de laboratoire avec le statut « final »
- Tension artérielle — une observation de signes vitaux
Les deux utilisateurs accéderont au même point de terminaison : GET /fhir/Observation
Mais Aidbox retournera des réponses différentes selon qui effectue la requête.
Test 1 : Accès médecin (accès complet)
Identifiants de connexion :
Username: physician
Password: password
Appel d'API :
GET /fhir/Observation
Authorization: Bearer <physician_token>
Portées du jeton :
user/Patient.rs user/Encounter.rs user/Observation.rs
Résultat : Le médecin voit les deux observations :
- Hémoglobine (laboratoire, final)
- Tension artérielle (signes vitaux)
La portée user/Observation.rs accorde un accès non restreint à toutes les ressources Observation.
Test 2 : Accès technicien de laboratoire (accès restreint)
Identifiants de connexion :
Username: lab_technician
Password: password
Appel d'API :
GET /fhir/Observation
Authorization: Bearer <lab_technician_token>
Portées du jeton :
user/Patient.rs
user/Observation.rs?category=laboratory&status=final
Résultat : Le technicien de laboratoire ne voit qu'une seule observation :
- Hémoglobine (laboratoire, final)
L'observation de tension artérielle est filtrée parce qu'elle ne correspond pas aux exigences de la portée :
- Sa catégorie est « vital-signs » (et non « laboratory »)
- La portée exige explicitement
category=laboratory&status=final
Même point de terminaison, même code, résultats différents selon qui pose la question. Aucune logique de filtrage personnalisée requise.
Essayez-le vous-même
Vous voulez voir tout ça en action? L'exemple complet est prêt à être exécuté avec Docker.
Prérequis : Docker et Docker Compose installés
Démarrage rapide :
- Clonez le dépôt et naviguez vers l'exemple :
cd aidbox-features/smart-keycloak-roles
- Démarrez tous les services :
docker compose up --build
-
Initialisez Aidbox : Naviguez vers http://localhost:8080 et complétez l'initialisation d'Aidbox.
-
Ouvrez l'application de démonstration : Naviguez vers http://localhost:3000.
-
Testez les deux rôles utilisateur :
Connectez-vous en tant que physician/password — voyez toutes les observations
- Cliquez sur « Start Over » et connectez-vous en tant que
lab_technician/password— voyez uniquement les résultats de laboratoire
L'application de démonstration montre côte à côte ce à quoi chaque utilisateur peut accéder, rendant les différences de contrôle d'accès immédiatement visibles.
Points clés à retenir
Cette approche offre plusieurs avantages significatifs :
1. Zéro code d'autorisation personnalisé Votre application n'a pas besoin de comprendre les rôles ni d'implémenter une logique de filtrage. Aidbox s'en charge automatiquement en fonction des portées SMART.
2. Contrôle d'accès centralisé Toutes les définitions de rôles résident dans votre fournisseur d'identité (Keycloak). Modifiez les permissions d'un rôle en un seul endroit, et les changements s'appliquent partout.
3. Fondé sur des normes Basé sur SMART on FHIR V2, une norme établie d'interopérabilité en santé. Aucune solution propriétaire ni dépendance envers un fournisseur.
4. Contrôle granulaire Les restrictions par paramètres de requête (?category=laboratory&status=final) permettent un contrôle d'accès précis sans règles personnalisées complexes.
5. Facile à étendre Besoin d'un nouveau rôle? Créez un rôle composite dans Keycloak en combinant les rôles de base appropriés. Aucune modification de code requise.
Pour en savoir plus :
- Portées SMART on FHIR dans Aidbox
- Contrôle d'accès basé sur les rôles dans Keycloak
- Cadre SMART App Launch
Conclusion
Le contrôle d'accès granulaire pour les ressources FHIR n'a pas à être compliqué. En combinant les portées SMART on FHIR V2, le système de rôles composites de Keycloak et l'application automatique des portées par Aidbox, vous pouvez mettre en œuvre un RBAC sophistiqué sans écrire de code d'autorisation personnalisé.
Le résultat : des applications plus sécuritaires, une maintenance plus facile et une meilleure conformité aux normes d'interopérabilité en santé.
Prêt à mettre en œuvre le contrôle d'accès basé sur les rôles dans votre application FHIR? Commencez avec le dépôt d'exemples et personnalisez les rôles selon votre cas d'utilisation spécifique.




