API Designrules Extensions (Nederlandse API Strategie IIb)

Geonovum Handreiking
Consultatieversie

This version:
https://docs.geostandaarden.nl/api/cv-hr-API-Strategie-ext-20200117/
Latest published version:
https://docs.geostandaarden.nl/api/API-Strategie-ext/
Vorige versie:
https://docs.geostandaarden.nl/api/vv-hr-API-Strategie-ext-20190715/
Latest editor's draft:
https://geonovum.github.io/KP-APIs/API-strategie-extensies/
Editors:
Jasper Roes, Het Kadaster
Linda van den Brink, Geonovum
Author:
Participate:
GitHub geonovum/KP-APIs
File a bug
Commit history
Pull requests
Rechtenbeleid:

Abstract

The API strategy consists of a core — a generic set of rules for all government APIs — and various extensions that only pertain to a specific application or that are not maure enough for the core set.

These extensions are described in this docuement. It is indicated whether an extension is stable or in development.

Status of This Document

This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current Geonovum publications and the latest revision of this document can be found via https://www.geonovum.nl/geo-standaarden/alle-standaarden(in Dutch).

Dit is een door de werkgroep goedgekeurde consultatieversie. Commentaar over dit document kan gestuurd worden naar geo-standaarden@geonovum.nl.

Ten opzichte van de vorige versie van de API strategie (15-7-2019) is de status terug gezet naar consultatie versie. Dit om beter weer te geven dat nog actief aan de API designrules extensions gewerkt wordt. De huidige versie bevat dan ook een aantal inhoudelijke veranderingen ten opzichte van de versie van 15-7-2019. De sectie API security is behoorlijk aangepast en er is een sectie Signing & Encryption bijgekomen

1. Introduction

Dit onderdeel is niet normatief.

This document contains the extensions on the API Designrules standard.

1.1 Reading Guide

This document is part of the "Nederlandse API Strategie"

Part Description Status Link
I General description of the API Strategy Informative https://docs.geostandaarden.nl/api/API-Strategie/
IIa Standard for designing APIs Normative https://docs.geostandaarden.nl/api/API-Designrules/
IIb Extension on the Standard for designing APIs Informative https://docs.geostandaarden.nl/api/API-Strategie-ext/

2. Example: a stable extension

Dit onderdeel is niet normatief.

Note

The working group has indicated this extension to be stable.

Text of the extension...

3. Example: an extension that is still under development

Dit onderdeel is niet normatief.

Warning

This extension is still under development and can be modified at any time.

Text of the extension...

4. API Security

Dit onderdeel is niet normatief.

Warning

This extension is in development and may be modified at any time.

APIs can be accessed from any location on the internet. Information is only exchanged over TLS-based encrypted connections. No exceptions, so everywhere and always. One should follow the HTTP Strict Transport Security (HSTS) policy that mandates the use of HTTPS only.

API principle: Encrypt connections using at least TLS v1.3

Should we not just use NCSC guidelines for HTTPS? https://www.ncsc.nl/documenten/publicaties/2019/mei/01/ict-beveiligingsrichtlijnen-voor-transport-layer-security-tls

4.1 Identification

For Identification of individual users always use a pseudonym to avoid exposing sensitive information about a user. This pseudonym can optionally be translatable to actual personal information in a separate service, but access to this service should be tightly controlled and limited only to cases where there is a legal need to use this information. Use of a Burgerservice nummer(BSN) is only allowed when the organization has the right to do this. Even when an orgnization has the right to do this it is still reccomended to use a pseudonym that is only translatable to a BSN for a limited number of services/users within the organization. ** link naar DSO ontwerp?**

For identifying government organizations use the "organisatie-identificatienummer" (OIN) For identifying non-government organizations (companies, associations, foundations etc...) use the Handelsregisternummer (HRN) These are used in the PKIOverheid and e-Herkenning context. See https://www.logius.nl/diensten/oin for more information on these identifiers. OINs can be queried using the COR API https://portaal.digikoppeling.nl/registers/corApi/index HRNs are derived from the RSIN which can be queried in the "Handels register" https://developers.kvk.nl/documentation/search-v2

In the EU context use the eIDAS legal identifier. for more information see https://ec.europa.eu/digital-single-market/en/trust-services-and-eid

4.2 Authentication

We distinguish end user authentication methods and system authentication methods.

4.2.1 End user authentication

The following authentication methods can be used end user authentication: Out of band When distributing API tokens to users an out of band authentication method can be used. Common methods include an API store where a user logs in and is able to acquire an API key. In this case the login method is the out-of-band authentication method used for accesing an API.

Openidconnect A Dutch profile for OpenIDConnect is currently being drafted. The first version will be for web usecases, later on it will be extended to cover mobile. When this profile is complete it is expected its use will be mandated by the "pas toe of leg uit lijst" of Forum standaardisatie.

SAML The underlying current Authentication method for DigiD & eHerkenning. It can be the basis as the out-of band method mentioned earlier. other usecases are.... (further elaboration needed)

4.2.2 System authentication

The following authentication methods can be used for system authentication:

PKIOverheid These are x509 certificates derived from a root certificate owned by the Dutch Government. for more information on PKIOverheid see https://www.logius.nl/diensten/pkioverheid In the API context use only server or services certificates that include an OIN/HRN for identification. Extended validation certificates (as used for websites) do not include this identifier and are therefor not suitable for use with APIs.

