The complete URI of an API complies to the following structure:
{scheme}://{host}/{base-path}/{path}[?{query-string}]1,2
The next sections will explain the elements of this structure.
Proper naming of resources is key for an API to be easily understandable by clients. There are a few rules that should be followed ([2], [3], [5]):
-
Atomic resources, collection resources and composite resources should be named as nouns because they represent "things", not "actions" (actions would lean more towards verbs as names of resources).
-
Processing function resources and controller resources should be named as verbs because they in fact represent "actions".
-
Processing function resources and controller resources should not be sub-resources of individual other resources.
-
They should not be named by means of a URI template (see 5.6).
-
Individual resources become parameters.
-
Lower case characters should be used in names only because the rules about which URI element names are case sensitive and which are not may cause confusion.
-
If multiple words are used to name a resource, these words should be separated by dashes (i.e. "-").
-
Especially, no underscore (i.e. "_") should be used: when names are rendered in browsers, they will be interpreted as links, i.e. shown underlined, and, thus, the
underscore will be difficult to read.
-
Similarly, camel case or other programming language related naming should be avoided.
-
Singular nouns should be used for naming atomic resources.
-
Names of collections should be "pluralized", i.e. named by the plural noun of the
grouped concept (atomic resource or composite resource).
-
Use forward slashes (i.e. "/") to specify hierarchical relations between resources. A
forward slash will separate the names of the hierarchically structured resources. The
parent name will immediately precede the name of its immediate children.
A scheme denotes the transport protocol supported by the API. Typically, WSO2 APIs will
be all accessible over HTTPS, some APIs may support HTTP, and some may support both
schemes.
The host part of the API specifies the domain of the API. For WSO2 hosted APIs, this value
is apis.wso2.com. When hosted by or for customers this will be substituted by a customerspecific
string.
The base-path of an API follows the structure
/{feature-code}/[ {sub-code}/ ]/{version}
Thus, each base path consists of a feature-code structure indicating the feature for which
this API is for. An optional sub-code structure may be used for features containing logically
independent collections of functionalities. For example, the feature-code may be "apim" for
API manager, in which case no sub-code is used, and the version may be v1.0 (see section
5.5 for details on versioning). For Enterprise Store, the feature-code may be "es", and the
independent Publisher functionality may get the "publisher" sub-code assigned.
The base path will be the same for all APIs of certain features or logically independent
collections of feature functionalities, respectively. This base path will precede each proper
resource name of the API, i.e. the path element of the API's URI. Note, that a path is often
encoded as a URI template (see section 5.6).
The version of an API is specified as part of its URI. This version is specified as a pair of
integers (separated by a dot) referred to as the major and the minor number of the version,
preceded by the lower case letter "v". E.g. a valid version string in the base path would be
v2.1 indicating the first minor version of the second major version of the corresponding API.
Using this versioning scheme is referred to as semantic versioning [6]. In general, a version
number following the semantic versioning concept has the structure major.minor.patch and
the significance in terms of client impact is increasing from left to right:
-
An incremented patch number means that the underlying modification to the API
cannot even be noticed by a client - thus, the patch number is omitted from the
version string. Only the internal implementation of the API has been changed while
the signature of the API is unchanged. From the perspective of the API developer, a
new patch number indicates a bug fix, a minor internal modification, etc.
-
An incremented minor number indicates that new features have been added to
the API, but this addition must be backward compatible: the client can use the old
API without failing. For example, the API may add new optional parameters or a
completely new request.
-
An incremented major number signals changes that are not backward compatible: for
example, new mandatory parameters have been added, former parameters have been
dropped, or complete former requests are no longer available.
It is best practice to support the current major version as well as at least one major version
back. In case new versions are released frequently (e.g. every few months) more major
versions back have to be supported. Otherwise, clients will break (too fast).
When a client is using an API's URI with a version number no longer supported, the server
has to respond with the following response message that especially contains a Location
header field with the URI of the latest version of the API:
HTTP/1.1 301 Moved Permanently
Location:
Note: there is a lot of debate on the subject of how to specify versions, and a couple
alternative approaches to this area are presented. The discussion spans the whole spectrum
from what the pure REST style considers a resource to what the big players providing Web
APIs offer. What is recommended here is a pragmatic guideline.
A URI template is an element which contains strings in curly brackets [8]. These strings are
variables that must be substituted by values when such a URI template is used by a client.
For example, when using the URI template
/shopping-carts/{shopping-cart-id}/{item-id}/product
"shopping-cart-id" and "item-id" are variables. These variable must be substituted when an
API using this URI template in its path should be used.
A typical use of URI templates is in collection resources. An individual member of a
collection will be identified by a unique value that will become the variable in the template
immediately following the name of the corresponding collection resource. For example:
/products/{product-id}
Note the difference between this URI template and the one before. This URI template
denotes a collection that is immediately derived from an entity type of the data model; all
instances of this entity type should be accessible from the API, and they are grouped into
the collection for that purpose.
The URI template before is different in the sense that it has been abstracted from a relation
type of the data model, namely the relation between the Shopping Cart entity type and
the Item entity type. Since there is no need to access the set of all instances of the Item
entity type, no dedicated items collection type is part of the resource model. Instead, only
items associated with a certain shopping cart (identified by its shopping-cart-id) should be
accessible by the API, i.e. this set of items is scoped by the corresponding shopping cart:
the collection of items is called a scoped collection.
By definition, an optional query string (if specified) is a part of the URI contributing to the
unique identification of a resource. i.e. the URI without the query string and the URI with
the query string identify different resources (a fact that is often ignored).
However, it is best practice to not use fields of the query string as identifier components.
In this sense, a query string provides parameters to control the execution of the API
processing the resource identified by the structure preceding the "?" symbol:
{scheme}://{host}/{base-path}/{path}?{query-string}
The query string consists of a sequence of name/value pairs that are separated by an "&".
The name-string and the value-string are separated by a "=".
In practice, URIs are limited in size. Even worse, the maximum size supported by products
differ. As a consequence, parameters of large query strings have to be moved into the
message body of the corresponding requests. Note, that this will only work for requests
that allow a message body (especially POST, but not GET).
6. Representation Specification
A format in which instances of the entity types of the data model are exchanged is referred
to as a representation of such an instance. A representation is some sort of the shape of a
resource, not the resource itself. In this sense, a representation is some sort of view onto the
resource.
The information content of an atomic resource or a composite resource is immediately
defined by the data model underlying an API. The values of the attributes and - if
appropriate - the identifiers of associated resources make up the information content of an
atomic resource. Similarly, the aggregate of the information contents of the resources of a
composite resource is the information content of the composite resource.
A data structure must be decided for this information content. In addition, one or more
renderings of this data structure must be decided. For example, a certain data structure
may be rendered as a JSON (JavaScript Object Notation) document or an XML (eXtensible
Markup Language) document. Typically, renderings of data structures are specified
by means of MIME types. A specific rendering of a data structure is referred to as the
representation of the resource, i.e. a representation has a MIME (Multipurpose Internal Mail
Extensions) type. Keep in mind, that this MIME type does not indicate the data structure
of the information exchanged between the client and the API implementation but only the
rendering.
Note: In most practical situations, a single representation (e.g. JSON) for all entities
exchanged via an API suffice.
Manipulation of resources in the REST style is done by create, retrieve, update, and delete
operations (so-called CRUD operations), that map to the HTTP methods POST, GET, PUT,
and DELETE.
A request that can be used without producing any side-effect is called a safe request. A
request that can be used multiple times and that is always producing the same effect as the
first invocation is called idempotent.
GET is in HTTP as well as in the REST style specified as a safe and idempotent request.
Thus, an API using the GET method must not produce any side-effects. Retrieving an
atomic resource (4.1) or a composite resource (4.3) is done by performing a GET on the URI
identifying the resource to be retrieved.
Retrieving a (subset of) resources of a certain type is done by performing a GET on the URI
of the collection resource (4.2) of that type, and specifying a filter condition (10.2).
PUT substitutes the resource identified by the URI. Thus, the body of the corresponding
PUT message provides the modified but complete representation of a resource that will
completely substitute the existing resource: parts of the resource that have not changed
must be included in the modified resource of the message body. Especially, a PUT request
must not be used for a partial update. As a consequence, PUT is an idempotent request
(but not safe).
Partial updates, i.e. updates that modify only selective pieces of an existing resource have
to be realized by means of corresponding processing function resources (see section 7.3).
Note: PUT may be used to create a new resource, but this is not recommended. The reason
is that in this case, the client is in charge of creating such a globally unique URI identifying
the newly created resource - and ensuring uniqueness of identified is a difficult task. In
contrast, using POST to create new resources relieves the client from creating unique
identifiers because the server will create the URI and return it to the client (see section 7.3)
POST is neither safe nor idempotent. The main usages of POST are the creation of new
resource, and the initiation of functions, i.e. to interact with processing function resources
(4.5) as well as controller resources (4.4).
In order to create a new resource, a POST request is used with the URI of the collection
resource to which the new resource should be added. If the POST is processed successful,
the response message will especially include a Location header that will have the newly
created URI of the added resource as value. Also, it is a good practice to return in the
response message a Last-Modified header containing the time the resource has been
created, as well as the ETag (Entity Tag) header containing the entity tag of the new
resource.
It is often appropriate that the client checks the correctness of the created resource. For
this purpose, the response message body contains the resource as it would be returned by
retrieving it from the URI of the Location header. In this case, the response message also
includes a Content-Location header repeating the URI of the newly created resource.
A resource is deleted by means of the DELETE request on the URI of the resource. Once
a DELETE request returned successfully with a "200 OK" response, following DELETE
requests on the same URI will result in a "404 Not Found" response because there is no
resource available with the URI of the deleted resource.
Note: By definition, DELETE is an idempotent request, which has the following curious
theoretical implication. Responding with "200 OK" to the first DELETE and with "404 Not
Found" for any further DELETE on the same URI is not quite RESTful because the responses
of the first and all further requests are different; thus, the request is not idempotent. In
order to fully comply to the REST style, a server would have to maintain all URIs of deleted
resources in order to always respond with "200 OK", i.e. with the same response. This is
considered to much of an effort for nearly no gain, i.e. this is not implemented in practice.
HTTP headers provide the vehicle for many non-functional properties of REST APIs. The
following list of HTTP headers are used in most APIs.
Accept
This is the list of content types acceptable for the client.
Authorization
The credentials of the client for authentication by the server.
Content-Type
This is the MIME-type of the message body of the PUT or POST request.
If-Match
Used to avoid concurrency conflicts: if the client-passed entity tag is identical to the entity tag of the resource at the server-side, the request if performed.
If-Modified-Since
Used to avoid retrieving data that has been cached by the client. If the client-provided timestamp is identical to the time the entity has been modified last at the server side no message body is returned.
If-None-Match
Used to avoid retrieving data that has been cached by the client. If the client-provided entity tag is identical to the entity tag of the resource at the server side no message body is returned.
If-Unmodified-Since
Used to avoid concurrency conflicts. if the client-passed last-modified time stamp is identical to the time the resource has been changed last at the server-side, the request is performed.
Content-Location
The URL of the message body. For example, the URL of the resource describing the status of a long running request (see 10.6).
Content-Type
The MIME-type of the message body.
ETag
A "fingerprint" of the resource as currently available at the server, often a digest of the resource.
Last-Modified
The timestamp when the resource has been modified the last time at the server.
Location
The URL of a newly created resource.
WWW-Authenticate
An indication of the authentication scheme to be used to access the resource.
HTTP status codes [9] are returned by response messages and provide key information to clients about the status of a request. The following status codes are used in many APIs.
200 OK
The request has been performed successfully. If the request was a GET, the requested
resource is returned in the message body. If the request was a POST, the result of the
requested action is described by the message body, or it is contained in the message body.
201 Created
The request has been performed successfully. The URL of the newly created entity is
contained in the Location header of the response. An ETag header should be returned with
the current entity tag of the resource just created. The response may also contain an entity
corresponding to the created resource.
202 Accepted
The processing of the request has started but will take some time (see 10.6). The success
of the processing is not guaranteed and should be checked by the client. The body of the
response message should provide information about the current state of the processing, as
well as information about where the client can request updated status information at a later
point in time; typically, the Content-Location header of the response contains a URL where
this status information can be retrieved via GET.
303 See Other
The response of the request is available at a different URL; this URL is given as value of the
Location header of the response message. Typically, this status code is returned after the
processing of a long running request is completed and the client retrieves the status of the
long running request (see 10.6).
304 Not Modified
The requesting client has already received the latest version of the requested resource.
Thus, the body of the response message must be empty. This status code is returned as a
result of a conditional GET (see 10.4), and the the specified conditions (i.e. If-Non-Match, IfModified-Since)
are not met.
400 Bad Request
The request is invalid. For example, syntax errors in expressions passed with the request are
found, values are out of range, required data is missing etc.
401 Unauthorized
The request requires client authorization or the passed credentials are not accepted. The
response must include a WWW-Authenticate header. The request may be repeated by the
client including proper credentials in the Authorization headers.
403 Forbidden
The server understood the request but refused to perform it. For example, the request must
be conditional but no condition has been specified.
404 Not Found
The requested entity does not exist.
406 Not Acceptable
The requested media type is not supported. For example, a GET request wants to retrieve
an entity in a media type (specified as value of the Accept header of the request) not
supported by the server.
412 Precondition Failed
The request has not been performed because one of the preconditions has not been met. This status code is returned when the request was conditional (see 10.5) and one of the conditions specified (i.e. If-Match, If-Unmodified-Since) is not met.
415 Unsupported Media Type
The entity passed by the request was in a format that is not supported. For example, a
PUT request passed an entity in its body, and this entity was in a format or media type,
respectively, that is not understood by the server.
Note: 5xx status codes denote severe errors at the server side or the network, or denote
not implemented functions, etc. Such errors are very generic, i.e. there is no need to
document them explicitly for an API.
Except for very simple APIs, a REST API has to offer features that allows to cope with
advanced situations like large result sets, concurrent updates, or long running requests. The
following describes best practices to deal with some of those special situations.
Note: It is a good practice for an API to support at least queries (see 10.2) and pagination
(see 10.3). For example, this will allow to support push-down of filtering, etc. from an API
orchestration (a more and more important API technology [10]) to the individual APIs as
optimization of response time, bandwidth usage, etc
The REST style clearly distinguishes between a resource itself (i.e. as an abstract entity) and
its different possible representations. Such a representation is a rendering of the resource's
information content in the format of a certain media type. Which of the representation of a
resource is returned as content of the message body is negotiated between a client and the
resource provider (a.k.a. content negotiation).
Server-driven content negotiation assumes that a server processing a request determines
the representation best suited to the requesting client. The server is dependent on the
requesting client to specify information about its processing capabilities or requirements.
The latter is done by means of header fields of the request message like Accept, AcceptEncoding
etc.
In client-driven content negotiation the server detects that it has more than one
representation of a resource available that may serve the client's needs. The server
responds to the request with a "300 Multiple Choices" message that carries information
about the representations available, and the client finally selects one of these
representations and explicitly requests it in a following request.
Server-driven content negotiation has the advantage of avoiding a second request to be
made by the client, but has the disadvantage of potentially responding a representation
to the client that is not ideally suited. Client-driven content negotiation has the advantage
that the client gets the representation that is best suited for it, but the disadvantage of two
round-trips.
In the following request, the client specifies that it is able to process JSON, XML as
well as plain text, but that it prefers JSON. The preferences in media types is specified
by weighting a media type by a quality value q. The server will determine which of the
representations it has available and will return the one with the highest quality value.
Example:
GET /products/27182
Accept: application/json;q=0.9, application/xml;q=0.6, text/plain;q=0.1
In case the server does not have a client specified representation, it will respond with a
message returning a corresponding status code:
HTTP/1.1 406 Not acceptable
Note: Even in case an API document supports only one certain media type, a client may
pass a request that contains an Accept header with a different media type. In this case, the
API implementation must return the "406 Not acceptable"message.
A query on a collection resource consists of three artifacts: (i) a mandatory filter condition,
(ii) an optional sort expression, and (iii) an optional projection. First, a list of attributes from
the entity type that can be used in either of these artifacts on which the collection is based
on has to be distinguished.
A filter condition is a boolean expression in these attributes. The spectrum of filter
conditions is from simple (supporting to specify a single attribute name with a value
being compared for equality) to complex (arbitrary conditions and arbitrary comparison
operators).
Example of a complex query:
filter=((price > 1000 AND status = on-stock) OR (price < 200 AND NOT(status = on-stock)))
A sort expression consists of a list of attribute names together with the indication for each
attribute name whether the result is to be sorted ascending or descending with respect to
the corresponding attribute. The order in which the result is sorted is implied by the order
of the list of attribute names.
Example:
sort=(price ASC, delivery-date DESC)
A list of attribute names that have to be used to create each item in the result set is called a
projection. Each specified attribute name is used to extract the corresponding information
from each resource qualified and compile a corresponding item in the result set.
Example:
projection=price,color,status
There are two ways to enable queries on collections. First, a query is part of the query
string of the URI used by a GET. Second, the query is passed in the body of a POST request
of a special processing function resource.
An example of specifying a query as query string of the URI is:
GET /products?status=on-stock&sortAsc=price&projection=price,color,status HTTP/1.1
Specifying a query as part of a query string concatenated to the URI of the collection
resource that is enquired is the preferred way for queries. This is because a GET is used
with this URI, clearly expressing the semantics of the request namely retrieving a subset of
the collection. However, in practice URIs have a maximum length depending on the browser
or Web server used. When queries may become thus complex that the maximum length
may be exceeded, the use of a POST request that contains the (complex) query in the
request body is enforced. For this purpose, a separate processing function resource is to be
realized:
POST /product-search HTTP/1.1
filter=((price > 1000 AND status = on-stock) OR (price < 200 AND NOT(status = on-stock)))
sort=(price ASC, delivery-date DESC)
projection=price,color,status
When a large collection resource (or subsets of it) is to be retrieved, it is often convenient
to retrieve the result set in smaller chunks: for example, the latency of the request is
reduced, clients can predict the amount of data to be dealt with, etc.
For this purpose, the retrieval request specifies a query string containing an "offset" field
as well as a "limit" field; the offset is the position number of a qualified resource where the
retrieval should start, and the limit is the maximum number of resource to be returned.
The response message with the subset of the qualified resource returned should specify the
total number of all qualified resources ("count" field), a link to the next chunk of qualified
resources ("next" field), as well as a link to the previous chunk of qualified resources
("previous" field). The actual format of how these fields as well as the set of resources is
returned, is application specific. The following example is intended to show the principle
only.
Example:
GET /products?offset=42&limit=3
Response:
HTTP/1.1 200 OK
count=119
next={link-to-next-subset}
previous={link-to-previous-subset}
P42-details
P43-details
P44-details
A client may cache resources as well as header fields of resources to reduce transfer of
data that has not been changed since its last retrieval. For this purpose, a client uses a
conditional request to retrieve data. Such a conditional request specifies the If-None-Match
or the If-Modified-Since headers in the request.
The value of the If-None-Match header is the value of the ETag header of the resource as
retrieved last time by the requesting client. The value of the If-Modified-Since header is the
value of the Last-Modified header of the resource as retrieved last time by the requesting
client. When performing the conditional request, the API implementation has to compare
the value(s) passed by the client in the request with the corresponding values of the
current resource as stored by the server (note3 that If-None-Match takes precedence over
If-Modified-Since). If the values have not changed, the resource is not returned (response
code "304 Not Modified"); otherwise, the resource is returned.
Example:
GET /products/31415
If-None-Match: "42049dcaf450987cffd"
If-Modified-Since: Thu, 7 Jan 2016 17:41 CET
Response:
HTTP/1.1 304 Not Modified
Multiple clients interacting with the same resource may cause concurrency conflicts like
lost updates. Pessimistic concurrency control mechanisms avoid conflicts in advance by
locking resources. This is a good choice, if the probability for conflicts is high. Optimistic
concurrency control avoids conflicts by detecting conflicts and signaling them to clients
that may retry their requests. This is a good choice, if the probability for conflicts is low.
Many concurrent interactions in REST-based APIs can be handled by optimistic concurrency
control. For this purpose, requests are sent as conditional requests. A conditional request
specifies the If-Match or If-Unmodified-Since header in the request. The value of the IfMatch
header is the value of the ETag header of the resource as retrieved last time by the
requesting client. The value of the If-Unmodified-Since header is the value of the LastModified
header of the resource as retrieved last time by the requesting client. When
performing the conditional request, the API implementation has to compare the value(s)
passed by the client in the request with the corresponding values of the current resource as
stored by the origin server (note that If-Match takes precedence over If-Unmodified-Since).
If the values have been changed, the request is rejected (response code "412 Precondition
Failed"); otherwise, the request is executed.
Example:
PUT /products/31415
If-Match: "42049dcaf450987cffd"
If-Unmodified-Since: Thu, 7 Jan 2016 17:41 CET
Response:
HTTP/1.1 412 Precondition Failed
In order to send conditional requests, the requesting client either has to cache these values
after having retrieved the resource before, or has to retrieve these values by a GET on the
resource that is performed before the conditional request is made. The disadvantage of the
latter is that a second request has to be made.
Example:
GET /products/31415
...
Response:
HTTP/1.1 200 OK
ETag: "4562aae7732a56"
Last-Modified: Wed, 6 Jan 2016 11:13 CET
...
Even with PATCH, special care must be taken with respect to lost updates: if concurrent
PATCHes are to be supported, concurrency control has to be implemented as a conditional
request.
A similar pattern may be used to realize partial updates without using processing function
resources (section 4.5): a client has to GET the resource to be updated, apply the partial
updates locally, and then send a conditional PUT with the complete resource to the server.
10.6 Long Running Requests
Requests may take too long to return the response synchronously, i.e. such requests have
to be processed asynchronously. For example, a request like applying for a credit (see next
example) may kick-off a workflow involving human beings that will take some time. In this
case, the API will accept the request and return both, the URI of a so-called task resource
as well as the task resource itself in the response body. The task resource is a document
like in the following example, that contains the actual status of the long running request.
The URI of the task resource is passed in the Content-Location header field of the response.
This URI can be used by the client to poll for the processing state of the long running task
later on.
Example:
POST /apply-for-credit HTTP/1.1
...
Response:
HTTP/1.1 202 Accepted
Content-Type: application/xml
Content-Location: https://www.shark-credits.com/apply/tasks/1
< status >
< state > running </state>
< link rel="self" href=".../tasks/1"/ >
< estimatedCompletion> 2020-04-01 </estimatedCompletion>
</status>
The response message specifies, by the "202 Accepted" status code, that the request is
accepted but will be processed asynchronously. The task resource says that the request is
running and gives an estimated completion time, amongst other appropriate information
(note, that there is no standardized format of a task resource). In succeeding requests, the
client will retrieve the actual task resource via the URI value of the Content-Location header
field:
GET /apply/tasks/1 HTTP/1.1
Host: www.shark-credits.com
...
The sample response succeeds successfully ("200 OK"), which means that the retrieval of
the task resource was successful - note especially that this does not indicate that the long
running request completed successfully:
HTTP/1.1 200 OK
Content-Type: application/xml
Content-Location: https://www.shark-credits.com/apply/tasks/1
< status >
< state > running </state>
< link rel="self" href=".../tasks/1"/ >
< estimatedCompletion> 2020-10-10 </estimatedCompletion>
</status>
After some time, the long running request will have completed, i.e. the GET request above
will receive the following response:
HTTP/1.1 303 See Other
Content-Type: application/xml
Location: https://www.shark-credits.com/apply-for-credit
Content-Location: https://www.shark-credits.com/apply/tasks/1
< status >
< state > ready </state>
< link rel="self" href=".../tasks/1"/ >
< message> Image processed & stored </message>
</status>
The status code "303 See Other" specifies that a new resource has been created with the
URI with the value of the Location header field. This is the URI of the result of the long
running request. The task resource now reports in its state field that the long request
succeeded successfully.
The long running request may fail. In this case, the retrieval of the task resource will
succeed with a "200 OK" status code and the task resource's status will report that the long
running request completed but was not successfully. Thus, it must be kept in mind, that the
status code of the response of the retrieval of the task resource is not related to the status
of the long running request at all.
HTTP/1.1 200 OK
Content-Type: application/xml
Content-Location: https://www.shark-credits.com/apply/tasks/1
< status >
< state > FAILED </state>
< link rel="self" href=".../tasks/1"/ >
< estimatedCompletion> 2020-10-10 </estimatedCompletion>
</status>
When a 4xx status code is returned, a client error has been detected. In this case, more
detailed error information should be returned in the body of the response message. The
following error information (in YAML) detailing the client error will help the client to
understand how to fix the request and repeat it successfully.
Error:
title: Error object returned with 4XX HTTP status code
required:
  - code
  - message
properties:
  code:
   type: integer
   format: int64
   description: |
    A product-specific error code.
message:
   type: string
   description: |
    A detailed description of the error occured.
description:
   type: string
   description: |
    A short description about the error message.
moreInfo:
   type: string
   description: |
    Preferably a URL with more details about the error.
error:
type: array
description: |
  If more than one error occurred they are listed separately.
  For example, list out validation errors by each field.
items:
  $ref: '#/definitions/ErrorListItem'
If more than one error occurred, each error is reported as an element of the following array that is part of the error object before.
ErrorListItem:
title: Description of individual errors that may have occurred during a request.
required:
  - code
  - message
properties:
  code:
   type: integer
   format: int64
message:
   type: string
   description: |
    Description about the individual error occurred
Access to resources of an API are typically secured. These resources should be accessible
based on a properly designed permission model to prevent misuse of APIs. For example,
adding, updating, or deleting permissions to access resources or operations as well as
associated users or roles etc. is needed. Furthermore, access requirements can change, i.e.
the ability to manage these permissions flexibly is required.
The following examples elucidate this:
- The user who created a shopping cart has the permission to create and delete an
individual item from the shopping cart.
- Users need explicit permissions to add new products or update existing product
information.
- A different set of users will get permission to delete customers (for example those
with bad credit standing).
Thus, different resources are protected by different permissions and these permissions may
change. To support this, REST APIs need to support extensible security mechanisms like
HTTP Basic Authentication, OAuth (Open Authentication), or XACML (eXtensible Access
Control Markup Language).
Note: Because of the complexity of XACML, OAuth is the pragmatic way of adding security
to APIs. In what follows, XACML is sketched for completeness reasons only.
12.1 Basis Authentication
HTTP Basic Authentication [11] requires that a request contains an Authorization header
field containing user-id and password credentials in Base64 encoding:
Authorization: Basic Zm9d03wWUdz==
In case the request does not contain the credentials, the server will respond with a 401
status code containing the information about the protected resources ("realm") and the
name of the authentication scheme to be used (i.e. "Basic"):
HTTP/1.1 401 Unauthorized
...
WWW-Authenticate: Basic realm="/product"
...
This information will be used by the client to produce the correct value of the Authorization
header to be passed with the repeated request. Otherwise, the request will fail again.
Note, that HTTP Basic Authentication is unsecure. This is because the credentials are
in clear text, i.e. it is Base64 encoded, which can be immediately translated into clear
text. Because of this, HTTP Basic Authentication is only viable over HTTPS. HTTP Digest
encoding is more secure but is subject to a "man-in-the-middle-attack". By using HTTPS,
this is avoided. Thus, HTTP security mechanisms are only acceptable for non-critical APIs,
for requests that generate more secure access tokens (see next) - or these mechanisms are
used over HTTPS only.
12.2 Open Authorization (OAuth)
The likelihood of possible attacks of HTTP Basic or Digest Authentication is reduced by
using OAuth [12] tokens with the HTTP Bearer authentication mechanism. The request
message contains an Authentication header with the OAuth access token:
Authorization: Bearer mF_9.B5f-4.1JqM,
The string after the Bearer keyword is the credential provided by means of OAuth protocols
and mechanisms. The value of the scope field is a list of strings denoting the resources
intended to be accessed by the request. In case the token provided is not sufficient for the
scopes, the request fails.
Note: The use of OAuth protection of APIs requires a design of scopes along with the
APIs. A set of scopes needs to be designed that protects individual processing function
resources, (subsets of) collection resources, or the ability to create or delete resource, for
example.
While OAuth reduces the likelihood of attacks when used over HTTP, there are several
attacks known to OAuth. Thus, although OAuth has been proposed as a mechanism over
HTTP, it should be used over HTTPS to increase security.
12.3 eXtensible Access Control Markup Language (XACML)
A more fine-grained control of access to resources (i.e. finer than scopes) is supported
by XACML [13], but XACML is considered quite complex in practice. The following sketch
should underpin this.
XACML is an attribute-based access control (ABAC) mechanism, in contrast to role-based
access control (RBAC) mechanisms. As such, access to a resource is determined based on
attributes contained in a request message.
For this purpose, XACML describes an access control policy language, a request/
response language and protocol, and a reference architecture. The policy language is
used to express access control policies (who can do what in which context). The request/
response language supports queries about whether a particular access should be allowed
(requests) and specifies answers to those queries (responses). The reference architecture
proposes deployment of necessary components within an infrastructure to allow efficient
enforcement of policies.
XACML Processing
When using XACML to secure API access, a policy enforcement point (PEP) is assumed. It is
in charge of extracting the required parameters from the API request message. Permissions
are validated by a policy decision point (PDP) based on these parameters. The PEP will
create a XACML request based on the extracted parameters. This request is sent to the PDP
to validate permissions. The result of permission validation is returned by the PDP in the
response message and is based on predefined policies. The response message will contain
one of the following statuses:
Permit - the access is granted.
Deny - the access is not granted.
Indeterminate - an error occurred, or some required data was missing and no decision could be made.
Not Applicable - the request could not be processed.
Implications on API Design
When an API call is received, the PEP must return an HTTP 401 Unauthorized status
code if anything other than Permit results from access validation. In case the request is
authorized (i.e. Permit resulted from access validation), the request will be pass to the API
implementation.
XACML may be used with other authentication or authorization mechanisms such as HTTP
Basic Authorization or OAuth.
Note: The use of XACML for protecting APIs requires (i) an implementation that follows
the architecture above, and (ii) the ability for API administrators to define corresponding
policies.