Search

SSO from ZorgDomein

Introduction

ZorgDomein supports single sign on (SSO) to external applications (“apps”) for both practitioners and patients that use ZorgDomein. After SSO to an app, this app can exchange data with ZorgDomein through a FHIR API. This SSO-mechanism is based on SMART on FHIR, see SMART Application Launch Framework Implementation Guide 1.0.0 for detailed documentation. This framework uses the OpenID Connect specification for authentication of a platform user to a third party app. Authorization to the FHIR API of the platform application is established through OAuth 2.0. The SMART on FHIR specification defines two different ways for launching an external application:

  • EHR launch: the application launches from an existing EHR or Patient Portal session.
  • Standalone launch: the application launches as a standalone application and actively requests user authentication at the EHR.

ZorgDomein only supports the EHR launch (where ZorgDomein takes the role of an EHR).

Public vs confidential apps

The OAuth2 specification defines two types of apps: confidential and public apps. The differentiation is based upon whether the execution environment within which the app runs enables the app to protect secrets. Confidential apps are able to protect secrets, public apps aren’t. A client secret may be used to authenticate the app when requesting access tokens. Pure client-side apps (for example, HTML5/JS browser-based apps, iOS mobile apps, or Windows desktop apps) apps can provide adequate security, but they may be unable to “keep a secret” in the OAuth2 sense. In other words, any “secret” key, code, or string that is statically embedded in the app can potentially be extracted by an end-user or attacker. Hence security for these apps cannot depend on secrets embedded at install-time. ZorgDomein considers all apps as public apps. This means that we don’t issue client secrets, and that we do not require HTTP authentication when requesting access tokens.

The SSO mechanism involves four different system roles:

  • Browser: the internet browser that presents the user interface to the end user
  • App server: the web server of the third party application
  • FHIR server: the server that exposes an API to the ZorgDomein application
  • Auth server: the server that manages authorization of users. This server is maintained by ZorgDomein.  

Interaction sequence

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

The flow starts with an autheniticated user in ZorgDomein. As displayed in the diagram the flow consists of the following steps:

Browser
App
ZD FHIR server
ZD Auth server
1. Launch app
Browser navigates to launch URL of external application. More details
2. Requests authorization URLs
App requests authorization URLs from ZorgDomein. More details
3. Provide authorization URLs
The ZorgDomein FHIR server responds with the requested URLs. More details
4. Redirect to ZorgDomein authorization URL
App redirects browser to ZorgDomein authorization URL to request authorization code. More details
5. Request authorization code
Browser requests ZorgDomein authorization page. More details
6. Serve authorization page
ZorgDomein Auth server responds with authorization page. More details
7. Send user approval
User authorizes request, browser sends approval to ZorgDomein Auth server. More details
8. Provide authorization code, redirect to app
ZorgDomein authorization server responds with authorization code and redirects to app server. More details
9. Provide authorization code
Browser sends authorization code to app server. More details
10. Request access token
App server requests access token at ZorgDomein authorization server. More details
11. Provide access token and ID token
ZorgDomein authorization server responds with access token. More details
12. SSO complete, serve app start page
SSO completed, app sends start page to browser. More details

1. Launch app

The user wants to navigate to an external application. To facilitate this the ZorgDomein frontend exposes a lauch URL that points to the external application. The user navigates to this URL in the browser, so the browser issues a HTTP GET request to the launch URL of the application server. The launch URL will contain the following query parameters:

  • launch – opaque identifier for this specific launch. This parameter must be communicated back to ZorgDomein at authorization time by passing along a launch parameter (see step 4/5).
  • iss – fixed value: https://www.zorgdomein.nl/api/fhir/stu3  (base FHIR endpoint of ZorgDomein).

Example launch URL:

https://my.smart-app.com/oauth/login?  
  launch=twjAavxomS4ZpGcu  
  &iss=https%3A%2F%2Fwww.zorgdomein.nl%2Fapi%2Ffhir%2Fstu3

2. Requests authorization URLs

On receiving the launch notification, the app sends an HTTP GET request to the ZorgDomein FHIR server to query ZorgDomein’s Well-Known Uniform Resource Identifiers JSON document (www.zorgdomein.nl/api/fhir/stu3/.well-known/smart-configuration). This resource contains (among other details) the OAuth authorization and token endpoints of ZorgDomein. The app needs these endpoints to request authorization at ZorgDomein in step 4.

3. Provide authorization URLs

The ZorgDomein FHIR server will respond with a Well-Known URI’s JSON document. The endpoint values can be extracted from the response (see example below). 