Out of band When distributing API tokens to users an out of band authentication method can be used. Common methods include an API store where a user logs in and is able to acquire an API key. In this case the login method is the out-of-band authentication method used for accesing an API.

OAuth This is a standard for authorisation not authentication yet in some cases its use for machine to machine authentication can be appropriate. The client credentials authorization grant to be specific ...... (further elaboration needed)

4.3 Authorisation

A RESTful API should not maintain the state at the server. The authentication and authorisation of a request cannot depend on cookies or sessions. Instead, a token has to be sent for each request. Token based authorization is reccomended.

API principle: Accept tokens as HTTP headers only

Using tokens a distinction is made between authorised and non-authorised services and related headers:

Authorised Authorization: Bearer <token>
Non-authorised X-Api-Key: <api-key>

In case the proper headers are not sent, then there are no authentication details available and the a status error code 403 Forbidden is returned.

API principle: Use OAuth 2.0 for authorisation with rights delegation

See also The Dutch profile OAuth in the chapter Security for further explanation of the applicaton of OAuth.

API principle: Use PKIoverheid certificates for access-restricted or purpose-limited API authentication

4.3.1 Authorisation errors

In a production environment as little information as possible is to be disclosed. Apply the following rules for returned the status error code 401 Unauthorized, 403 Forbidden, and 404 Not Found:

Does the resource exist? Can authorisaton be determined? Authorised? HTTP statuscode
Yes Yes Yes 20x (200 OK)
Yes Yes No 401 Unauthorized
Yes No ? 403 Forbidden
No Yes Yes 404 Not Found
No Yes No 403 Forbidden
No No ? 403 Forbidden

First, it is established whether the requester (principal) has a valid authorisation(i.e. token is valid) then it is established whether this authorisation is valid for a requested resource. In case the requester is not authorised or the authorisation cannot be established, for example, the resource is required to establish authorisation and the resource does not exist, then a status error code 403 Forbidden is returned. In this way, no information is returned about the existence of a resource to a non-authorised principal.

An additional advantage of the stategy that establishes whether there is authorisation is the opportunity to separate access control logic from business logic.

4.3.2 Public identifiers

Publicly visible identifiers (IDs), that are frequently part of URLs a RESTful APIs shouldn't expose the underlying mechanisms (like number generations) and should certainly not have business logic.

UUID

Preferrably use UUIDs (Universally-Unique IDentifier) for confidential resources. This is a 16-bytes (128-bits) binary representation, a sequence of 32 hexadecimal digits, in five groups separated by hyphens and consists of 36 characters (32 alphanumerical characters and 4 hyphens]):

550e8400-e29b-41d4-a716-446655440000

To ensure UUIDs are shorter and guaranteed web-safe, be advised to only use the base64-encoded variant consisting of 22 characters. The above UUID looks like this:

abcdEFh4520juieUKHWgJQ

4.4 security for webbrowser API clients

When webbrowsers can be clients for an API these APIs should be compatible with the following policies and standards.

4.4.1 CORS-policy

Web browsers implement a so-called "same origin policy", an important security conect to prevent requests go to another domain than where they are provided. While this policy is effective to prevent requests in different domains, it prevents ligitimate interaction between an API and clients from a known and trusted origin.

API principle: Use CORS to control access

4.4.2 CSP-policy

Content Security Policy is a standard that allows API (and website) providers to define approved origins of contents that are accesable through an API but are not provided from the same origin as the API itself. When multiple origins are needed this standard can be use as a mechanism to explicitly provide exceptions to the CORS-policy.

4.4.3 Subresource integrity

Subresource integrity (SRI) is a standard that can be used to validate content delivered by a third party. It defines trusted location and a hash of external content. This allows a client to verify the integrity and trustworthyness of external content accessed through an API.

5. Versionering

Dit onderdeel is niet normatief.

Warning

This extension is still in development and can be modified at any time.

5.1 Deprication of a major API version

Major API releases should always be backward incompatible. In case a new API release does not result in backward incompatibility, it does not warrant a full version increase and it is merely a minor release. In case a major release takes place, all (potential) clients have to implement the new version.

As clients cannot be broken, a migration from the previous to the new release cannot happen abruptly. After the launch the new version, the previous version has to be available in production as well. As not to maintain the previous version indefinitely and to encourage clients to use the new version, a transition period is communicated for users to adjust their code to the new version. This period is referred to as deprecation period. The duration of this period may very per API. Typically, the deprecation period lasts 6 months, but not more than one year. From a maintenance perspective, at most two major versions (one of which is the deprecated version) should be available in production concurrently. Communication with users is crucial during this period. The following points have to be communicated:

These should be communicated using the following channels:

A step-by-step approach:

  1. Launch new version;
  2. Determine deprecation period;
  3. Write migration plan;
  4. Communicate in the API documentation of the old version;
  5. Communicate per e-mail, Web forum and other channels;
  6. Add Warning response header to the old version;
  7. Check logs to monitor the usage of the old version during the deprecation period;
  8. Shut down end-point of the old version at the planned date and monitor feedback;
  9. No feeedback related to the old version within two weeks, remove the old version (including docs);

