Digital Health-9 min read

Integrating Pro Santé Connect: OpenID Connect, e-CPS and the French health professional login

A practical guide to connecting software to Pro Santé Connect: Datapass and sandbox onboarding, the authorization code flow, scopes and claims, token lifetimes, logout, mTLS and the Espace de Confiance.

Ala Ben Aicha

Integrating Pro Santé Connect: OpenID Connect, e-CPS and the French health professional login

Direct answer

Pro Santé Connect (PSC) is the OpenID Connect identity provider run by France's digital health agency (ANS) for health, medico-social and social sector professionals. Your application is a confidential OIDC client: it redirects to the PSC login page (CPx smart card, e-CPS mobile app, FIDO2 key or delegated identity), exchanges the code server-side with scope=openid scope_all and acr_values=eidas1, then keys the user on SubjectNameID. Onboarding goes through a Datapass request, a mandatory sandbox phase (BAS), then a production request from the ANS industry portal.

Pro Santé Connect in two minutes

PSC is a free service built and run by ANS on top of Keycloak. According to the Pro Santé Connect product page, it has been mandatory since 1 January 2023 for digital health services classed as "sensitive" under the French health information security policy (PGSSI-S). Identities come from the Annuaire Santé, which is fed by the RPPS professional registry.

Four authentication means (MIE) currently appear on the login page and in the authMode claim:

Authentication means authMode value Note
e-CPS mobile app MOBILE The only means that can reach the "Certified" level
CPS / CPx smart card CARD PC/SC reader; on macOS, CPS-Gestion is needed outside Firefox
FIDO2 security key WEBAUTHN Standard level
Delegation to a third-party IdP IDENTITY_BROKER Partially open, separate onboarding track

ANS explicitly advises against gating access on authMode: it tells you which means was used, not the assurance level.

Environments and endpoints

The PSC technical documentation lists two environments. The sandbox is for tests with fictitious identities; production still uses the historical esw domain.

Endpoint Sandbox (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 under /auth/realms/esante-wallet/protocol/openid-connect/ same path on auth.esw.esante.gouv.fr

First trap: the discovery document is named wallet-openid-configuration. A library that appends /.well-known/openid-configuration to the issuer still gets a valid Keycloak document, but its authorization_endpoint points at raw Keycloak instead of the wallet login page. Pass the full discovery URL.

The onboarding path

The standard path into the PSC community tier (Espace Communautaire) has these steps, with the average lead times ANS publishes:

  1. Create an account for your technical lead on iSC, the ANS identity provider for vendors (about 2 weeks).
  2. File a Datapass request for the Pro Santé Connect API, one request per connected service (about 2 weeks). Later forms ask for the request number, not the authorisation number.
  3. Activate the authenticated area of the industry portal and submit a sandbox creation request under "Mon raccordement à Pro Santé Connect".
  4. Get a test authentication means: a test CPx card through the F414 procedure if you have a reader, or a test identity generated in EDiT and used with the test e-CPS app.
  5. Test, then submit the production request.

The PSC requirements framework requires a successful sandbox test before production (EXI PSC 12) and ANS access to your test service (EXI PSC 13). Version 1.8.4 is in force; version 2.0, which adds the Espace de Confiance, is published for information. Healthcare facilities buying a solution PSC has already validated can use "direct connection" and skip the sandbox.

The authorization code flow, parameter by parameter

PSC expects a confidential client: client ID and secret stay on a server (requirement 22), CORS is not allowed and the login page will not render in an iframe. A desktop (thick) client must open an external browser (EXI PSC 25 and 26).

Parameter Value Source
response_type code Required
scope openid scope_all EXI PSC 17
acr_values eidas1 EXI PSC 16, required
state, nonce Random, checked on return Recommended
prompt, max_age login to force re-authentication Optional
use_camp, authentication_mode Login page pre-fill (CAMP feature, June 2026) Optional

On return PSC adds code, state and iss to the callback URL. The token call authenticates with either client_secret_post or tls_client_auth, the latter using an IGC-Santé ORG AUTH_CLI certificate whose CN contains the client ID. mTLS has been available in production since 18 February 2025; it is recommended for the community tier and mandatory for the Espace de Confiance.

On PKCE: the discovery document advertises S256, but the ANS parameter table does not mention it. Treat it as defence in depth and confirm it on the sandbox before enabling it in production.

A minimal openid-client example

Node.js with openid-client v6. Values are placeholders; the callback URL must match, byte for byte, the one registered for the service.

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
  };
}

Behind a reverse proxy, rebuild currentUrl from the public URL: the library derives the redirect_uri it sends to the token endpoint from it.

What the tokens contain