{
    "authorization_endpoint": "https://www.zorgdomein.nl/api/oauth/authorize",
    "token_endpoint": "https://www.zorgdomein.nl/api/oauth/token",
    "token_endpoint_auth_methods_supported": [
        "none"
    ],
    "scopes_supported": [
        "openid",
        "profile",
        "email",
        "address",
        "phone",
        "online_access",
        "http://zorgdomein.nl/terminology/naming-system/zd-number" 
    ],
    "capabilities": [
        "launch-ehr",
        "client-public",
        "context-ehr-patient",
        "sso-openid-connect"
    ]
}

4. Redirect to ZorgDomein authorization URL

To send a request for an authorization code to the ZorgDomein authorization server, the app sends a HTTP redirect response to the browser, redirecting to the authorization URL with the following query parameters:

  • response_type – Fixed value: code (the OAuth authorization code flow is used, see Open ID specifications for details)
  • client_id – The client’s identifier. This identifier will be exchanged between ZorgDomein and app supplier during the registration process (see Registration of SMART apps).
  • redirect_uri – Must match one of the redirect URLs that have been registered at ZorgDomein during the registration process.
  • launch – Must contain the value of the launch id issued by ZorgDomein in step 1.
  • scope – Describes the access that the app needs. Multiple scopes can be defined separated by a space. Supported values:
    • openid – request an OpenID token .
    • profile – request access to the user’s default profile claims. The following claims are supported and may be included in the ID token: name, family_name, gender and birthdate
    • email – request access to the email and email_verified claims.
    • address – requests access to the address claim (see Open ID specifications for details).
    • phone – request access to the phone_number and phone_number_verified claims.
    • http://zorgdomein.nl/terminology/naming-system/zd-number –  request the ZD number of the current transaction in ZorgDomein at launch time. 
  • state – An opaque value used by the app to maintain state between the request and callback. The ZorgDomein authorization server includes this value when redirecting the browser back to the app (see step 8). The parameter SHALL be 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 specifications.
  • aud – Base URL of the ZorgDomein FHIR server: https://www.zorgdomein.nl/api/fhir/stu3. 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.zorgdomein.nl/api/oauth/authorize?  
  response_type=code  
  &client_id=mysmartappid  
  &redirect_uri=https%3A%2F%2Fmy.smart-app.com%2Foauth%2Fgetaccesstokenforcode  
  &launch=twjAavxomS4ZpGcu  
  &scope=openid%20profile%20email%20phone%20launch%2Fpatient%20patient%2FAppointment.write%20online_access  
  &state=X2HO7ZxXTd7NNwe3  
  &aud=https%3A%2F%2Fwww.zorgdomein.nl%2Fapi%2Ffhir%2Fstu3

5. Request authorization page

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

6. Serve authorization page

ZorgDomein responds with the authorization page which presents the authorization request to the user.

7. Send user approval

The user reviews and approves the authorization. The browser sends the authorization approval to the ZorgDomein authorization server.

8. Provide authorization code to browser

When approval has been received, the ZorgDomein authorization server generates an authorizaton code and communicates this code to the app server through an HTTP redirect to the app server. This causes the browser to redirect to the 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://my.smart-app.com/oauth/getaccesstokenforcode?  
  code=q9kNvFCZSUNdOoAat2CaD229bl6744dD&  
  &state=X2HO7ZxXTd7NNwe3

9. Provide authorization code to app

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

10. Request access token

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

  • grant_type – Fixed value: authorization_code.
  • code – Code that the app received from the ZorgDomein authorization server.
  • redirect_uri – The same redirect_uri used in the initial authorization request (step 4).

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%2Fmy.smart-app.com%2Foauth%2Fgetaccesstokenforcode

11. Provide access token and ID token

ZorgDomein authorization server responds with a JSON object that contains the following parameters:

  • access_token – The access token issued by the ZorgDomein 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 ZorgDomein FHIR server.
  • scope – Scope of access authorized. Note that this can be different from the scopes requested by the app.
  • id_token – Authenticated user identity and details. See ID token documentation for details.
  • 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 content in ZorgDomein. 
  • http://zorgdomein.nl/terminology/naming-system/zd-number – ZD number of the active transaction in ZorgDomein at launch time.
  • http://zorgdomein.nl/terminology/sso-parameters/callback-uri – URL the app should use when sending the user back to ZorgDomein when closing the app.

Example access token response:

{
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJjbGllbnRfaWQiOiJteXNtYXJ0YXBwaWQiLCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIHBob25lIGxhdW5jaC9wYXRpZW50IGxhdW5jaC96ZG51bWJlciBwYXRpZW50L0FwcG9pbnRtZW50LndyaXRlIG9ubGluZV9hY2Nlc3MiLCJ1c2VyIjoiMWFmMjE2ZTQtNjFjYy00ZmE0LWJhOTMtYzE3MDhhZTVmNmUwIiwicGF0aWVudCI6IjliZTA3NDA4LWUyMDYtNGQ1Zi05YmRjLTcwMjRjMTg3NzY5YiIsInpkbnVtYmVyIjoiWkQxMjM0NTY3OCIsImlhdCI6MTU3MTMyNTg2MywiZXhwIjoxNTcxMzI3NjYzfQ.mdESytoX3Bl-3F1ag86_xe0YVigyaEV7BsvzVhYo9y4",
    "token_type": "Bearer",
    "expires_in": 1800,
    "scope": "openid profile email phone launch/patient patient/Appointment.write online_access",
    "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Inpvcmdkb21laW4tMjAxOTEwMTcxMTMwMDUzMCJ9.eyJpc3MiOiJodHRwczovL3pvcmdkb21laW4ubmwiLCJzdWIiOiIxYWYyMTZlNC02MWNjLTRmYTQtYmE5My1jMTcwOGFlNWY2ZTAiLCJhdWQiOiJteXNtYXJ0YXBwaWQiLCJleHAiOjE1NzEzMjc2NjMsImlhdCI6MTU3MTMyNTg2MywibmFtZSI6IkluZ3JpZCBUZXN0Z2VicnVpa2VyIC0gdmFuIFpvcmdEb21laW4iLCJmYW1pbHlfbmFtZSI6IlRlc3RnZWJydWlrZXIgLSB2YW4gWm9yZ0RvbWVpbiIsImdpdmVuX25hbWUiOiJJbmdyaWQiLCJnZW5kZXIiOiJmZW1hbGUiLCJiaXJ0aGRhdGUiOiIxOTc2LTEwLTE0IiwiZW1haWwiOiJpbmdyaWRAbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYWRkcmVzcyI6eyJmb3JtYXR0ZWQiOiJTdHJhYXR3ZWcgNjhcclxuMzYyMSBCUlxyXG5CcmV1a2VsZW4iLCJzdHJlZXRfYWRkcmVzcyI6IlN0cmFhdHdlZyA2OCIsImxvY2FsaXR5IjoiQnJldWtlbGVuIiwicG9zdGFsX2NvZGUiOiIzNjIxIEJSIn0sInBob25lX251bWJlciI6IjA2MTIzNDU2NzgiLCJwaG9uZV9udW1iZXJfdmVyaWZpZWQiOnRydWV9.KwfKMjvN3VXZr-Cs1gpgfZv6SrVGLp7F0uGdOIUm8Eg",
    "refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJjbGllbnRfaWQiOiJteXNtYXJ0YXBwaWQiLCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIHBob25lIGxhdW5jaC9wYXRpZW50IGxhdW5jaC96ZG51bWJlciBwYXRpZW50L0FwcG9pbnRtZW50LndyaXRlIG9ubGluZV9hY2Nlc3MiLCJ1c2VyIjoiMWFmMjE2ZTQtNjFjYy00ZmE0LWJhOTMtYzE3MDhhZTVmNmUwIiwicGF0aWVudCI6IjliZTA3NDA4LWUyMDYtNGQ1Zi05YmRjLTcwMjRjMTg3NzY5YiIsInpkbnVtYmVyIjoiWkQxMjM0NTY3OCIsImlhdCI6MTU3MTMyNTg2M30.vc5rOoaRWS3wqzTG_UcEG1LGl1MKVHCmpMtAmumnYbA",
    "patient": "9be07408-e206-4d5f-9bdc-7024c187769b",
    "http://zorgdomein.nl/terminology/naming-system/zd-number": "ZD12345678",
    "http://zorgdomein.nl/terminology/sso-parameters/callback-uri": "https://www.zorgdomein.nl/patient/referral/e09abe15-1ef6-40c6-8d8c-bb6816e36fb5/detail"
}

The signature of the ID token must be validated using the public key that ZorgDomein provides. The payload of the ID token should be used to create a user session in the app. The access token may be used to authorize future requests to the ZorgDomein FHIR server. Currently ZorgDomein does not expose any FHIR endoints, but that may change in the future.

12. SSO complete, serve app start page

After all user details have been obtained, a user session can be initiated in the app which completes the app launch. The app start page is sent to the browser.

ID token specifications