5.2 The Warning response header

The Warning reponse header (see: RFC 7234) has the status code 299 ("Miscellaneous Persistent Warning") and the API endpoint (including version number) as the warn-agent of the warning, followed by the warn-text with the human-readable warning. For example:

Waarschuwing: 299 https://service.../api/.../v1 "Deze versie van de API is verouderd en zal uit dienst worden genomen op 2020-02-01. Raadpleeg voor meer informatie hier de documentatie: https://omgevingswet.../api/.../v1".

Users should have sufficient time to phase out the old API. A period of 6 to 12 months is recommended.

API principle: Inform users of a deprecated API actively

6. JSON

Dit onderdeel is niet normatief.

Warning

This extension is still in development and can be modified at any time.

JavaScript Object Notation (JSON) is a format, just like XML, to serialise, store and transfer data. JSON is the primary representation format for APIs. For more information about JSON, read https://json.org. In contrast to XML, JSON has a compact notation, for example:

{
"persoon": {
  "naam": "Jan",
  "geboortejaar": 1983
}
}

API principle: JSON first - APIs receive and send JSON

API principle: APIs may provide a JSON Schema

API principle: Support content negotiation

API principle: Check the Content-Type header settings

6.1 Field names in snake_case, camelCase, UpperCamelCase, or kebab-case?

Use camelCase for field names.

API principe: Define field names in in camelCase

6.2 Pretty print

Most REST clients and browsers (whether or not using extensions) can display JSON nicely formatted, even if the response does not include white-space.

API principle: Disable pretty print

6.3 Do not use envelope

Many APIs encapsulate responses in envelopes like below:

{
"persoon": {
  "naam": "Jan",
  "geboortejaar": 1983
}
}

A future-proof API does not have envelopes.

API principle: Send a JSON-response without enclosing envelope

6.4 JSON-encoded POST, PUT, and PATCH payloads

APIs should at least support JSON-encoded POST, PUT, and PATCH payloads. Encoded form data (application/x-www-form-urlencoded) payloads are not supported. What is the difference?

Content-Type: application/json results in:

{
"Name": "John Smith",
"Age": 23
}

and Content-Type: application/x-www-form-urlencoded results in: Name=John+Smith&Age=23

API principle: Support JSON-encoded POST, PUT, and PATCH payloads

7. Filtering

Dit onderdeel is niet normatief.

Warning

This extension is in development and may be modified at any time.

Base URLs of resources should be as straightforward as possible. Complex result filters, sorting, and advanced querying (restricted to a single resource) are implemented as query paramters on top of the base URL.

Filtering uses unique query parameters that correspond to the queryable fields. If you would like to request a list of applications from the end point /aanvragen/ and limit this to open applications, use the request GET /aanvragen?status=open gebruikt. In this example status is a queryable field.

API principle: Use query parameters corresponding to the queryable fields

This appreoach can be applied to nested properties as well. This is the collection:

[{
"id": 1,
"status": "actief",
"overheid": {
  "code": "0000",
  "naam": "Ministerie van BZK"
}
}, {
"id": 2,
"status": "inactief",
"overheid": {
  "code": "9901",
  "naam": "Provincie Gelderland"
}
}]

All objects with the status actief can be filtered using /?status=actief. Filtering objects with code 0000 of the property overheid at the same time, this relates to a nested property. In this case, use the dot notation (like JavaScript): /?status=actief&overheid.code=0000.

8. Sorting

Dit onderdeel is niet normatief.

Warning

This extension is in development and may be modified at any time.

To sort, use the query parameter sorteer. This query parameter takes a comma-separated list of field names to be used in the sort. Prefixing a filed name with a minus sign (-), the field is sorted in descending order. A few examples:

Request Explanation
GET /aanvragen?sorteer=-prio Retrieves a list of aanvragen sorted by the property prio in descending order.
GET /aanvragen?sorteer=-prio,aanvraagDatum Retrieves a list of aanvragen sorted by the property prio in descending order. Within a specific value of prio, the objects are sorted by aanvraagDatum in ascending order.

API principle: Use the query parameter sorteer to sort

10. Time travelling

Dit onderdeel is niet normatief.

Warning

This extension is in development and may be modified at any time.

Information about a resource may change over time. Time travelling is a default mechanism to request information about a specific moment in time is. From the perspective of time travelling using an API, there are three important moments in time:

Moment in time Description
Geldig This is a moment in time on which the data returned is valid (geldig) in reality.
Beschikbaar This is a moment in time on which the data returned is available (beschikbaar) using the same API.
In werking (getreden op) This is a moment in time on which the data returned became legally effective (in werking getreden op).

The base principle for time travelling is as follows:

The values of these three query parameters in the URI are structured like this (based on RFC3339 / ISO 8601):

Parameter Value
geldigOp YYYY-MM-DD
beschikbaarOp YYYY-MM-DDThh:mm:ss.s
inWerkingOp YYYY-MM-DD
Value Description
YYYY Four-digit year
MM Two-digit month (01 = January, etc.)
DD Two-digit day of the month (01 to 31)
hh Two-digit hour (00 to 23) (am / pm not allowed)
mm Two-digit minute (00 to 59)
ss Two-digit second (00 to 59)
s One or more digits to represent the decimal fraction of a second

