[begrippenlijst]
The Koppeltaal 2.0 implementation guide provides a guide for engineers, architects and product owners. This guide attempts to explain in what to expect when implementing Koppeltaal 2.0 in a high over fashion.
Depending on your application architecture, you must be aware that Koppeltaal 2.0 is deployed in the context of a domain. In case of a multi-tenant solution, you must be aware of the fact that most of the things you need will be domain specific and the architecture you choose must cater for this. If you are starting your application architecture from scratch you might want to consider a deployment-per-domain strategy and simplify your application architecture. However, such e per-domain deployment strategy might hinder the replication of content between installations.
The aim of Koppeltaal 2.0 is to simplify things and align to standards. The largest change is to switch from a message bus architecture to a single truth FHIR resource service where information is exchanged. The push aspect of the message bus architecture is replaced by a subscription model. The major FHIR change is the switch from FHIR DSTU3 to FHIR R4.
The Koppeltaal 2.0 standard in is core is composed of many other open standards, shown in the table below.
Standard | Use |
---|---|
HTI | Launch of modules |
FHIR R4 | Exchange of information |
FHIR R4 Rest API | API interaction with FHIR resources |
SMART on FHIR app launch | Launch of modules |
SMART on FHIR backend services | Identification of client applications |
OAuth 2.0 Client Credentials Grant | Identification of client applications |
The parts that are custom defined for Koppeltaal 2.0 are:
- The security model and authorization rules
- The combination of HTI and SMART on FHIR app launch
Koppeltaal uses a application level trust model, so applications get a role with a set of permissions that give access to FHIR resources. In Koppeltaal 2.0, the individual user of the system are NOT identified, it is the responsibility of the application to enforce user level restrictions. Applications are assigned roles, Koppeltaal 2.0 uses a form of Role-Based Access Control (RBAC). The trust model is based on a central authority in the Koppeltaal 2.0 domain, the authorization service, to which all access related tasks are delegated to. The authorization service hands out access to the API, validates launches and provides token introspection.
In order to participate in a domain, an application needs to generate a keypair and publish the public part by JWKS. The secret leg of the keypair must be kept secret. If the private part is leaked, others can easily take over your application role in the domain. By making use of JWKS it is make possible to have multiple key pairs active and change key pairs with minimal effort. It is advised to not store and share the private keys, instead to keep the private part in memory and disseminate the public part by JWKS.
The public keys should be published by providing a JWKS URL to the Koppeltaal 2.0 domain. This URL contains a list of public keys identified by Key ID's (kid). These Key ID's must match the kid header of the JWTs that are generated by the application to identify itself. This JWKS URL is a way of publishing the verifications methods of the application. The URL must be public accessible. It is discouraged to use a common or well-known URL path for this URL.
Standards and specifications involved:
Applications in a domain get assigned a role by the domain administrator. This role is built up out of permissions to resources. These permissions are expressed in .. format.
- Resource: linked to a FHIR resource type.
- Action: a set if Create (C), Read (R), Update (U) of Delete (D) actions.
- Scope: one of the following:
- OWN: access your own resources.
- GRANTED: a list of applications
- ALL: all resources. The "ownership" of a resource is defined by the resource-origin extension field. This field refers to the Device of the owning application.
The authorization service plays a central role in the trust infrastructure of Koppeltaal 2.0. The authorization service provided access_tokens to the FHIR Rest API, validates the launch with SMART on FHIR app launch and token introspection.
Standards and specifications involved:
The Koppeltaal 2.0 profile is published in the Koppeltaal 2.0 Simplifier profile.
Resource Type | Profile | Usage |
---|---|---|
Organization | KT_Organization | A healthcare institution, location or department. |
Device | KT_Device | An Application Instance. This resource is created by the Administration Portal when a connection request for an Application in the Domain is approved. At that time, the ClientId is also assigned. This resource cannot be modified by applications. |
ActivityDefinition | KT_ActivityDefinition | Describes an eHealth Module that can be assigned to a patient. |
Patient | KT_Patient | Represents the patient. |
CareTeam | KT_CareTeam | Represents a Care Team. That can be patient specific |
Practitioner | KT_Practitioner | Represents the practitioner. |
Task | KT_Task | A Task is created each time a ActivityDefinition is assigned to a Patient. The task has a status that tracks the lifecycle of the task. |
Endpoint | KT_Endpoint | An endpoint describes the URL for the ActivityDefinition by the KT2EndpointExtension. |
Subscription | KT_Subscription | A subscription to notifications about changes to a particular resource type. |
AuditEvent | KT_AuditEvent | Is created for every relevant event in a Connecting Language Domain, such as the creation or modification of a resource instance, the opening or being opened of an eHealth Module via a launch. |
The Koppeltaal 2.0 Rest API can be accessed with an access_token. This token must be acquired by executing the SMART on FHIR backend services flow. This flow consists of a token request that validates the applications's identity and enforces the configured role for the application. The access_token consists of a JWT that contains the information the FHIR Rest API needs to apply the access rules that are in effect for the application.
Standards and specifications involved:
The SMART on FHIR backend services flow is in the core a OAuth2 client credential flow, that makes use of JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants as client credential method. This means that the application needs to:
- Generate a key pair that is used for signing the JWT.
- Publish the public key in an URL known to the Koppeltaal 2.0 domain.
- Sign a JWT as client credential with a kid header matching at least one key in the JWKS file published at 2).
- The client credential is provided as part of the token request, the field
client_assertion_type
is set tourn:ietf:params:oauth:client-assertion-type:jwt-bearer
and the JWT is provided as `client_assertion.
Standards and specifications involved:
- SMART on FHIR backend services
- JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants
- OAuth 2.0 Client Credentials Grant
Koppeltaal makes use if the FHIR R4 Rest API as much as possible, with exception to the following:
- The update method requires a
If-Match
header. - Conditional creates, conditional updates and conditional deletes MAY be supported by the FHIR resource service.
- Patching and conditional patching is not supported.
- Deletes are discouraged, lifecycle should be managed through status.
- Searching does not support the
_include
,_revinclude
,_contained
and_containedType
parameters.
Standards and specifications involved: FHIR R4 Rest API
The lifecycle of entities are managed by either the active
field or the status
field of the entity.
Resource Type | Profile | Usage |
---|---|---|
Organization |
active | true , The organization's record is in active use. |
false , The organization's record is not in active use. |
||
Device |
status | active , the device is available for use. |
inactive , The device is no longer available for use. |
||
entered-in-error ,The device was entered in error and voided. |
||
unknown , The status of the device has not been determined. |
||
Patient |
active | true , The patients record is in active use. |
false , The patients record is not in active use. |
||
CareTeam |
status | active , The care team is currently participating in the coordination and delivery of care |
inactive , The care team was, but is no longer, participating in the coordination and delivery of care. |
||
proposed , The care team has been drafted and proposed, but not yet participating in the coordination and delivery of patient care. |
||
suspended , The care team is temporarily on hold or suspended and not participating in the coordination and delivery of care. |
||
entered-in-error , The care team should have never existed. |
||
Practitioner |
active | true , The practitioner's record is in active use. |
false , The practitioner's record is not in active use. |
||
Task |
status | draft , The task is not yet ready to be acted upon. |
entered-in-error , The task should never have existed and is retained only because of the possibility it may have used. |
||
ready , The task is ready to be performed, but no action has yet been taken. Used in place of requested/received/accepted/rejected when request assignment and acceptance is a given. |
||
in-progress , The task has been started but is not yet complete. |
||
cancelled , The task was not completed. |
||
on-hold , The task has been started but work has been paused. |
||
failed , The task was attempted but could not be completed due to some error. |
||
completed , The task has been completed. |
||
Endpoint |
status | active , This endpoint is expected to be active and can be used. |
suspended , This endpoint is temporarily unavailable. |
||
error , This endpoint has exceeded connectivity thresholds and is considered in an error state and should no longer be attempted to connect to until corrective action is taken. |
||
off , This endpoint is no longer to be used. |
||
entered-in-error , This instance should not have been part of this patient's medical record. |
||
test , This endpoint is not intended for production usage. |
||
Subscription |
status | requested , The client has requested the subscription, and the server has not yet set it up. |
active , The subscription is active. |
||
error , The server has an error executing the notification. |
||
off , Too many errors have occurred or the subscription has expired. |
||
ActivityDefinition |
status | draft , This resource is still under development and is not yet considered to be ready for normal use. |
active , This resource is ready for normal use. |
||
retired , This resource has been withdrawn or superseded and should no longer be used. |
||
unknown , The authoring system does not know which of the status values currently applies for this resource. Note: This concept is not to be used for "other" - one of the listed statuses is presumed to apply, it's just not known which one. |
||
``, |
The Koppeltaal 2.0 specification discourages the use of deletions in favor of the lifecycle (status and active fields). However, use cases around the right to be forgotten might require the deletion of resources. Therefore the Koppeltaal 2.0 specification does cater for deletions. Some FHIR resources services MAY implement the &expunge operator.
The AuditEvent
entity tracks the audit events in Koppeltaal 2.0 that need to be tracked. Fortunately, most of the events are being managed by the FHIR resource service and security service. As an application developer, most of the burden of creating is thereby taken of your hands. However, in a couple of cases an AuditEvent
needs to be created. These are:
- When initiating a launch
- When receiving a launch
- When fetching an entity from the FHIR resource service that cannot be processed by your application.
The FHIR Rest API makes use of tracing headers in order to be able to link API calls to each other and be able to link API calls to AuditEvent
s. The fields used to trace the requests are:
Header | If missing | Description |
---|---|---|
X-Request-ID |
Generated | Each request needs a unique identifier, generated if missing. |
X-Correlation_ID |
ignored | The X-Request-ID of the parent request, if any. |
X-Trace-id |
ignored or generated | If provided, this field is passed on to child requests without modification. |
In practice, if an application initiates a request to the FHIR Rest API, no headers need to be provided, although it would be neat if the application would set the X-Request-ID
header with an UUIDv4 value. The FHIR resource service will generate a X-Request-ID
header for the request if it not set anyway, and may generate a X-Trace-id
header. If that request initiates a Subscription update request, the update request will be executed with a new X-Request-ID
and the X-Correlation_ID
filled with the (generated) X-Request-ID
and optionally the X-Trace-id
of the original request that caused the subscription update. The receiving application now needs te be aware of the headers as it will initiate a read request to the FHIR resource service to fetch the updated resources. The request will require a new X-Request-ID
and the X-Correlation_ID
needs to be the X-Request-ID
from the subscription update request. If present, the X-Trace-id
needs to be copied 'as is'.
- Generate and securely retain public/private keypairs
- Be able publish the public keys in a JWKS endpoint
- Develop the logic to generate a JWT to identify your application to the auth service.
- Request an access_token to access the API.
- Be able to work with FHIR entities
- Understand the lifecycle of FHIR entities
- Subscriptions & notifications
- Synchronization and errors
- Staring a launch: generating a JWT
- Receiving a launch: execute the SMART on FHIR sequence.