Op basis van gedefinieerde toegangsregels kan al dan niet toegang verleend worden aan een Client Application (applicatie instantie) die daar om verzoekt. Hierbij worden achtereenvolgens de volgende stappen doorlopen:
- Identificatie. Het bepalen en registreren van de identiteit (stappen 1, 1.1 en 2).
- Authenticatie. Het verifiëren van de geclaimde identiteit (stappen 3 en 4).
- Autorisatie. Het toekennen van rechten en het verlenen van een dienst (stappen 5 en 6).
Het Koppeltaal (2.0) profiel is gebaseerd op OAuth2.0. OAuth staat voor Open Autorisatie. Dit is een open protocol, gebouwd op de IETF standaarden en licenties van de Open Web Foundation. Met OAuth2 kunnen gebruikers en/of systemen (de consument) hun gegevens delen met andere partijen (zoals eHealth modules of native apps), terwijl ze hun eigen credentials geheim houden.
Gegevens worden meestal opgeslagen bij een (andere) dienstverlener of bij Koppeltaal in een FHIR (Resource) Provider. OAuth verleent de aanvrager toepassing (client - applicatie instantie) een toegangstoken (access token) te verlenen, op basis van client authenticatie, zie sectie 4.4 van RFC-6749. Elke toegangstoken verleent beperkte toegang tot specifieke informatie voor een bepaalde periode. Authenticatie en autorisatie vereisen het gebruik van de OAuth 2.0 bearer tokens, dat verkregen wordt door het mechanisme dat beschreven wordt in RFC-6749.
Het registreren en vragen tot toegang tot een dienstverlener of bij Koppeltaal de FHIR (Resource) Provider bestaat uit de volgende stappen:
- De client (applicatie-instantie) MOET zich (dynamisch) registreren om voor elke instantie een afzonderlijke en unieke "Client_ID" te verkrijgen (zie stappen 1 en 2). Client registratie KAN worden voltooid door ofwel statische configuratie (out-of-band, via een beheerder, etc.) of dynamisch. De client (applicatie-instantie) MOET een unieke publieke en geheime sleutelpaar genereren, en de jwks_uri (referentie naar de sleutelpaar) of publieke sleutelwaarde registreren bij de Autorisatie Server registreren. Zie RFC-7517.
- Het verkrijgen van een toegangstoken door een token verzoek te sturen naar de Autorisatie Server, die het verzoek van de consument verifieert met behulp van een JWT (JSON Web Token) of identiteitsbewijs (zie stappen 3 en 4)
- Zodra de client (applicatie-instantie) het toegangstoken verkregen heeft kan deze (her)gebruikt worden, tot het toegangstoken verlopen is, voor elke serviceverzoek aan de FHIR Resource Provider (zie stappen 5 en 6). Nadat het toegangstoken verlopen is, moet men opnieuw een toegangstoken opvragen.
De URL's gebruikt in de PoC:
Client (Applicatie)
Autorisatie server
Domeinbeheer
- Applicatie registratie - https://smart-backend-services.koppeltaal.headease.nl/
FHIR Resource Provider
- FHIRStoreEndPoint - https://hapi-fhir-server.koppeltaal.headease.nl/fhir
Autorisatie Server Interface
- Metadata van de Autorisatie Server is via publiek internet benaderbaar. Metadata kan conform RFC-8414 worden opgehaald, op een bekende URL, die herleid kan worden m.b.v. de '
iss
' claim uit het token (zie ook section-3.1 van RFC8414). - De Authorization Server Metadata Response bevat o.a. de volgende attributen:
- issuer;
- token_endpoint;
- token_endpoint_auth_signing_alg_values_supported;
- jwks_uri;
- scopes_supported
Zie ook gerelateerde topics:
CapabilityStatement (eHealth Mogelijkheden) - [review]
TOP-KT-016 - Conformiteit [review]
JWKS
De public key waarmee de digitale handtekening kan worden gecontroleerd wordt conform RFC-7517, als een JWK beschikbaar gesteld. De URL van waarop de JWK Set kan worden opgevraagd (jwks_uri) maakt deel uit van de Autorisatie Server metadata response.
Iedere JSON Web Key (JWK) in de set, die beschikbaar wordt gesteld op de jwks_uri, bevat een 'kid
' parameter. De juiste JWK in de JWK Set wordt gevonden o.b.v. de waarde van het 'kid
' attribuut in de header van de ontvangen JWT.
Een systeem die de signature van een JWT wil valideren mag verkregen JWK's opslaan en hoeft een JWK slechts op te halen wanneer het nog niet beschikt over een JWK met het juiste 'kid
'.
De attribuut definities van JWK kan worden gevonden in RFC-7517 en RFC-7518. De volgende attributen worden nu gebruikt:
- "
kty
":"RSA" (key type - cryptografische algoritme RSA-Rivest, Shamir en Adelman) - "
use
":"sig" (gebruik van de publieke sleutel - signature) - "
kid
":"key identifier" (sleutelpaar om JWT te onderteken) - "
n
": de module waarde om RSA publieke sleutel te genereren (product van 2 priemgetallen) - "
e
": de exponent waarde om RSA publieke sleutel te genereren (wordt gebruikt om uit de ondertekening de oorspronkelijke waarde te decoderen)
{ "keys": [ { "kty": "RSA", "kid":"lhqSXMVFn8AFG2", "use":"sig", "n":"prv1Rj9R2tpdun4IuOulqKerjAX9MEsxMpYvVobh_AbkS", "e":"AQB" }, { "kty": "RSA", "kid":"lhqSXMVFn8AFG3", "use":"sig", "n":"kwa83hj9R2tp6doqIuOulqKeruJa4MEsxMpYvVjee_dh5", "e":"AQB" } ] }
Client Registratie (stap 1, 1.1 en 2)
Voor Koppeltaal is een Proof of Concept gebouwd om een client (applicatie-instantie) te registreren en een client_id te ontvangen. Zie SMART Service Registratie (headease.nl). Hier kan een client een aanvraag indienen en een jwks_uri (jwks endpoint) of een publieke sleutel opvoeren, zodat deze op elk moment door de Autorisatie Server gevalideerd en geverifieert kan worden. Na registratie krijgt de client een uniek client_id toegewezen, de client_id moet door de client beschermt worden en kan vervolgens in het identiteitsbewijs (gesigneerde JWT), bij stap 3, gebruikt worden.
Tijdens de registratie wordt er door Autorisatie Server, voordat de client_id aan de client wordt afgegeven, een Device resource aangemaakt en gepubliceerd bij de FHIR (Resource) Provider die als Device.identifier de client_id krijgt. Zie stap 1.1. De eigenaar en beheerder (resource-origin) van alle geregistreerde Device resources is de Autorisatie Server.
{ "resourceType": "Device", "extension": [ { "url": https://koppeltaal.nl/resource-origin, "valueString": "AutorisatieServer_id" } ], "identifier": [ { "system": https://koppeltaal.nl/client_id, "value": "client_id" } ], "status": "active", "deviceName": [ { "name": "ApplicatieInstantieNaam" } ] }
BELANGRIJK: In bovenstaand voorbeeld willen we kunnen zoeken op Device.resource-origin. In bovenstaand voorbeeld op "AutorisatieServer_id". Indien we naar de (huidige) zoekparameters van Device kijken, is er geen parameter gedefinieerd die deze extensie selecteert. Hiervoor moet een nieuwe searchParameter worden gedefinieerd. Alleen de FHIR (Resource) Provider MOET automatisch een SearchParameter toevoegen voor deze extensie, als deze nog niet bestaat. De extensie meegeven op een POST MOET in een foutmelding resulteren. De extensie mag meegegeven worden op een PUT en mag dan NIET aangepast worden. Als de resource-origin niet aanwezig is in de PUT, dan zal KT deze zelf toevoegen. Indien er een aanpassing op de extensie wordt uitgevoerd resulteert dit in een foutmelding.
Voordat een client of consument informatie wil uitwisselen, MOET deze een asymmetrische sleutelpaar genereren of verkrijgen en MOET de publieke sleutel bij de Autorisatie Server geregistreerd worden, zodat de client zichzelf bij de Autorisatie Server kan authentiseren. We vereisen bij Koppeltaal geen op standaard gebaseerde registratie proces, maar we raden aan om te overwegen het OAuth 2.0 Dynamic Client Registration Protocol te gebruiken.
De client MOET zijn geheime sleutel beschermen tegen ongeoorloofd gebruik en openbaarmaking en is daarvoor verantwoordelijk.
Clients of consumenten die (direct) toegang hebben tot het token endpoint van de Autorisatie Server MOETEN een publieke en geheime sleutelpaar hebben voor gebruik van authenticatie bij het token endpoint. Deze clients MOETEN hun publieke sleutels registreren in de metadata van hun client applicatie registratie door ofwel de publieke sleutel rechtstreeks aan de Autorisatie Server beschikbaar te stellen, ofwel door een jwks_uri te registreren die voor de Autorisatie Server toegankelijk MOET zijn. Het jwks endpoint kan men (meestal) vinden op https://YOUR_DOMAIN/.well-known/jwks.json. Dit eindpunt bevat de JWKs die gebruikt worden om de te ondertekenen JWTs te kunnen valideren.
Het wordt AANBEVOLEN dat clients, indien mogelijk, een jwks_uri gebruiken, omdat dit de sleutelrotatie gemakkelijker maakt. Dit geldt voor zowel dynamische als statische (out-of-band) client registratie.
Het jwks-veld of de inhoud die beschikbaar is vanaf de jwks_uri van een client applicatie MOET een publieke sleutel bevatten, zoals beschreven in 'JSON Web Key Set (JWK Set)' (RFC-7517). De Autorisatie Server MOET de inhoud van het geregistreerde jwks_uri-document van de client valideren en verifiëren dat het een JWK-set bevat.
De volgende paragraaf laat zien hoe men een (RSA) sleutelpaar in PKCS8 tekst formaat met sleutellengte 2048 kan aanmaken, voor het onderteken van JWT en uitwisselen van publieke sleutel. Zie ook: https://github.com/Koppeltaal/Koppeltaal-2.0-Generate-KeyPair.
Het aanmaken van sleutelparen
BELANGRIJK:
- De ondertekening MOET gebruik maken van een asymmetrisch algoritme, en een asymmetrische (RSA) sleutelpaar
- De validerende partij MOET de volgende asymmetrische algoritmen accepteren (zie [RFC-7518] - 3.1):
- RS256 (Aanbevolen), RS384 (Optioneel) en RS512 (Optioneel)
- ES256 (Aanbevolen), ES384 (Optioneel)en ES512 (Optioneel)
- Bij de aanbevolen asymmetrische algoritme MOET een sleutel van 2048 bits of groter worden gebruikt.
Er zijn verschillende manieren om asymmetrische (RSA) sleutelparen aan te maken.
Zie: https://github.com/Koppeltaal/Koppeltaal-2.0-Generate-KeyPair/blob/master/gen_keypair.sh
Volgende voorbeeld wordt een asymmetrische (RSA) sleutelpaar aangemaakt met de OpenSSL tool.
Genereer een RSA geheime sleutel met een lengte van 2048 en sla dit op in een bestand 'key.pem':
Geheime sleutel genereren$ openssl genrsa -out key.pem 2048 Generating RSA private key, 2048 bit long modulus ..........+++ ..........................................................................+++ e is 65537 (0x10001)
Extraheer de publieke sleutel uit het sleutelpaar en sla dit op in 'public.pem':
Publieke sleutel extractie$ openssl rsa -in key.pem -outform PEM -pubout -out public.pem writing RSA key
Wanneer een asymmetrisch (RSA) sleutelpaar wordt gegenereerd,
bewaar de geheime sleutel op een veilige plek (bv een KeyStore - kluis)
op een systeem dat als veilig beschouwd wordt.- Geheime sleutels worden op een PKCS8 (Public-Key Cryptography Standards) beheerd en bewaard. Dit formaat wordt bij alle algoritmes gebruikt en is niet alleen voor RSA sleutels bedoeld. PKCS8 is de standaard om geheime sleutel gegevens op te slaan en te beheren.
Converteer RSA geheime sleutel naar PKCS8 in tekst formaat en sla dit op in 'pk8key.pem':
Geheime sleutel naar PKCS8 converteren$ openssl pkcs8 -in key.pem -topk8 -nocrypt -out pk8key.pem
Voor verdere detail specificaties over PKCS8 verwijzen we naar RFC-5208.
Het aanmaken van een JSON Web Signature (JWS - client identiteitsbewijs - voorwerk voor stap 3)
Met een JSON Web Signature (JWS - client identiteitsbewijs) kan men een toegangstoken opvragen bij de Autorisatie Server. De client MOET in staat zijn om een JWS te genereren, zoals beschreven in RFC-7515. Voor de PoC in Koppeltaal 2.0 ondersteunt de client RS512 voor de JSON Web Algoritme (JWA) header parameter, zoals vastgelegd in RFC-7518. De Autorisatie Server moet in staat zijn, voor de PoC, de signature (handtekening) te valideren met RS512.
De JWS bevat de volgende claims en MOET worden ondertekend met de geheime sleutel van de client. Zie https://jwt.io voor een praktische referentie over JWT(S), evenals fout opsporingstools en cliënt bibliotheken.
Authenticatie JWT Header waarden | ||
---|---|---|
alg | vereist | RS512 |
typ | vereist | Vaste waarde: JWT . |
kid | optioneel | Hier wordt aangegeven welke sleutel is gebruikt om de JWS te beveiligen. Met deze parameter kan de initiator expliciet een sleutelwijziging signaleren naar de ontvanger. |
Authenticatie JWT Claims | ||
---|---|---|
iss | vereist | Issuer of uitgever van de JWT - de client_id van de client, zoals bepaald tijdens de registratie bij de Autorisatie Server. |
sub | vereist | De (technische) identifier van de gebruiker. Bij een applicatie is dit de |
aud | vereist | De Autorisatie Server "token URL" (De zelfde URL waar de JWT naar gestuurd wordt, zie volgend voorbeeld). |
iat | vereist | Tijdstempel voor wanneer de JWT is aangemaakt. |
exp | vereist | Verlooptijdstip voor verificatie van de JWT, uitgedrukt in seconden sinds het "tijdvak" (1970-01-01T00:00:00Z UTC). Deze tijd MAG in de toekomst niet meer dan vijf minuten duren. |
jti | vereist | Een nonce-tekenreekswaarde die deze verificatie-JWT uniek identificeert. |
// Authenticatie JWT header - Staat in met welk algoritme dit token ondertekend wordt { "kid": "enU0JAQq6YJScTKIJ4NN5g", "alg": "RS512", "typ": "JWT" } // Payload - Bevat een aantal beweringen (authenticatie JWT claims - standaard velden) { "aud": "https://authentication-service.koppeltaal.headease.nl/oauth2/token", "iss": "b11360ba-4b03-41e1-ab74-c2871804c87c", "sub": "b11360ba-4b03-41e1-ab74-c2871804c87c", "iat": 1623835733, "exp": 1623836033, "jti": "b587ff6c-61ae-4c1c-b924-f030523a5c63" } // Signature - digitale handtekening waarmee men het JSON Web token valideert. In dit voorbeeld RSA handtekening met RSASHA512( base64UrlEncode(header) + "." + base64UrlEncode(payload) + "." + base64UrlEncode(signature) )
Voor Koppeltaal is er een testsuite beschikbaar waar een JWS gegenereerd wordt a.d.h.v. bovenstaande parameters, zie Koppeltaal 2.0 SMART Backend Service Test Suite (headease.nl).
Hier moet men de geheime sleutel als PKCS8 formaat opvoeren. De geheime sleutel wordt NIET bij de testsuite opgeslagen of bewaard.
Toegangstoken opvragen (stap 3 en 4)
Indien de client een JWS heeft gegeneerd, zoals beschreven on voorgaande, kan de client m.b.v. de aangemaakte JWS een toegangstoken aanvragen, door de aanvraag te posten naar het tokenEndpoint van de Autorisatie Server (zie het veld "aud" in de JWT claim), met behulp van content-type application/ x-www-form-urlencoded met de volgende parameters:
Parameters | ||
---|---|---|
scope | optioneel | De scoop of omvang van de gevraagde toegang. Zie opmerkingen over de "scope"verderop. |
grant_type | vereist | Vaste waarde: client_credentials. |
client_assertion_type | vereist | Vaste waarde:urn:ietf:params:oauth:client-assertion-type:jwt-bearer. |
client_assertion | vereist | De JWS bestaat uit de drie Base64-URL-reeksen: header, payload en signature, gescheiden door punten. |
Stap 3
POST https://authentication-service.koppeltaal.headease.nl/oauth2/token Accept: application/json Content-Type: application/x-www-form-urlencoded grant_type=client_credentials &scope=system%2FActivityDefinition.* &client_assertion=eyJn0.MdIBDor2Bsq5ge51EkWajzZ7AL.isVp-bskzUsqUf77ejqX_CBAqkNdH1Zebn93 <fictief encoded JWS voorbeeld> &client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
- De Autorisatie Server antwoordt op de volgende manier en retourneert een toegangstoken voor de client
- De toegangstoken is een JSON Web Token(!) en kan men decoderen
Stap 4
{ "access_token":"eyJraWQiOiJ1N.eyJhenDdiJteS1j.qBPcaQRD6qAGFEqipmfBA", <fictief encoded JWS voorbeeld> "token_type":"Bearer", "expires_in":3600, "refresh_token":"e0c248e-30ee-4667-8eb4-4cfebe15a351", "scope":"system/ActivityDefinition.*" }
Scope
De scope bestaat uit de volgende onderdelen:
- Resource context. Een (gebruikers) context waarin rechten worden aangevraagd: user (een geauthentiseerde gebruiker), patiënt (beperkt tot een geïdentificeerde patiënt) of system (geauthentiseerd systeem)
- Resource type. Een FHIR resource type, die bij naam benoemd wordt
- Rechten. Rechten die met lezen (ook historische gegevens) en/of schrijven (ook het verwijderen van gegevens) te maken hebben op een instantie van een resource . '*' is lezen en schrijven.
Kanttekening: De scope wordt nu niet in de PoC gebruikt.
Service (interactie met FHIR Resource Provider) (stap 5 en 6)
Merk op dat bij een RESTful-framework alle interacties rechtstreeks op de FHIR Resource Provider worden uitgevoerd met behulp van het HTTP protocol (POST, PUT, GET en DELETE). De API (interface) heeft geen directe betrekking op authenticatie, autorisatie en controle. Hiervoor gebruiken we het toegangstoken (access_token) die we in de autorisatie (Authorization
) header opnemen bij elk service verzoekbericht (interactie), zie stap 5. Het service verzoekbericht kan goedgekeurd worden en dan krijgt de client informatie terug over het verzoek of het verzoek kan afgewezen worden, en dan krijgt de client informatie terug waarom het is afgewezen.
Toegangstoken management en refresh tokens
Zoals ook in het "AccessToken" voorbeeld al gebruikt wordt, wordt aanbevolen om het toegangstoken (access_token) 3600 seconden, oftewel één uur te laten gelden (zie ook expires_in
). Dit betekent dat na één uur het toegangstoken NIET meer gebruikt kan worden, voor een service verzoekbericht. In OAuth2 MAG de client het toegangstoken zelf NIET vernieuwen. Daarom MOET, zodra de client de melding krijgt dat het toegangstoken verlopen is (Token Expired Error), een nieuw toegangstoken aanvragen. Hiervoor kan de client, de "Client Credential Flow" opnieuw doorlopen of, als de client in bezit is van een refresh_token die bij het verlopen toegangstoken hoort, een nieuw refresh_token verzoek indienen, om zo een nieuw toegangstoken en een refresh token te verkrijgen.
Omdat een refresh_token een langere levensduur heeft, wordt de refresh_token aan de aanvrager (client) gekoppeld. Ook een refresh_token kan op een bepaald moment niet meer valide (bruikbaar) zijn, omdat:
- de autorisatie server het refresh_token kan hebben ingetrokken
- het refresh_token verlopen is
- de beleidsregels omtrent de toegang tot de resources zijn gewijzigd
Refresh token verzoek
POST https://authentication-service.koppeltaal.headease.nl/oauth2/token Accept: application/json Content-Type: application/x-www-form-urlencoded grant_type=refresh_token &scope=* <optioneel> &refresh_token=e0c248e-30ee-4667-8eb4-4cfebe15a351 <fictief refresh token voorbeeld>
Response
{ "access_token":"eyJrbWQiOiK1N.eyJhebDdiDteS2j.qBPcbQRD6eAGFEqilmCB", <fictief encoded JWS voorbeeld> "token_type":"Bearer", "expires_in":3600, "refresh_token":"e0c248e-21de-5267-8eb4-4cfebe16b385" }
Het (nieuwe) toegangstoken, kan men weer voor een bepaalde tijd gebruiken, en zodra dit token verlopen is, MOET men weer opnieuw een toegangstoken aanvragen. met de daarbij behorende refresh token.