10.1 Level of support

Time travelling is an optional mechanism with optional features. It may be that:

Users have to be informed of the invalid/not-supported requests when processing time travelling requests:

Not supported? HTTP status code and explanation
Time travelling HTTP/1.1 403 Content-Type: application/problem+json Content-Language: nl { "type": ".../id/<c>/parameter/TijdreizenNietOndersteund", "title": "{Het verzoek is begrepen, maar de tijdreisparameters worden niet ondersteund}", "status": 403, "detail": "{Tijdreizen wordt niet ondersteund, verwijder alle tijdreisparameters}", "instance": "/resource/#id"}
Legally effective HTTP/1.1 403 Content-Type: application/problem+json Content-Language: nl { "type":".../id/<c>/parameter/InWerkingTredingNietOndersteund’", "title": "{Het verzoek is begrepen, maar de tijdreisparameter ‘inWerkingOp’ wordt niet ondersteund}", "status": 403, "detail": "{Tijdreizen met een inwerkingstredingsmoment wordt niet ondersteund, verwijder de parameter ‘inWerkingOp’}", "instance": "/resource/#id"}
Future HTTP/1.1 403 Content-Type: application/problem+json Content-Language: nl { "type": ".../id/<c>/parameter/ToekomstNietOndersteund", "title": "{Het verzoek is begrepen, maar tijdreisparameters mogen geen tijdstip in de toekomst bevatten” }", "status": 403, "detail": "{Tijdreizen naar de toekomst wordt niet ondersteund, verwijder de tijdreisparameters of gebruik een tijdstip in het verleden}", "instance": "/resource/#id"}

10.2 Robustness

Users have to be informed of the lack of historical data and general requests in a resource collection when processing time travelling requests:

Request HTTP status code and explanation
Specific resource HTTP/1.1 404 Content-Type: application/problem+json Content-Language: nl {"type": ".../id/<c>/BestaatNiet", "title": "De gevraagde versie bestaat niet.", "status": 404, "detail": "Versie 2017-01-01 van regeling 123 bestaat niet.", "instance": "/regelingen/v1/123?geldigOp=2017-01-01"}
Resource-collectie GET .../regelingen/v1?beschikbaarOp=2017-01-01T00:00:00 HTTP/1.1 200 Content-Type: application/json+hal {"_links": {"self": {"href": "/r-n/v1/123?beschikbaarOp=2017-01-01T00:00:00" }, "items": [ { "href": "/r-n/v1/123?beschikbaarOp=2017-01-01T00:00:00" }, { "href": "/r-n/v1/456?beschikbaarOp=2017-01-01T00:00:00" }, { "href": "/r-n/v1/789?beschikbaarOp=2017-01-01T00:00:00" }] } }

11. GEO support

Dit onderdeel is niet normatief.

Warning

This extension is in development and may be modified at any time.

REST APIs for handling spatial geometries may provide spatial filtering. There is a distinction between retrieving geometries in the result (response) and supplying a spatial filter in the call (request). When requesting parcel information, users do not necessarily require the geometry. A name or parcel ID may be sufficient.

API principle: Support GeoJSON for GEO APIs

For GEO APIs, preferably use the GeoJSON standard [rfc8142].

11.1 Result (response)

In a JSON API the geometry is returned as GeoJSON, wrapped in a separate GeoJSON object.

API principle: Include GeoJSON as part of the embedded resource in the JSON response

11.2 Call (requests)

A spatial filter can be complex and large. It is best practice to supply complex queries in the body, not in the request URI. Since GET may not have a payload (although supported by some clients) use a POST request to a separate endpoint. For example, a GEO query to all panden where the geometry in the field _geo (there may be multiple geometry fields) contains a GeoJSON object (in this case a Point, so one coordinate pair):

// POST /api/v1/panden/_zoek with request body:
{
"_geo": {
  "contains": {
    "type": "Point",
    "coordinates": [5.9623762, 52.2118093]
  }
}
}

API principle: Provide a POST endpoint for GEO queries

The POST endpoint is preferably set up as a generic query endpoint to support combined queries:

// POST /api/v1/panden/_zoek with request body:
{
"_geo": {
  "contains": {
    "type": "Point",
    "coordinates": [5.9623762, 52.2118093]
  }
},
"status": "Actief"
}

API principle: Support mixed queries at POST endpoints

Other GEO queries like intersects or within operators can be used as well. For further information, read: https://www.w3.org/TR/sdw-bp/#entity-level-links

API principle: Put results of a global spatial query in the relevant geometric context

For example:

// POST /api/v1/_zoek:
{
"_embedded": {
  "results": [{
    "type": "enkelbestemming",
    "_links": {
      "self": {
        "title": "Enkelbestemming 1234",
        "href": "/enkelbestemmingen/1234"
      }
    }
  }, {
    "type": "dubbelbestemming",
    "_links": {
      "self": {
        "title": "Dubbelbestemming 8765",
        "href": "/dubbelbestemmingen/8765"
      }
    }
  }]
}
}

11.3 CRS-negotiation

The default CRS (Coordinate Reference System) for GeoJSON is WGS84. This is the global coordinate reference system that can be applied world-wide. Due the datum and the tectonic displacements it is not accurate enough for local coordinate reference systems like ETRS89 (EPSG:4258, European), or RD/Amersfoort (EPSG:28992, Dutch).

