Token-gebaseerde diensten

Inleiding

Het aanbod van MAGDA werd begin 2020 uitgebreid met REST diensten. Voor deze REST diensten wordt gebruik gemaakt van een token-gebaseerd authenticatie systeem.
Token-gebaseerde authenticatie laat toe om authenticatie informatie naadloos te laten doorstromen van het ene systeem naar een andere. Ook multi-partijen scenario’s kunnen hiermee gemakkelijker ondersteund worden. Bijvoorbeeld een scenario met een resource server (MAGDA) die informatie (of resources) ter beschikking stelt, een opdrachtgever (de entiteit die de recht heeft om de informatie te raadplegen) en een leverancier (de entiteit die de opvraging uitvoert in naam van de opdrachtgever).

Definities

Een token is een stuk informatie dat kan gebruikt worden om een identiteit en/of een recht te representeren.
Tokens kunnen ofwel onleesbaar (opaque) of leesbaar zijn.
Opaque tokens zijn een niet interpreteerbaar, ruw stuk informatie. Om ze te gebruiken is er altijd een derde partij (meestal de autorisatie provider) nodig om te weten als het token geldig is of aan welke toegang of identiteit het token is gelinkt.
Leesbare tokens zijn tokens die informatie bevatten over identiteit en/of autorisatie. JSON Web Tokens (JWTs) zijn leesbare tokens.

Context

MAGDA maakt gebruik van de AIV Authenticatie en Autorisatie Server (AAS) voor het authentiseren en autoriseren van afnemers in het geval van token authenticatie.

Werken met tokens omvat twee fases:

  1. Het krijgen van een access token van een authenticatie en autorisatie server

  2. Het gebruiken van een access token om een oproep naar een resource server te doen (bv een MAGDA dienst)

De access tokens die uitgekeerd worden door de AIV AAS zijn opaque tokens. Tijdens het proces om ze te krijgen wordt er gebruik gemaakt van een JWT (aanvraag token).

 

Stappen 1 en 2 maken deel uit fase 1: het krijgen van een access token.

Stappen 3 en 4 maken deel uit fase 2: het opvragen van MAGDA resource / diensten

Magda is ook de “resource server”.

Fase 0: Opzet bij de Token provider van de Vlaamse Overheid (AIV AAS)

Alvorens er gestart kan worden, dient de afnemer opgezet zijn bij de token provider van de Vlaamse Overheid (AIV AAS).

Wanneer dat gebeurd is, ontvangt de gemandateerde gebruiker het recht om de publieke sleutels te beheren bij AIV AAS.

Hij of zij dient dan de publieke sleutel van het certificaat dat gebruikt zal worden, 1-malig op te laden (via de user interface van AIV AAS).

Bijkomende informatie over hoe dat dient te geburen kan u vinden op: https://beta.oauth.vlaanderen.be/authorization/Help/Api/ClientCredentialsGrant

Fase 1: Het krijgen van een access token

Authenticatie en autorisatie aanvraag aanmaken

De afnemer stuurt een request met 1-Way SSL naar de AIV AAS om een access token te krijgen.

Deze request moet een aanvraag token bevatten. Dit aanvraag token is een JWT token. Dit procedure volgt de RFC 7523: “JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants”.

Dit token wordt getekend door de private key die overeenstemt met het VO DCB certificaat met een door MAGDA erkende Common Name. Deze Common Name krijgt de afnemer van MAGDA tijdens het aansluitingsproces.

Er zijn twee AA servers, een voor TNI en een andere voor productie:

JWT token aanmaken

Het JWT aanvraag token moet het volgende bevatten:

“iss” issuer eigenschap

de client id nummer die aan de afnemer gecommuniceerd is na de accounts aangemaakt zijn

“sub” subject eigenschap

moet ook met de client id ingevuld worden

“aud” eigenschap

audience van de request, moet altijd de URI van het token endpoint zijn, zie hierboven gemeeld lijst.

“exp” expiry eigenschap

het moment vanaf wanneer het token niet meer geldig zal zijn

“iat” issued at eigenschap

het moment waarop het token is aangemaakt

“jti” token id

een uniek nummer voor de aanvraag. (om “replay attacks” voort te komen)

Voorbeeld:

1 2 3 4 5 6 7 8 { "iss": "3318", "sub": "3318", "aud": "https://beta.oauth.vlaanderen.be/authorization/ws/oauth/v2/token", "exp": "1587979731", "iat": "1587979431", "jti": "19017aeb-3673-4f04-8ef7-e0756b80a667" }

