Search

SSO to ZorgDomein based on SMART on FHIR

NB: This is a draft specification!

Introduction

For many users their primary information system (or any other application) is the starting point to initiate or follow up a transaction in ZorgDomein. For a smooth transition from that application to ZorgDomein the user should preferably not have to reauthenticate when launching ZorgDomein. Hence a single sign-on mechanism from that application to ZorgDomein is necessary. After the user has entered ZorgDomein, ZorgDomein will need some contextual data (e.g., patient details) from the source application to guide the user in his journey to start or follow up a transaction in ZorgDomein. In that case the source application is the data provider and ZorgDomein is the data consumer.

In more abstract terms, the SSO mechanism should enable an application user to send a SSO call to ZorgDomein and should enable ZorgDomein to request transaction data from the source application based on that SSO call. The SSO call could be considered as a front channel notification, notifying ZorgDomein about the fact that it has some data available for ZorgDomein to start or follow up a transaction. Based on that front channel notification, ZorgDomein can pull available transaction data from the source application.

This matches the notified-pull exchange pattern, hence the SSO mechanism should preferrably use the same principles as the notified-pull mechanism that uses back-channel notifications (i.e. notifications from backend to backend). That means that the SSO mechanism should use the OAuth2 protocol for authorization and authentication.

This is perfectly in line with the SMART on FHIR specification, see SMART Application Launch Framework Implementation Guide 1.0.0 for detailed documentation. This framework uses the OpenID Connect specification for authentication of user to a third party application. Authorization to the FHIR API of the platform application is established through OAuth 2.0. This mechanism enables the client (ZorgDomein) to access data at the server (source application) using access tokens that are exchanged during SSO. Therefore, inbound SSO to ZorgDomein is implemented according to the SMART on FHIR specification.

Interaction sequence

The SSO mechanism involves four different system roles:

  • Browser: the internet browser that presents the user interface to the end user
  • ZD server: the ZorgDomein web server
  • App FHIR server: the FHIR server of the external application
  • App auth server: the server of the external application that manages user authorization.

During the process of SSO these systems exchange data according to the sequence diagram displayed below.

Browser
ZorgDomein
XIS FHIR server
XIS Auth server
1. Launch ZorgDomein
2. Request authorization URLs
3. Provide authorization URLs
4. Redirect to XIS authorization URL
5. Request authorization code
6. Serve authorization page (optional)
7. Send user approval (optional)
8. Provide authorization code, redirect to ZorgDomein
9. Provide authorization code
10. Request access token and ID token
11. Provide access token and ID token
12. Request patient and context details
13. Serve patient and context details
14. SSO complete, serve ZorgDomein start page

The flow starts with an authenticated user in the external application. As displayed in the diagram the flow consists of the following steps:

1. Launch ZorgDomein

The user wants to navigate from an external application to ZorgDomein. To facilitate this, the frontend of the external application exposes a launch URL that points to ZorgDomein. The user navigates to this URL in the browser, so the browser issues a HTTP GET request to the following url: https://www.zorgdomein.nl/api/oauth2/login. The launch request must contain the following query parameters:

  • launch: opaque identifier for this specific launch. This parameter will be communicated back to the external application at authorization time by passing along a launch parameter in the authorization request (see step 4/5).
  • iss: base FHIR endpoint of the external application.

Example launch URL:

https://www.zorgdomein.nl/api/oauth2/login?
  launch=twjAavxomS4ZpGcu
  &iss=https%3A%2F%2Fwww.xis.com%2Fapi%2Ffhir%2Fstu3

2. Requests authorization URLs

After receiving the launch notification, ZorgDomein sends an HTTP GET request to the FHIR server of the external application to query its capability statement:

GET [AppFhirServerUrl]/metadata

This resource contains (among other details) the OAuth authorization and token endpoints of the external application. ZorgDomein needs these endpoints to request authorization at the external authorization server in step 4.

3. Provide authorization URLs