Since most client-side mapping libraries use WGS84, the W3C/OGC working group Spatial Data on the Web recommends to use this as the default coordinate reference system. Thus, spatial data can be mapped without any complex transformations. The API strategy caters for this supporting not only ETRS89 and RD/Amersfoort, but also WGS84 and Web Mercator (EPSG:3857).

API principle: Use ETRS89 as the preferred coordinate reference system (CRS)

The CRS can be specified for request and response individually using custom headers: RD/Amersfoort, ETRS89, WGS84, and Web Mercator.

The guiding priciples for CRS support:

API principe: Pass the coordinate reference system (CRS) of the request and the response in the headers

The following headers are purely meant for negotiation between the client and the server. Depending on the application, the request not only contains geometries but also specific meta data, e.g. the original realistion including the collection date.

Request and response may be based on another coordinate reference system. This applies the HTTP-mechanism for content negotiation. The CRS of the geometry in the request (request body) is specified using the header Content-Crs.

HTTP header Value Explanation
Content-Crs EPSG:4326 WGS84, global
Content-Crs EPSG:3857 Web Mecator, global
Content-Crs EPSG:4258 ETRS89, European
Content-Crs EPSG:28992 RD/Amersfoort, Dutch

The preferred CRS for the geometry in the response (response body) is specified using the header Accept-Crs.

HTTP header Value Explanation
Accept-Crs EPSG:4326 WGS84, global
Accept-Crs EPSG:3857 Web Mercator, global
Accept-Crs EPSG:4258 ETRS89, European
Accept-Crs EPSG:28992 RD/Amersfoort, Dutch

11.4 CRS transformation

Certified software is available to the national government to transform between coordinate reference systems.

API principle: Use content negotiation to serve different CRSs

12. Paging

Dit onderdeel is niet normatief.

Warning

This extension is in development and may be modified at any time.

For content identified with the media type 'application/hal+json', Hypertext Application Language (HAL) is used for paging. Two reserved fields, _links (required) and _embedded (optional) are added to retrieved objects. These fields represent hyperlinks and embedded resources respectively:

{
"_links": {
  "self": {
    "href": "https://.../api/registratie/v1/aanvragen?pagina=3"
  },
  "first": {
    "href": "https://.../api/registratie/v1/aanvragen"
  },
  "prev": {
    "href": "https://.../api/registratie/v1/aanvragen?pagina=2"
  },
  "next": {
    "href": "https://.../api/registratie/v1/aanvragen?pagina=4"
  },
  "last": {
    "href": "https://.../api/registratie/v1/aanvragen?pagina=5"
  }
},
"id": "https://.../api/registratie/v1/aanvragen/12",
"naam": "Mijn dakkapel",
"samenvatting": "Ik wil een dakkapel bouwen!",
"_embedded": {
  "aanvrager": {
    "naam": "Bob"
  }
}
}

In case of plain JSON, GeoJSON, or something different from HAL, the _links field is omitted. These may be added to the link response headers. Besides the representation the following metadata is returned using HTTP headers.

HTTP header Explanation
X-Total-Count (optional) Total number of results
X-Pagination-Count (optional) Total number of pages
X-Pagination-Page (optional) Current page
X-Pagination-Limit (optional) Number of results per page

For large collections, the generation of X-Total-Count and X-Pagination-Count may have a considerable impact on the performance, particularly if there is no or limited filtering.

API principle: Use JSON+HAL with media type application/hal+json for pagination

All links in HAL are absolute. This relieves clients from the burder to resolve URLs to external links (to other endpoints, linked-data resources, etc.) for navigation.

13. Caching

Dit onderdeel is niet normatief.

Warning

This extension is in development and may be modified at any time.

For some resources, caching is required. HTTP provides default mechanisms for caching. Furthermore, both clients and infrastructure already handle these mechanisms by default.

The requirements to leverage these default caching mechanisms:

There are 2 ways to implement caching: ETag and Last-Modified.

13.1 ETag

An ETag (Entity Tag) is a hash code or checksum of a resource. A modification of a resource results in another ETag. Hence, an ETag is unique to a particular version of a resource. Returning a resource, the ETag is added as the HTTP header ETag. The client caches both the resource and the ETag. Requesting the same resource, this ETag is supplied in the HTTP request header If-None-Match. The server matches the ETag in the HTTP request header If-None-Match to the ETag for the resource available to the server. In case these match, the server returns a HTTP response status code 304 Not Modified terug. The client retrieves the resource from its own cache. This only applies to client-side caching, in case the client sends the request headers along, otherewise it receives an HTTP status code 200 OK.

13.2 Last-Modified

It works just like ETag, but uses timestamps instead. The response HTTP header Last-Modified contains a timestamp that corresponds to the RFC 1123 notation and is valided against the request HTTP header If-Modified-Since. The server checks whether the resource has been modified since the timestamp provided. If not, the server returns an HTTP status code 304 Not Modified. The client retrieves the resource from its own cache. This only applies to client-side caching, in case the client sends the request headers along, otherewise it receives an HTTP status code 200 OK.

API principle: Apply caching to improve performance

14. Rate limiting

Dit onderdeel is niet normatief.

Warning