Tekenen van het JWT token

Het JWT aanvraag token voor de access token aanvraag moet getekend zijn met de private key die overeen stemt met het VO DCB certificaat. Tekenen van JWT volgt de JWS standard (RFC 7515).

De AIV AAS ondersteunt de “x5c”, “x5u”, “jwk”, “jku” eigenschappen van JWS niet. Dit betekent dat het certificaat dat gebruikt wordt om te tekenen, niet bij het token mag zitten. De public key van het certificaat moet op voorhand opgeladen worden door de afnemer in Geosecure. Dit kan via de volgende link: https://beta.oauth.vlaanderen.be/admin/ (voor TNI) en https://oauth.vlaanderen.be/admin (voor Productie). Hier kan je bij de desbetreffende OAuth client via de link 'Client certificaat toevoegen' de publieke sleutel van je VO-DCB certificaat opladen.

De getekende JWT moet in de “client_assertion”.

Aanvraag HTTP request

De request moet een HTTP POST request zijn met x-www-form-urlencoded parameters. De volgende parameters moeten ingevuld worden:

  • “grant_type”: “client_credentials”

  • “scope”: moet ingevuld worden met een lijst van scopes waaraan het access token toegang gaat geven. Er wordt een scope per dienst gebruikt. Dus voor elke MAGDA dienst die men wil aanroepen, moet hier de nodige scope aangevraagd worden. “scopes” worden door middel van een spatie van elkaar afgescheiden.

  • “client_assertion_type”: “urn:ietf:params:oauth:client-assertion-type:jwt-bearer”

  • “client_assertion”: het JWT token beschreven in het vorige punt

Voorbeeld:

1 2 3 4 5 6 7 8 9 10 11 POST https://beta.oauth.vlaanderen.be/authorization/ws/oauth/v2/token Content-Type: application/x-www-form-urlencoded User-Agent: PostmanRuntime/7.22.0 Accept: */* Cache-Control: no-cache Host: beta.oauth.vlaanderen.be Accept-Encoding: gzip, deflate, br Content-Length: 855 Connection: keep-alive grant_type=client_credentials&scope=msg_statuses_v1_G%20msg_mailbox_v1_P%20msg_msg_v1_P&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIzMzE4Iiwic3ViIjoiMzMxOCIsImF1ZCI6Imh0dHBzOi8vYmV0YS5vYXV0aC52bGFhbmRlcmVuLmJlL2F1dGhvcml6YXRpb24vd3Mvb2F1dGgvdjIvdG9rZW4iLCJleHAiOiIxNTk4NTMyOTQzIiwiaWF0IjoiMTU5ODUzMjY0MyIsImp0aSI6IjI2MWQ4NmQ4LTVhY2MtNGNiNS1hMGZkLWEyYTNkZDJmY2I5NCJ9.W5dEZa5RDZ1U250Sm65qkvd5bW6O1ZeZWW8W_RjfKcNg0hSAXOXdJKP8TXnFlqpWswhb8OGYMDS-6YcSVpyrV6roD3FyioPqpaxFkFe1xzanoF6KZTMYA4TTkYROMZkG1U166ZQf7y09S5CekeNzfGF9ORk1toEiROksVZa-inTe46ioJSdVzUf8t-IRXPWR-ucHO9A8IXEQ-dwCrzJ_f1dq24R8eZsiZmplx_nXtfQ9yXD3XPDu-NZiSLY0TKexqPdd4uvlrdzoFe6O8usMkhpBfsC3inUCzMfKT30rk1uJvvKH2s0iYpUp-FZCCn-0unWATwivWhMkvBEVZOWbGA

Antwoord van de Authenticatie en Autorisatie Server (AAS)

Als de authenticatie aanvraag correct verlopen is, krijgt de aanvrager een JSON payload terug met het access token.

1 2 3 4 5 6 { "access_token": "POtyWHuQd94pk6AtHbew3B==", "scope": "msg_statuses_v1_G msg_mailbox_v1_P msg_msg_v1_P", "expires_in": 57599, "token_type": "Bearer" }

De eigenschap “access_token” bevat het token voor de call naar elke MAGDA REST dienst (resource).

Access tokens worden uitgereikt met een bepaalde levensduur. Die levensduur wordt meegeven in het “expires_in” veld (uitgedrukt in seconden). Momenteel bedraagt die 16 uur, maar het staat onze token provider vrij dit interval eenzijdig aan te passen.

