# Intégrer Pro Santé Connect dans une application : OIDC, e-CPS, scopes et homologation

> Guide pratique pour raccorder un logiciel à Pro Santé Connect : parcours Datapass et bac à sable, flux code d'autorisation, scopes et claims, durées de vie des jetons, déconnexion, mTLS et Espace de Confiance.

Auteur: Ala Ben Aicha

Page de référence: https://alabenaicha.me/fr/insights/pro-sante-connect-integration

Mise à jour: 2026-10-06

## Réponse directe

Pro Santé Connect (PSC) est le fournisseur d'identité OpenID Connect de l'ANS pour les professionnels des secteurs sanitaire, médico-social et social. Votre application est un client OIDC confidentiel : elle redirige vers la mire PSC (carte CPx, application e-CPS, clé FIDO2 ou délégation), échange le code côté serveur avec `scope=openid scope_all` et `acr_values=eidas1`, puis identifie l'utilisateur par `SubjectNameID`. Le raccordement passe par une demande Datapass, un passage obligatoire en bac à sable (BAS), puis une demande de production depuis l'Espace Authentifié du portail industriels.

## Pro Santé Connect en deux minutes

PSC est un service gratuit développé et maintenu par l'ANS, bâti sur Keycloak. D'après la [page produit Pro Santé Connect](https://esante.gouv.fr/produits-et-services/pro-sante-connect), son implémentation est obligatoire depuis le 1er janvier 2023 pour les services numériques en santé dits « sensibles » au sens de la PGSSI-S. L'Annuaire Santé, alimenté par le RPPS, est la source des identités.

Quatre moyens d'identification électronique (MIE) apparaissent aujourd'hui dans la mire et dans le claim `authMode` des jetons :

| MIE                      | Valeur `authMode` | Remarque                                                        |
| ------------------------ | ----------------- | --------------------------------------------------------------- |
| Application e-CPS        | `MOBILE`          | Seul MIE pouvant atteindre le niveau « Certifié »               |
| Carte CPS / CPx          | `CARD`            | Lecteur PC/SC ; sous macOS, CPS-Gestion est requis hors Firefox |
| Clé de sécurité FIDO2    | `WEBAUTHN`        | Niveau Standard                                                 |
| Délégation à un FI tiers | `IDENTITY_BROKER` | Ouverture partielle, parcours dédié                             |

L'ANS déconseille explicitement de filtrer les accès sur `authMode` : il décrit le moyen utilisé, pas le niveau de garantie.

## Environnements et endpoints

La [documentation technique PSC](https://esante.gouv.fr/produits-et-services/pro-sante-connect/documentation-technique) publie deux environnements. Le bac à sable sert aux tests avec des identités fictives ; la production utilise le domaine historique `esw`.

| Endpoint                | Bac à sable (BAS)                                                                                       | Production                                                                                          |
| ----------------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| Discovery               | `https://auth.bas.psc.esante.gouv.fr/auth/realms/esante-wallet/.well-known/wallet-openid-configuration` | `https://auth.esw.esante.gouv.fr/auth/realms/esante-wallet/.well-known/wallet-openid-configuration` |
| Authorization           | `https://wallet.bas.psc.esante.gouv.fr/auth`                                                            | `https://wallet.esw.esante.gouv.fr/auth`                                                            |
| Token, UserInfo, Logout | sous `/auth/realms/esante-wallet/protocol/openid-connect/`                                              | idem sur `auth.esw.esante.gouv.fr`                                                                  |

Premier piège : le document de découverte s'appelle `wallet-openid-configuration`. Une bibliothèque qui ajoute automatiquement `/.well-known/openid-configuration` à l'issuer obtient bien un document Keycloak valide, mais son `authorization_endpoint` pointe vers Keycloak et non vers la mire `wallet`. Passez l'URL de découverte complète.

## Le parcours de raccordement

Le parcours standard vers l'Espace Communautaire suit ces étapes, avec les délais moyens annoncés par l'ANS :

1. Créer le compte du responsable technique sur iSC, le fournisseur d'identité de l'ANS pour les industriels (environ 2 semaines).
2. Déposer une demande Datapass d'accès à l'[API Pro Santé Connect](https://www.data.gouv.fr/fr/dataservices/api-pro-sante-connect/), une demande par service raccordé (environ 2 semaines). C'est le numéro de demande, et non le numéro d'habilitation, qui est attendu ensuite.
3. Activer l'Espace Authentifié et soumettre la demande de création en BAS depuis la rubrique « Mon raccordement à Pro Santé Connect ».
4. Obtenir un MIE de test : carte CPx de test via la démarche F414 si vous avez un lecteur, ou identité de test générée dans EDiT avec l'application e-CPS de test.
5. Tester, puis soumettre la demande de production.

Le [référentiel PSC](https://esante.gouv.fr/produits-et-services/pro-sante-connect/referentiel-psc) impose un test réussi sur le BAS avant toute production (EXI PSC 12) et l'accès de l'ANS à votre service de test (EXI PSC 13). La version 1.8.4 du référentiel est en vigueur ; la version 2.0, qui ajoute l'Espace de Confiance, est publiée à titre informatif. Les établissements qui achètent une solution déjà validée par PSC peuvent passer par le « raccordement direct » et sauter le BAS.

## Le flux code d'autorisation, paramètre par paramètre

PSC attend un client confidentiel : le client ID et le secret restent côté serveur (exigence 22), CORS n'est pas autorisé et la mire ne s'affiche pas dans une iframe. Un client lourd doit ouvrir un navigateur externe (EXI PSC 25 et 26).

| Paramètre                         | Valeur                                               | Source                  |
| --------------------------------- | ---------------------------------------------------- | ----------------------- |
| `response_type`                   | `code`                                               | Obligatoire             |
| `scope`                           | `openid scope_all`                                   | EXI PSC 17              |
| `acr_values`                      | `eidas1`                                             | EXI PSC 16, obligatoire |
| `state`, `nonce`                  | Aléatoires, vérifiés au retour                       | Recommandés             |
| `prompt`, `max_age`               | `login` pour forcer la ré-authentification           | Optionnels              |
| `use_camp`, `authentication_mode` | Préremplissage de la mire (fonction CAMP, juin 2026) | Optionnels              |

Au retour, PSC ajoute `code`, `state` et `iss` à l'URL de callback. L'appel au token endpoint s'authentifie soit par `client_secret_post`, soit par `tls_client_auth` avec un certificat ORG AUTH\_CLI de l'IGC-Santé dont le CN contient le client ID. Le mTLS est disponible en production depuis le 18 février 2025 ; il est conseillé sur l'Espace Communautaire et obligatoire pour l'Espace de Confiance.

Sur PKCE : le document de découverte annonce `S256`, mais le tableau de paramètres de l'ANS ne le mentionne pas. Traitez-le comme une défense en profondeur et vérifiez-le sur le BAS avant de l'activer en production.

## Exemple minimal avec openid-client

Exemple Node.js avec [openid-client](https://github.com/panva/openid-client) v6. Les valeurs sont des placeholders ; l'URL de callback doit être identique, octet pour octet, à celle déclarée lors de la création du service.

```js
import * as client from 'openid-client';

const PSC_DISCOVERY = new URL(
  'https://auth.bas.psc.esante.gouv.fr/auth/realms/esante-wallet/.well-known/wallet-openid-configuration'
);
const REDIRECT_URI = 'https://app.example.test/auth/psc/callback';

const config = await client.discovery(
  PSC_DISCOVERY,
  process.env.PSC_CLIENT_ID,
  undefined,
  client.ClientSecretPost(process.env.PSC_CLIENT_SECRET)
);

// Step 1: build the redirect to the PSC login page
export async function startLogin(session) {
  session.codeVerifier = client.randomPKCECodeVerifier();
  session.state = client.randomState();
  session.nonce = client.randomNonce();

  return client.buildAuthorizationUrl(config, {
    redirect_uri: REDIRECT_URI,
    scope: 'openid scope_all',
    acr_values: 'eidas1',
    state: session.state,
    nonce: session.nonce,
    code_challenge: await client.calculatePKCECodeChallenge(session.codeVerifier),
    code_challenge_method: 'S256'
  });
}

// Step 2: exchange the code server-side and read UserInfo
export async function handleCallback(session, currentUrl) {
  const tokens = await client.authorizationCodeGrant(config, currentUrl, {
    pkceCodeVerifier: session.codeVerifier,
    expectedState: session.state,
    expectedNonce: session.nonce
  });

  const idClaims = tokens.claims();
  const userinfo = await client.fetchUserInfo(config, tokens.access_token, idClaims.sub);

  return {
    nationalId: userinfo.SubjectNameID,
    authLevel: userinfo.auth_level,
    idToken: tokens.id_token,
    refreshToken: tokens.refresh_token
  };
}
```

Derrière un reverse proxy, reconstruisez `currentUrl` à partir de l'URL publique : la bibliothèque en dérive le `redirect_uri` envoyé au token endpoint.

## Ce que contiennent les jetons

Le scope choisi détermine les claims du UserInfo. Le référentiel exige `scope_all`, qui renvoie l'ensemble.

| Scope       | Claims UserInfo                                   |
| ----------- | ------------------------------------------------- |
| `openid`    | `sub`                                             |
| `profile`   | `codeCivilite`, `given_name`, `family_name`       |
| `rpps`      | `SubjectRefPro`, `SubjectNameID`                  |
| `scope_all` | Tout le UserInfo, dont `otherIds` et `auth_level` |

Trois évolutions de 2026 changent la façon de modéliser l'utilisateur :

* Depuis le 19 mars 2026 en production, `sub` est un UUID technique. L'ancien format qui concaténait l'identifiant national a disparu, et un service qui indexait ses comptes sur `sub` se retrouve avec des doublons.
* Le claim `PSISubjectNameID` porte l'identifiant Pro Santé Identité, vide tant que l'utilisateur ne l'a pas activé.
* Depuis le 16 avril 2026, `SubjectNameID` contient le RPPS lorsqu'il existe pour une authentification e-CPS, et `auth_level` vaut « 1 » (Standard) ou « 2 » (Certifié).

La règle reste celle d'EXI PSC 18 : clé de compte et traçabilité sur `SubjectNameID`, rapprochement par le RPPS via `otherIds` si l'utilisateur possède encore des identifiants ADELI ou locaux. La [page UserInfo](https://esante.gouv.fr/produits-et-services/pro-sante-connect/userinfo) détaille `SubjectRefPro`, qui liste les exercices et activités issus de l'Annuaire Santé. Exemple synthétique, tronqué :

```json
{
  "sub": "00000000-0000-4000-8000-000000000000",
  "SubjectNameID": "899999999999",
  "PSISubjectNameID": "",
  "auth_level": "1",
  "given_name": "JANE",
  "family_name": "DOE",
  "otherIds": [
    { "identifiant": "899999999999", "origine": "RPPS", "qualite": 1 }
  ],
  "SubjectRefPro": { "exercices": [{ "codeProfession": "10", "activities": [] }] }
}
```

PSC ne propose pas de sélection d'activité. Si votre logiciel doit savoir dans quelle structure le praticien exerce, construisez l'écran de choix à partir des `activities`.

## Durées de vie, rafraîchissement et déconnexion

| Élément              | BAS et production      |
| -------------------- | ---------------------- |
| Authorization code   | 1 minute               |
| Access token         | 2 minutes              |
| Refresh token        | 30 minutes, glissantes |
| Session PSC maximale | 4 heures               |

Un access token de deux minutes surprend souvent. EXI PSC 11 demande de rafraîchir uniquement quand l'utilisateur est actif ; l'ANS suggère de le faire au changement de page ou de rubrique. Sans activité, la session PSC expire au bout de 30 minutes.

La [déconnexion](https://esante.gouv.fr/produits-et-services/pro-sante-connect/la-deconnexion) est un GET vers le logout endpoint avec `id_token_hint` et, en option, `post_logout_redirect_uri`. Elle termine la session PSC sur tous les services et appareils de l'utilisateur. Le document de découverte affiche `backchannel_logout_supported`, mais la documentation précise que PSC ne pousse ni front-channel ni back-channel logout : pour savoir si une session vit encore, introspectez ou rafraîchissez. Le référentiel impose aussi une déconnexion automatique de votre service après inactivité, sans fermer la session PSC (EXI PSC 32).

## Espace de Confiance et API Pro Santé Connectées

L'Espace Communautaire couvre l'authentification. L'[Espace de Confiance](https://esante.gouv.fr/produits-et-services/pro-sante-connect/espace-de-confiance-api-pro-sante-connectees) va plus loin : le jeton PSC sert à appeler des services socles (DMP, ordonnance numérique et INSi dans un premier temps) via des API Pro Santé Connectées, en passant par un proxy e-Santé qui réalise l'échange de jeton auprès du serveur d'autorisation. Le mTLS y est obligatoire, les tests d'intrusion sont définis par profil de logiciel et l'accès aux environnements partenaires du CNDA exige une authentification BAS réussie et un rapport validé de l'outil de conformité du proxy. L'ANS publie un [exemple de proxy e-Santé](https://github.com/ansforge/psc-edc-proxy-esante) sur GitHub.

Pour le Ségur vague 2, l'habilitation EDC fait partie du parcours de référencement. L'ANS a fixé le dépôt du dossier de preuves (jalon 2) au 23 mai 2026 pour RIS et DRIMbox et au 22 juillet 2026 pour les LGC ; une preuve alternative issue du guichet EDC sur Convergence était acceptée, d'après l'[actualité ANS sur l'habilitation EDC](https://esante.gouv.fr/actualites/segur-vague-2-precisions-habilitation-espace-confiance-pro-sante-connect). Si votre logiciel lit l'INS, l'article sur [FR Core, INS et CI-SIS](https://alabenaicha.me/fr/insights/fhir-fr-core-ins-cisis) explique où l'INSi s'insère.

## Pièges fréquents

| Piège                                                        | Symptôme                                   | Correction                                                                     |
| ------------------------------------------------------------ | ------------------------------------------ | ------------------------------------------------------------------------------ |
| Découverte standard au lieu de `wallet-openid-configuration` | Mire Keycloak brute, parcours e-CPS absent | Configurer l'URL de découverte complète                                        |
| Comptes indexés sur `sub`                                    | Doublons après mars 2026                   | Clé sur `SubjectNameID`, rapprochement RPPS                                    |
| `acr_values` omis                                            | Erreur à l'autorisation ou non-conformité  | Toujours envoyer `eidas1` ; `eidas2` n'est pas encore servi                    |
| Contrôle d'accès sur `authMode`                              | Refus arbitraires selon le MIE             | Utiliser `auth_level` avec discernement, plus les attributs de `SubjectRefPro` |
| Refresh en tâche de fond                                     | Session PSC prolongée sans utilisateur     | Rafraîchir sur action utilisateur seulement                                    |
| Client PAR activé par défaut                                 | Échec de l'autorisation                    | PAR est annoncé mais pas activé ; le désactiver                                |
| Apache `mod_auth_openidc`                                    | `otherIds` absent des en-têtes             | `OIDCPassUserInfoAs json`                                                      |

## Checklist avant la demande de production

* Client ID et secret (ou certificat ORG AUTH\_CLI) stockés uniquement côté serveur.
* Scopes, `acr_values`, `state` et `nonce` envoyés et vérifiés.
* Comptes clés sur `SubjectNameID`, fusion autour du RPPS si d'autres modes de connexion existent (EXI PSC 19).
* Refresh piloté par l'activité, déconnexion PSC et déconnexion locale sur inactivité.
* Données de test uniquement sur le BAS (EXI PSC 37), hébergement HDS si le service traite des données de santé (EXI PSC 06, voir [l'article sur l'hébergement HDS](https://alabenaicha.me/fr/insights/hds-health-data-hosting-france)).
* Tests réalisés avec une carte de test et une e-CPS de test, outils de [PSC Tools](https://psc-tools.esante.gouv.fr/) à portée de main.

Le jeton PSC sert aussi d'authentification sur l'API LPS de la messagerie sécurisée : l'article sur [l'intégration MSSanté](https://alabenaicha.me/fr/insights/mssante-messaging-integration) montre comment. Côté FHIR, le lancement d'une application dans le contexte d'un DPI relève d'un autre protocole, décrit dans [SMART on FHIR](https://alabenaicha.me/fr/insights/smart-on-fhir-app-launch).

Si vous devez cadrer un raccordement PSC ou une candidature à l'Espace de Confiance, je propose un accompagnement en [développement HealthTech](https://alabenaicha.me/fr/services/healthtech-development).