This extension is in development and may be modified at any time.

APIs limit the amount of requests that can be sent within a particular time frame to prevent server overload and to guarantee a high service level. APIs may track a rate limit (quota) per month that is enforced per 60 second time interval.

HTTP headers communicate the rate limit to the users.

HTTP header Explanation
X-Rate-Limit-Limit Amount of client requests per time frame
X-Rate-Limit-Remaining Amount of remaining client request in the current time frame
X-Rate-Limit-Reset Amount of remaing seconds in the current time frame

API principe: Apply rate limiting

[rfc6585] introduces an HTTP status code 429 Too Many Requests to inform users the rate limit has been reached.

API principe: Provide rate limiting information

14.1 Expose API-key

API keys are "unrestricted" by default. There are no usage restrictions and these API keys should therefore not be exposed in a web application. Using API keys without usage restrictions in JavaScript creates a real change for abuse and quota theft. To prevent this, restricted API keys should be issued and used.

API principe: Use public API-keys

15. Error handling

Dit onderdeel is niet normatief.

Warning

This extension is in development and may be modified at any time.

Like a Web page displays useful error messages to visitors in case of an error, an API should return useful error message in a known and manageable format. The representation of an error is no different from the representation of a random resource, but with a particular set of fields.

APIs should always return useful HTTP status codes. HTTP status codes are divided in two categories:

A JSON representation of an error should contain a few details to assist developers, operators, and users:

The base for these default formats is [rfc7807]. A JSON-representation of an error is formulated like the following object:

{
"type": "URI: https://content.omgevingswet.overheid.nl/id/<c>[/{categorie}]/{fout}",
"title": "Hier staat wat er is misgegaan",
"status": 401,
"detail": "Meer details over de fout staan hier",
"instance": "urn:uuid:ebd2e7f0-1b27-11e8-accf-0ed5f89f718b" // The error instance  
}

Validation errors for POST, PUT, and PATCH requests are specified per field. The full list of errors is returned concurrently. The response consists of a fixed top level and error code for validation error and additional error fields with detailed errors per field:

{
"type": "https://content.omgevingswet.overheid.nl/id/<c>/ValidatieFout",
"title": "Hier staat wat er is misgegaan…",
"status": 400,
"invalid-params": [{
  "type": "https://content.omgevingswet.overheid.nl/id/<c>/validatie/Voornaam",
"name": "voornaam",
"reason": "De voornaam mag geen speciale karakters bevatten."
}, {
"type": " https://content.../<c>/fouten/validatie/Wachtwoord",
"name": "wachtwoord",
"reason": "Het wachtwoord is verplicht."
}],
"instance": "urn:uuid:4017fabc-1b28-11e8-accf-0ed5f89f718b" // De fout-instantie
}

API principle: Use default error handling

15.1 HTTP status codes

HTTP defines a range of default status codes for APIs. These assist users to of the APIs to handle errors.

API principle: Use the required HTTP status codes

Summary of HTTP operations and the primary HTTP status codes:

Operation CRUD Full collection (e.g. /resource)
specific item (e.g. /resource/\<id>)
POST Create 201 (Created), HTTP header Location with the URI to the new resource (/resource/\<id>)
405 (Method Not Allowed), 409 (Conflict) in case the resource already exists
GET Read 200 (OK), list of resources. Use paging, filtering and sorting to ease the handling of large collections
200 (OK) single resource, 404 (Not Found) if the ID does not exist or is invalid
PUT Update 405 (Method Not Allowed), except for the purpose to modify or replace every resource in a collection
409 in case a modification is not possible due to the current state of an instance
200 (OK) or 204 (No Content), 404 (Not Found) if the ID does not exist or is invalid
PATCH Update 405 (Method Not Allowed), except for the purpose to replace the full collection.
409 if a modification is not possible due to the current state of an instance.
200 (OK) or 204 (No Content), 404 (Not Found) if the ID does not exist or is invalid
DELETE Delete 405 (Method Not Allowed), except for the purpose to remove the full collection.
200 (OK) or 404 (Not Found) if the ID does not exist or is invalid

A short list and description of HTTP status codes:

HTTP status code Description
200 OK Response to a successful GET, PUT, patch or DELETE. Also suitable for POST that does not result in a creation
201 Created Response to a POST that results in a creation. Should be combined with a location header that points to the location of the newly created resource
204 No Content Response to a successful request that does not return content (e.g. a DELETE)
304 Not Modified If HTTP caching headers are applied
400 Bad Request The request is invalid, e.g. in case the request (body) cannot be interpreted
401 Unauthorized If no or invalid authentication credentials are supplied. Also useful to display an authentication window if the API is used in a Web browser
403 Forbidden Response to a successful authentication, but the verified users is not authorised to access the resource
404 Not Found Response to a request for a non-existing resource
405 Method Not Allowed Response to an HTTP method that is not allowed for the authenticated user
406 Not Acceptable Reponse to an unsupported format request (part of content negotiation)
409 Conflict The request cannot be handled due to a conflict with the current state of the resource
410 Gone Indicates the resource is no longer available at the requested endpoint. Useful top level response to requests for previous API versions
412 Precondition Failed The preconditions supplied in one or more fields in the request header have been omitted or failed upon validation by the server
415 Unsupported Media Type If the wrong content type is supplied as part of the request
422 Unprocessable Entity Response to a request (body) that is correct but that cannot be handled by the server
429 Too Many Requests Response if the rate limit has been exceeded.
500 Internal Server Error If an unexpected error occured and server cannot respond.
503 Service Unavailable If an API is not available (e.g. due to planned maintenance)