The external FHIR server will respond with a CapabilityStatment resource. ZorgDomein will extract the endpoint values from this document as follows:

  • CapabilityStatement.rest.security.extension(url:http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris).extension(url:authorize).valueUri: url of the authorization endpoint
  • CapabilityStatement.rest.security.extension(url:http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris).extension(url:token).valueUri: url of the token endpoint

4. Redirect to external application authorization URL

To send a request for an authorization code to the external authorization server, ZorgDomein sends a HTTP redirect response to the browser, redirecting to the authorization url that was extracted in step 3. ZorgDomein will add the following query parameters to that url:

  • response_type: Fixed value: code (the OAuth authorization code flow is used, see OpenID Connect specifications for details)
  • client_id: The OAuth client identifier of ZorgDomein. This identifier will be exchanged between ZorgDomein and the application vendor during the registration process.
  • redirect_uri: Fixed value: https://www.zorgdomein.nl/api/oauth2/authorization-code.
  • launch: Contains the value of the launch id issued by the external application in step 1.
  • scope: Describes the access that ZorgDomein needs.
  • state: An opaque value used by ZorgDomein to maintain state between the request and callback. The external authorization server must include this value when redirecting the browser back to ZorgDomein (see step 8). The parameter is used for preventing cross-site request forgery or session fixation attacks.
  • nonce (optional): String value used to associate a client session with an ID Token, and to mitigate replay attacks. The value is passed through unmodified from the authentication request to the ID Token. For implementation notes, see section 15.5.2 of the OpenID Connect specifications.
  • aud: Base URL of the FHIR server of the external application. This parameter prevents leaking a genuine bearer token to a counterfeit resource server. Note: this aud value is the same as the launch’s iss value.

Example redirect URL:

https://www.xis.com/oauth/authorize?
  response_type=code
  &client_id=zdclientid
  &redirect_uri=https%3A%2F%2Fwww.zorgdomein.nl%2Fapi%2Foauth%authorization-code
  &launch=twjAavxomS4ZpGcu
  &scope=openid%20profile%20email%20phone%20launch
  &state=X2HO7ZxXTd7NNwe3
  &aud=https%3A%2F%2Fwww.xis.com%2Fapi%2Ffhir%2Fstu3

5. Request authorization page

Following the HTTP redirect that the browser receives from ZorgDomein, the browser sends a HTTP GET request to the authorization page of the external application. Through this page ZorgDomein can request an authorization code.

6. Serve authorization page (optional)

The external application responds with the authorization page which presents the authorization request to the user.

7. Send user approval (optional)

The user reviews and approves the authorization request. The browser sends the authorization approval to the authorization server of the external application.


NB: steps 6 and 7 are optional steps. These steps are only relevant if the source application requires the user to authorize ZorgDomein explicitly. It is also possible to assume an implicit authorization: by navigating to ZorgDomein from the source application, the source application might assume that the user agrees with exchanging data with ZorgDomein. In that case steps 6 and 7 can be omitted, and the source application can provide the authorization code (step 8) directly after the request for the authorization code (step 5).

8. Provide authorization code to browser

When approval has been received, the authorization server of the external application generates an authorization code and communicates this code through an HTTP redirect to ZorgDomein. This causes the browser to redirect to the ZorgDomein redirect_uri (as provided in step 4) with the following query parameters:

  • code: The authorization code generated by the authorization server.
  • state: The exact value received from the app server in step 4.

Example redirect URL:

https://www.zorgdomein.nl/api/oauth2/authorization-code?
  code=q9kNvFCZSUNdOoAat2CaD229bl6744dD&
  &state=X2HO7ZxXTd7NNwe3

9. Provide authorization code to ZorgDomein

Following the redirect command received from the authorization server of the external application, the browser sends a HTTP GET request to the redirect_uri (i.e. ZorgDomein) containing the authorization code and state parameters.

10. Request access token

After obtaining the authorization code, ZorgDomein trades the code for an access token via HTTP POST to the token endpoint URL of the authorization server of the external application, using content-type application/x-www-form-urlencoded. The following form parameters will be included in the POST:

  • grant_type: Fixed value: authorization_code.
  • code: Code that ZorgDomein received from the authorization server of the external application.
  • redirect_uri: The same redirect_uri used in the initial authorization request (step 4).
  • client_id: The OAuth client identifier of ZorgDomein. This identifier will be exchanged between ZorgDomein and the application vendor during the registration process.