We raden aan om een token maximaal te hergebruiken en pas kort voor het einde van haar levensduur, een nieuw exemplaar aan te vragen. Dit verlaagt immers de load naar onze token provider. Bovendien vermijdt u op die manier dat onze token provider u geen tokens meer zal toekennen (momenteel ligt die grens op maximaal 2000 token bevragingen per uur per afnemer).

Wanneer u een token gebruikt waarvan de levensduur verstreken is, zal u van MAGDA een 401 UNAUTHORIZED fout terug krijgen. U zal dan een nieuw token moeten aanvragen om verdere requests naar MAGDA te kunnen uitvoeren.

Code voorbeeld

In de volgende voorbeelden wordt er van Apache Http Components (HttpClient) gebruik gemaakt om de HTTP Call uit te voeren en van jose4j voor het aanmaken van de JWT. Andere libraries zullen op een gelijkaardige manier werken.

Voor C# verwijzen we naar deze https://vlaamseoverheid.atlassian.net/wiki/spaces/GEO/pages/1915388760 .

Het bouwen van de getekend JWT met jose4j:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private static String buildClientAssertion(String clientId, PrivateKey key) throws JoseException { JwtClaims claims = new JwtClaims(); claims.setIssuer(clientId); claims.setAudience(AIP_AUDIENCE); claims.setExpirationTimeMinutesInTheFuture(2.0f); claims.setIssuedAtToNow(); claims.setGeneratedJwtId(); claims.setSubject(clientId); JsonWebSignature signature = new JsonWebSignature(); signature.setPayload(claims.toJson()); signature.setKey(key); signature.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); signature.setContentTypeHeaderValue("JWT"); return signature.getCompactSerialization(); }

Voor het aanmaken van een HttpClient http request:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private static HttpPost buildAccessTokenRequest(String scopes, String signedJWT) throws UnsupportedEncodingException { HttpPost post = new HttpPost(AIP_TOKEN_PROVIDER); ArrayList<BasicNameValuePair> retVal = new ArrayList<BasicNameValuePair>(); retVal.add(new BasicNameValuePair("grant_type", "client_credentials")); retVal.add(new BasicNameValuePair("scope", scopes)); retVal.add(new BasicNameValuePair("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); retVal.add(new BasicNameValuePair("client_assertion", signedJWT)); post.setEntity(new UrlEncodedFormEntity(retVal)); post.setHeader("Accept", "application/json"); return post; }

Daarna kan de request verstuurd worden:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public static String getAccessToken(PrivateKey authenticationKey, String scopes[]) throws JsonParseException, JsonMappingException, UnsupportedOperationException, IOException, JoseException { HttpClient c = HttpClientBuilder.create().build(); String scopeStr = String.join(" ", scopes); String signedJWT = buildClientAssertion("3318", authenticationKey); HttpPost target = buildAccessTokenRequest(scopeStr, signedJWT); HttpResponse resp = c.execute(target); assertEquals(200, resp.getStatusLine().getStatusCode()); ObjectMapper mapper = new ObjectMapper(); LiveTest.AccessTokenResponse token = mapper.readValue(resp.getEntity().getContent(), LiveTest.AccessTokenResponse.class); return token.access_token; }

Als het request correct is, antwoord de AAS met een antwoord zoals beschreven in 4.2

Fase 2: Het opvragen van Magda resource

Authenticatie

Voor elke MAGDA resource “request” is een geldig access token nodig, dat de afnemer in Fase 1 verkregen heeft.

Het access token wordt meegegeven in de “Authorization” HTTP header. De token wordt voorafgegaan door de prefix “Bearer “.

1 2 POST https://.........................../api/v1/messages/messages Authorization: Bearer POtyWHuQd94pk6AtHbew3B==

Maximal Load

Om een maximale dienstverlening te kunnen garanderen gebruikt Magda throttling. Bij een overtreding tegen 1 van de 4 onderstaande regels: zal MAGDA dan ook een 429 response geven:

  • Maximum calls per domein: 18000 transacties per minuut

  • Maximum calls per dienst: 2400 transacties per minuut

  • Maximum calls per afnemer: 1800 transacties per minuut

  • Maximum calls per afnemer per dienst: 1800 transacties per minuut

Antwoord signing

MAGDA tekent alle haar antwoorden met het eigen MAGDA certificaat.

Het antwoord van MAGDA wordt met het MAGDA certificaat getekend. Bijvoorbeeld: keyId=”magda-response-signing-key”.