16. Signing and Encryption

Dit onderdeel is niet normatief.

Warning

This extension is in development and may be modified at any time.

Signing and encryption are mechanisms that can provide additional security on top of the transport layer security that is provided by HTTP TLS in combination with some form of authentication and authorization.

In this API strategy we concern ourselves with RESTful APIs. In RESTful APIs you request the current value of a resource. This is different from SOAP based protocols where content is transported using messages. Using SOAP part of or all of the content is signed and/or encrypted by the sender of the message. This has utitlity as there are no pre conceptions about resources and their behaviour. When using REST you have to ask yourself what is being signed or encrypted. The value of a resource at a certain date and time. Does this have utility above and beyond the transport layer security thats already there?

Be aware that signing and encryption are "expensive". There is a performance hit (especially for encryption), but most of all in the real world implementation of an API using these security methods will be more expensive. Knowledge among programmers is ussually not complete, leading to extra time learning new skills. The added complexity leads to all manner of extra possibilities to create incorrect code, meaning testing will take longer.

Have a long hard look at alternatives to the technical solution of signing and encryption before commiting to using them.

16.1 Signing

Signing can be used to achieve:

16.1.1 Detached, Enveloping and Enveloped signatures

Generally speaking there are three distinct types of signature: Detached, Enveloping and Enveloped. A signature can be detached, that is the signature is separate from the content it is signing. Signatures can be enveloping, that is the content being signed is contained within the signature itself. The last option is an enveloped signature, that is the signature structure/element is contained wihin the content that is signed.

16.1.2 Canonicalization

Canonicalization(C14n) ensures that syntactically irrelavant information such as trailing whitespaces, different line terminators or tabs do not invalidate a signature. This is especially important with detached signatures where a different rendering of (semantically) the same content can invalidate a signature. A C14n algorithm ensures semantically identical content receives the same signature.

16.1.3 Signing within encoding formats

When signing takes place within an encoding format the way information is exchanged(e.g. through APIs) is not directly relevant to the way signatures are applied. The API design rules do not currently express any normative preference for encoding formats. We do have the JSON extension that expresses a non normative preference for JSON. Therefor we do include information on signing within the two most common encoding formats (XML & JSON) themselves even if it is not directly related to APIs.

16.1.3.1 Signing XML

The standard for XML digital signatures allows for Detached, enveloping and enveloped signatures of any XML format. https://www.w3.org/TR/xmldsig-core1/ When using a specific XML encoding format (or instance GML for geograpic information) one should be aware that when this specific XML format was not made with digital signatures in mind it may be very difficult to implement digital signatures. For instance for enveloped XML digital signatures most implementations expect the signature element as either the first or last element of the XML root element. An encoding standard such as GML currently does not allow users to add a signature element to the root of an xml document. Making XML signatures practically unimplementable using standard software.

16.1.3.2 Signing JSON

16.1.4 Signing requests/response

16.1.4.1 Signing requests/respone with knowledge of encoding format

JWT security best practices: https://datatracker.ietf.org/doc/draft-ietf-oauth-jwt-bcp/

16.1.4.2 Signing requests/response without knowledge of encoding format

16.2 Encryption

17. Informative API Principles

Dit onderdeel is niet normatief.

Note

17.1 API-11: Encrypt connections using at least TLS v1.3

Encrypt connections using at least TLS v1.3. Use TLS v1.2 as a fall-back option only. In case of access restrictions use two-way TLD. Since the connection is always encrypted, the authentication method is straightforward. This allows the application of basic authentication tokens instead of encrypted authentication tokens.

17.2 API-12: Allow access to an API only if an API key is provided

Preferrably, APIs should require at least a sign-up process that involves accepting its fair use policy before an API key is issued.

17.3 API-13: Accept tokens as HTTP headers only

There is an inherent security issue when passing tokens as a query parameter, because most Web servers store query parameters in the server logs.

17.4 API-14: Use OAuth 2.0 for authorisation

A RESTful API should not maintain state. A token has to be sent for each request. OAuth 2.0 is the recommended standard. Chapter Beveiliging contains further information.

17.5 API-15: Use PKIoverheid certificates for access-restricted or purpose-limited API authentication

In the case of APIs that have access-restrictions or purpose-limitations, additional authentication based on PKIoverheid certificates and mutual TLS authentication should be provided.

17.6 API-21: Inform users of a deprecated API actively

Using the Warning response header in all responses of the deprecated APIs, users are informed of the deprecation and upcoming removal date.

17.7 API-22: JSON first - APIs receive and send JSON

APIs receive and send JSON.

17.8 API-23: APIs may provide a JSON Schema

APIs may support JSON Schema (<http: json-schema.org)="">to allow and facilitate validation.

17.9 API-24: Support content negotiation

Besides JSON, APIs should support other representations as XML and RDF using the default HTTP content negotiation mechanism. In case the requested format cannot be provided, a 406 ot Acceptable response is sent.