Example POST request:

POST /api/oauth/token HTTP/1.1
Host: www.zorgdomein.nl
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=q9kNvFCZSUNdOoAat2CaD229bl6744dD
&redirect_uri=https%3A%2F%2Fwww.zorgdomein.nl%2Fapi%2Foauth%2Fauthorization-code
&client_id=zorgdomein.nl

11. Provide access token and ID token

The authorization server of the external application must respond with a JSON object that contains the following parameters:

  • access_token: The access token issued by the authorization server.
  • token_type: Fixed value: Bearer.
  • expires_in: Lifetime in seconds of the access token, after which the token will not be accepted by the external application.
  • scope: Scope of access authorized. This may be different from the scope requested by the ZorgDomein.
  • id_token: Authenticated user identity and details. The sub claim in the ID token must be a locally unique and never reassigned identifier of the user in the external application. This value is used by ZorgDomein to map the external user to a known user in ZorgDomein.
    The ID token must be signed using an asymmetric key pair. The public key of this key pair must be available as a JSON web key at a URI that is exposed by the external application. This URI must be referenced by the jwks_uri attribute in the OpenID config file that the external application must publish at [issuer]/.well-known/openid-configuration, where [issuer] is the value of the iss claim in the ID token.
  • refresh_token: Token that can be used to obtain a new access token, using the same or a subset of the original authorization grants.
  • patient: ID of the FHIR Patient resource representing the active patient context in the external application.
  • __organization: Id of the organization of the user that initiated the launch. This ID must be exchanged between ZorgDomein and the external application vendor out of band.
  • __task: Id of the FHIR Task resource that represents the transaction in the external application.

Example access token response:

{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.ey…",
  "token_type": "Bearer",
  "expires_in": 1800,
  "scope": "openid profile email phone launch/patient launch online_access",
  "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6…",
  "refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ…",
  "patient": "9be07408-e206-4d5f-9bdc-7024c187769b",
  "__organization": "60c363cd-7eb5-4da1-b8c5-5439d0ee43dc",
  "__task": " b903e17e-883a-11ec-a8a3-0242ac120002"
}

Based on organization identifier and the claims in the OpenID token that was provided by the external application in the access token response, ZorgDomein can either map the provided organization and user IDs to a known user for a known organization, or register a new user. Subsequently, ZorgDomein can create a user session.

12. Request patient and context details

Now that ZorgDomein has obtained an access token, it is able to send authorized requests to the FHIR server of the external application. To set the application context in ZorgDomein, ZorgDomein will request the details of the patient that should be initiated by requesting the Patient resource as specified in the patient parameter in the access token response using a FHIR read request:

GET [AppFhirServerUrl]/Patient/[AppPatientId]

ZorgDomein will include the retrieved access token as a Bearer token in the HTTP header of the GET request.

Additionally, ZorgDomein will request insurance details for the patient using a FHIR search request:

GET [AppFhirServerUrl]/Coverage?subscriber=[AppPatientId]

ZorgDomein will use the patient details that are provided in the Patient resource and the insurance details that are provided in the Coverage resource to set the patient context in ZorgDomein.

If a __task attribute was provided in the access token response, ZorgDomein will use the value of this attribute to request a FHIR Task resource using a FHIR read request:

GET [AppFhirServerUrl]/Task/[__task]

This Task resource will be considered as the representation of the transaction in the external application. All relevant actions performed in ZorgDomein on this transaction, will be fed back to this Task resource in the external application.

13. Provide patient and context details

After validation of the access token in the request of ZorgDomein, the external resource server will respond to the app with the requested Patient, Coverage and Task resources.

14. SSO complete, serve ZorgDomein landing page

Now that both a user session has been established and a patient context is set in ZorgDomein, ZorgDomein can serve its landing page to the user.