The ID token that ZorgDomein provides in the access token response (step 11) is a JWT (JSON web token). This is a token that consists of a header, a payload and a signature. See the OpenID token specifications for detailed documentation.

ID token header

The ID token header contains the following elements:

  • alg – fixed value: RS256. Indicates that tokens are signed using the RSA SHA-256 algorithm.
  • typ – fixed value: JWT. Indicates that this is a JSON web token.
  • kid – id of the key that was used to sign the token. This is issued by ZorgDomein upon registration of the app.

Header example:

{
    "typ": "JWT",
    "alg": "RS256",
    "kid": "zorgdomein-2019101711300530"
}

ID token payload

The payload of the ID token contains the following claims:

  • iss – Fixed value: https://zorgdomein.nl/.
  • sub – Subject identifier. A locally unique and never reassigned identifier within ZorgDomein for the user. The sub value is a case sensitive string.
  • aud – Audience(s) that this ID Token is intended for. It will contain the OAuth 2.0 client_id of the app. This is a single case sensitive string.
  • exp – Expiration time on or after which the ID Token MUST NOT be accepted for processing.
  • iat – Time at which the JWT was issued. Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the date/time.
  • nonce (optional) – String value used to associate an app session with an ID Token, and to mitigate replay attacks. The value is passed through unmodified from the authentication request (step 4/5) to the ID Token. If present in the ID Token, the app MUST verify that the nonce claim value is equal to the value of the nonce parameter sent in the authentication request. The nonce value is a case sensitive string.
  • name (optional) – Full name of the user in displayable form including all name parts, possibly including titles and suffixes.
  • family_name (optional) – Surname(s) or last name(s) of user.
  • gender (optional) – Gender of the user (female or male).
  • birthdate (optional) – Birthday of the user, represented as an YYYY-MM-DD format.
  • email (optional) – Preferred e-mail address of the user.
  • email_verified (optional) – true if the user’s e-mail address has been verified; otherwise false.
  • address (optional) – Preferred postal address of the user. This value is a JSON object containing some or all of the members defined in section 5.1.1 of the OpenID specifications.
  • phone_number (optional) – Preferred telephone number of the user.
  • phone_number_verified (optional) – true if the user’s phone number has been verified; otherwise false.
  • http://zorgdomein.nl/terminology/identity-claims/bsn (optional) - BSN of the user.

Payload example:

{
  "iss": "https://zorgdomein.nl",
  "sub": "1af216e4-61cc-4fa4-ba93-c1708ae5f6e0",
  "aud": "mysmartappid",
  "exp": 1571327663,
  "iat": 1571325863,
  "name": "Ingrid Testgebruiker - van ZorgDomein",
  "family_name": "Testgebruiker - van ZorgDomein",
  "gender": "female",
  "birthdate": "1976-10-14",
  "email": "ingrid@mail.com",
  "email_verified": true,
  "address": {
    "formatted": "Straatweg 68\r\n3621 BR\r\nBreukelen",
    "street_address": "Straatweg 68",
    "locality": "Breukelen",
    "postal_code": "3621 BR"
  },
  "phone_number": "0612345678",
  "phone_number_verified": true,
  "http://zorgdomein.nl/terminology/identity-claims/bsn": "12344321"
}

ID token signature

Following the JWT specification, the signature is generated using the encoded header, the encoded payload, and both the key pair and algorithm specified in the header. This signate must be validated before processing the payload of the ID token and creating a new user session!

Returning to ZorgDomein

Once SSO has succeeded and the user has logged in to the app, the app must provide a button in its user interface that enables the user to navigate back to ZorgDomein. This button must direct the user to the url provided in the http://zorgdomein.nl/terminology/sso-parameters/callback-uri attribute of the access token response.

If the SSO flow is used to enable the user to schedule an appointment in the app, a booked query parameter must be appended to this callback uri, having one of the following values:

  • booked=1 if an appointment was scheduled
  • booked=0 if no appointment was scheduled

App registration

ZorgDomein does not support automated registration of apps. If you are the vendor of an app that you want to register at ZorgDomein, please contact us. When applying for registration of your app, please supply the following details:

  • A fully specified launch URL
  • A fully specified redirect URL

After successful registration we will provide the following details: 

  • The client_id parameter for your app
  • The public key or certificate you can use the validate our ID tokens

Validation criteria

Before a connector with ZorgDomein can be released, ZorgDomein will validate the connector against a set of validation criteria. The functional criteria for this component are listed below.

The app must:

  1. support creating a user session based on the details provided in the ID token
  2. provide a button to the user to return to ZorgDomein.