17.10 API-25: Check the Content-Type header settings

Check the Content-Type header is application/json or another supported content types, otherwise send the HTTP status code 415 Unsupported Media Type.

17.11 API-26: Define field names in in camelCase

Define field names in a such a way that the first word starts with a lower case and subsequent words start with a capital letter, with no intervening spaces or punctiation.

17.12 API-27: Disable pretty print

The assumption is that REST clients and Web browsers (either with or without add-ons or extensions) can pretty print JSON.

17.13 API-28: Send a JSON-response without enclosing envelope

By default, don't apply an envelope.

17.14 API-29: Support JSON-encoded POST, PUT, and PATCH payloads

APIs support at least JSON-encoded POST, PUT, and PATCH payloads. Encoded form data (application/x-www-form-urlencoded) is not supported.

17.15 API-30: Use query parameters corresponding to the queryable fields

Use uniqe query parameters that correspond to the fields that can be queried.

17.16 API-31: Use the query parameter sorteer to sort

Specify the comma-separated field to sort using the generic query parameter sorteer. Placing a minus sign (-) in front of a field name, the field is sorted in descending order.

17.18 API-33: Support both * and ? wildcard characters for full-text search APIs

Full-text search APIs should support two wildcard characters:

17.19 API-34: Support GeoJSON for GEO APIs

Preferrably, GEO APIs should support the GeoJSON standard (RFC-7946).

17.20 API-35: Include GeoJSON as part of the embedded resource in the JSON response

In case a JSON (application/json) response contains a geometry, represent it in the same way as the geometry object of GeoJSON (RFC-7946):

{
 "type": "Point",
 "coordinates": [125.6, 10.1]
}

17.21 API-36: Provide a POST endpoint for GEO queries

Spatial queries are sent in a POST to a dedicated endpoint.

17.22 API-37: Support mixed queries at POST endpoints

Mixed queries may include both spatial and property queries.

17.23 API-38: Put results of a global spatial query in the relevant geometric context

In case of a global query /api/v1/_zoek, results should be placed in the relevant geometric context, because results from different collections are retrieved. Express the name of the collection to which the results belongs in the singular form using the property type.

17.24 API-39: Use ETRS89 as the preferred coordinate reference system (CRS)

General usage of the European ETRS89 coordinate reference system (CRS) is preferable, but is not necessarily the default CRS. Hence, the CRS has to be explicitly included in each request.

17.25 API-40: Pass the coordinate reference system (CRS) of the request and the response in the headers

The coordinate reference system (CRS) for both the request and the response are passed as part of the request headers and reponse headers. In case this header is missing, send the HTTP status code 412 Precondition Failed.

17.26 API-41: Use content negotiation to serve different CRSs

The CRS for the geometry in the response body is defined using the Accept-Crs header. In case the API does not support the requested CRS, send the HTTP status code 406 Not Acceptable.

17.27 API-42: Use JSON+HAL with media type application/hal+json for pagination

Add two reserved fields _links (required) and _embedded (optional) to the representation. Pass pagination meta data as HTTP headers.

17.28 API-43: Apply caching to improve performance

For caching apply the default HTTP caching mechanisms using a few additional HTTP headers (ETag or Last-Modified) and functionality to determine wether a few specific HTTP headers are supplied (If-None-Match or If-Modified-Since).

17.29 API-44: Apply rate limiting

To prevent server overload and to guarantee a high service level, apply rate limiting to API requests.

17.30 API-45: Provide rate limiting information

Use the HTTP header X-Rate-Limit to inform users of rate limits. In case the rate limits are exceeded, send the HTTP status code 429 Too Many Requests.

17.31 API-46: Use default error handling

API support the default error messages of the HTTP 400 and 500 status code ranges, including the parsable JSON representation (RFC-7807).

17.32 API-47: Use the required HTTP status codes

APIs should at least support the following HTTP status codes: 200, 201, 204, 304, 400, 401, 403, 404, 405, 406, 409, 410, 415, 422, 429, 500, and 503.

17.33 API-49: Use public API-keys

In JavaScript, only use restricted API-keys, linked to specific characteristics of the client-application (web application or mobile application), e.g. a clientId and/or referring URL.

17.34 API-50: Use CORS to control access

Check the domain of the incoming request and generate the response header depending on whether this domain may send requests or not (whitelist). In that case, only add this particular domain to the response header Access-Control-Allow-Origin.

**NOTE: It is technically possible to pass a wildcard ("*") in the response header Access-Control-Allow-Origin to allow all sources. However, this is malpractice!**

17.35 API-52: Use OAuth 2.0 for authorisation with rights delegation

This is in line with the way the OAuth standard appears on the comply or explain list of forumstandaardisatie.

A. References

A.1 Informative references

[rfc6585]
Additional HTTP Status Codes. M. Nottingham; R. Fielding. IETF. April 2012. Proposed Standard. URL: https://httpwg.org/specs/rfc6585.html
[rfc7807]
Problem Details for HTTP APIs. M. Nottingham; E. Wilde. IETF. March 2016. Proposed Standard. URL: https://tools.ietf.org/html/rfc7807
[rfc8142]
GeoJSON Text Sequences. S. Gillies. IETF. April 2017. Proposed Standard. URL: https://tools.ietf.org/html/rfc8142