The scope decides which UserInfo claims you get. The framework requires scope_all, which returns everything.

Scope UserInfo claims
openid sub
profile codeCivilite, given_name, family_name
rpps SubjectRefPro, SubjectNameID
scope_all All of UserInfo, including otherIds and auth_level

Three 2026 changes affect how you model the user:

  • Since 19 March 2026 in production, sub is a technical UUID. The old format that embedded the national identifier is gone, and a service that keyed accounts on sub ends up with duplicates.
  • The PSISubjectNameID claim carries the Pro Santé Identité identifier, empty until the user activates it.
  • Since 16 April 2026, SubjectNameID holds the RPPS number when one exists for e-CPS logins, and auth_level is "1" (Standard) or "2" (Certified).

The rule is still EXI PSC 18: key accounts and audit trails on SubjectNameID, and reconcile through the RPPS in otherIds if the user still has ADELI or local identifiers. The UserInfo page documents SubjectRefPro, which lists the practice records and activities from the Annuaire Santé. A synthetic, truncated example:

{
  "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 has no activity picker. If your software needs to know which facility the clinician is working in, build the selection screen from activities.

Lifetimes, refresh and logout

Item Sandbox and production
Authorization code 1 minute
Access token 2 minutes
Refresh token 30 minutes, sliding
Maximum PSC session 4 hours

A two-minute access token surprises most teams. EXI PSC 11 asks you to refresh only while the user is active; ANS suggests doing it when the user opens a new page or section. Without activity, the PSC session expires after 30 minutes.

Logout is a GET to the logout endpoint with id_token_hint and, optionally, post_logout_redirect_uri. It ends the PSC session across every service and device the user is signed in to. The discovery document shows backchannel_logout_supported, but the documentation states PSC pushes neither front-channel nor back-channel logout: to know whether a session is still alive, introspect or refresh. The framework also requires your service to log the user out after inactivity without closing the PSC session (EXI PSC 32).

Espace de Confiance and API Pro Santé Connectées

The community tier covers authentication. The Espace de Confiance goes further: the PSC token is used to call national services (DMP, e-prescription and INSi in the first phase) through "API Pro Santé Connectées", via an e-Santé proxy that performs a token exchange with the authorization server. mTLS is mandatory there, penetration tests are defined per software profile, and access to the CNDA partner test environments requires a successful sandbox login plus a validated report from the proxy conformance tool. ANS publishes a sample e-Santé proxy on GitHub.

For Ségur wave 2, Espace de Confiance approval is part of the software listing (référencement) process. ANS set the evidence deadline (milestone 2) at 23 May 2026 for RIS and DRIMbox and 22 July 2026 for GP practice software (LGC), with an alternative proof from the Convergence submission desk accepted, according to the ANS note on Espace de Confiance approval. If your software reads the national patient identifier, the article on FR Core, INS and CI-SIS explains where INSi fits.

Common pitfalls

Pitfall Symptom Fix
Standard discovery instead of wallet-openid-configuration Raw Keycloak login, no e-CPS flow Configure the full discovery URL
Accounts keyed on sub Duplicates after March 2026 Key on SubjectNameID, reconcile on RPPS
Missing acr_values Authorization error or non-compliance Always send eidas1; eidas2 is not served yet
Access control on authMode Arbitrary denials depending on the means Use auth_level with care, plus SubjectRefPro attributes
Background refresh PSC session kept alive with nobody there Refresh only on user activity
PAR enabled by default in the client Authorization fails PAR is advertised but not enabled; turn it off
Apache mod_auth_openidc otherIds missing from headers OIDCPassUserInfoAs json

Checklist before the production request

  • Client ID and secret (or ORG AUTH_CLI certificate) kept server-side only.
  • Scopes, acr_values, state and nonce sent and verified.
  • Accounts keyed on SubjectNameID, merged around the RPPS if other login methods exist (EXI PSC 19).
  • Activity-driven refresh, PSC logout, and local logout on inactivity.
  • Test data only in the sandbox (EXI PSC 37), and certified hosting if the service processes health data (EXI PSC 06, see the HDS hosting article).
  • Tests run with a test card and a test e-CPS, with PSC Tools at hand.

The PSC token also authenticates against the MSSanté secure messaging API LPS: the MSSanté integration article shows how. On the FHIR side, launching an app inside an EHR context is a different protocol, covered in SMART on FHIR.

If you need to scope a PSC integration or an Espace de Confiance application, I offer support through HealthTech development.

Pro Santé ConnectPSCe-CPSOpenID ConnectANSRPPSSégurFrance

Related reading and services

Let's Continue the Conversation

Have questions about this topic? I'd love to hear from you.