Before a client system can interface with the eHealth Infrastructure a set of items must be carried out and complied with:
A client application to the eHealth Infrastructure must implement an OpenID Connect "code flow" in order to login and get a set of tokens.
A client application must be created and setup in the login server and assigned a name, and the URL's used to redirect back to the client must be whitelisted.
A client can be either confidential (like a server application) or public (like an app or a web application). Confidential clients authenticate themselves with a password. Public client must use PKCE (pronounced "pixi"). Explanations can be found many places, for instance here and here.
Information on redirect URL.
These URIs specifies where client sends (redirects) their users to to after log-ins/log-outs/refresh. The URLs that the Keycloak shall redirects to shall be whitelisted. The URLs shall be specific and may not contain ’wildcards’ (*) as this can be a security risk (see Securing Applications and Services Guide (keycloak.org)). This could be pages like ‘/login
', '/login-landing
' page which users are redirected to after logged in and/or logged out.
Having completed these bullets, the Authorization Server (AS) will delegate parts of the login to potentially other federated servers, but that is transparent for the client (provided the login is handled by a generic browser window that can handle redirects).
Client types
Clients currently comes in two flavors. Clinical- and citizen clients. Each type uses its own login flow and realm which results in two authorization url's (see the section Authorization Server for further information).
As a result: If clients (and server applications) handles both clinical and citizen users, then token validation must be able to handle multiple certificate url's.
Example of certificate url's for the inttest environment:
Clinical clients: https://saml.inttest.ehealth.sundhed.dk/auth/realms/ehealth/protocol/openid-connect/certs
Citizen clients: https://saml.inttest.ehealth.sundhed.dk/auth/realms/nemlogin/protocol/openid-connect/certs
Clinical logins
For clinical client applications it is expected that the total login flow will look somewhat like this. Details can vary depending on the organization of the user (region or municipality).
Federated clinical login flow
If there is already a session in SEB from the same browser, then a single-signon experience can be realized.
Citizen logins (first time)
For citizens, a similar login flow could look like this:
Federated citizen login flow
Client systems for citizens will in their (initial) login be met by NemID, which is presented via a federated login service from NemLogin. If the client system is running on a platform that supports it, it will be possible to store the RT and apply this later to resume an authenticated session, based on either pin code or biometric data. Details of this is can be read on Key Service overview, but in essence it involves selecting a PIN, registering a user / device / PIN via the Key Service, and resuming the session based on stored data and calling the Key Service.
The aim of this is to make an alternative - but simpler - "authentication" available to citizens in a secure manner.
Authorization Server Endpoints
Current active environments:
Clinical:
Base url: https://saml.${environment}.ehealth.sundhed.dk/auth/realms/ehealth
https://saml.inttest.ehealth.sundhed.dk/auth/realms/ehealth
Session lifespan is 10 hours (30 minutes idle)
Citizen:
Base url: https://saml.${environment}.ehealth.sundhed.dk/auth/realms/nemlogin
https://saml.inttest.ehealth.sundhed.dk/auth/realms/nemlogin
Session lifespan is 1200 days (120 days idle)
Client Adapters
Keycloak (being the AS) has an extensive set of client adapters (libraries) for usage on various platforms and programming languages:
Example authentication
If, for some reason, you can't use a keycloak client adapter - an example URL for an authentication request using HTTP GET, written in a readable format is shown below. It should be sent as a single line without spaces or newlines.
Authentication request
http://saml.inttest.ehealth.sundhed.dk/auth/realms/ehealth/protocol/openid-connect/auth? response_type=code& client_id=<client_id>& redirect_uri=<redirect_uri>& scope=openid+profile& state=<state>& nonce=<nonce>& code_challenge=<challenge>& code_challenge_method=S256
The parameters have the following meaning:
response_type=code – indicates that your server expects to receive an authorization code
client_id= – A client ID that is registered on the Authorization Server
redirect_uri= – Indicates the URL to return the user to after authorization is complete, such as org.example.app://redirect or a tradition URL for a webapp https://app.example.org/redirect.
state=1234zyx – A random string generated by your application, which you’ll verify later
code_challenge=XXXXXXXXX – The code challenge generated as previously described
code_challenge_method=S256 – either
plain
orS256
, depending on whether the challenge is the plain verifier string or the SHA256 hash of the string. If this parameter is omitted, the server will assumeplain
.
When the authentication is complete, the browser is redirected back to the given "redirect_uri" (which must be whitelisted in the AS) including a "code" as a request parameter. This code must be used when calling the token endpoint afterwards.
Example token exchange
An example of the following POST request to obtain a context aware access token is shown below.
Authorization code exchange
POST /auth/realms/ehealth/protocol/openid-connect/token HTTP/1.1 Content-Type: application/x-www-form-urlencoded grant_type=refresh_token& refresh_token=<refresh_token>& client_id=<client_id>& client_secret=<client_secret>& organization_id=<organization_id>& care_team_id=<care_team_id>& patient_id=<patient_id>& episode_of_care_id=<episode_of_care_id>
The first four parameters are required, and the remaining are optional. 'organization_id' and 'care_team_id' can be used individually or in combination. 'patient_id' and 'episode_of_care_id' can also be used individually or in combination, but requires that 'care_team_id' is also present. More details on context switching can be found at Switching Context.
Use of Access Token
The Access Token is sent as an HTTP header (See https://jwt.io/introduction/ for further introduction) in all service requests in this form (where "<access token>" is replaced by the specific Access Token):
Authorization: Bearer <access token>
Access Tokens and Refresh Tokens are so-called "opaque tokens" but may be in JWT format. Client systems must not assume this and the format of AT and RT may change without notice.
Determining System Roles Available in Current Context
The list of system roles available with the currently selected context can be obtained by querying the AS using a HTTP GET with the current Access token at the path /auth/realms/ehealth/protocol/openid-connect/userinfo. The following shows an example request in realm ehealth for a clinical user:
Userinfo
GET https://saml.exttest.ehealth.sundhed.dk/auth/realms/ehealth/protocol/openid-connect/userinfo Response: { "sub": "...", "email_verified": true, "cpr": "...", "roles": [ "Service and Logistics", "Incident Manager", "Report User", "Questionnaire Editor", "Catalogue Responsible", "Clinical Viewer", "Care Team Administrator", "Clinical Administrator", "Catalogue Annotator", "Terminology Administrator", "Monitoring Adjuster", "Citizen Enroller", "Monitoring Assistor", "Contract Responsible", "Order Placer", "Incident Reporter", "Clinical Supporter" ], "preferred_username": "...", ... }
The list in “roles” is the KeyCloak role names which can be mapped to system roles by doing a reverse lookup from groups, see https://ehealth-dk.atlassian.net/wiki/spaces/EDTW/pages/270991361/Switching+Context#Mapping-from-Role-to-Privileges.
Logout
To end a session, use end_session_endpoint found in the openid-configuration of the environment (e. g. : openid-configuration)
Example:
Logout
GET /auth/realms/ehealth/protocol/openid-connect/logout HTTP/1.1 Authorization: Bearer <access_token>
You can redirect the browser to /auth/realms/ehealth/protocol/openid-connect/logout?redirect_uri=encodedRedirectUri, which logs you out if you have an SSO session with your browser.