This document captures guidelines for the API design in CAMARA project. These guidelines are applicable to every API to be worked out under the CAMARA initiative.
- API design guidelines
- Table of Contents
- Common Vocabulary and Acronyms
- 1. Introduction
- 2. APIfication Principles
- 2.5 Reduce telco-specific terminology in API definitions
- 3. API Definition
- 4. API Resource Definition
- 5. Versioning
- 6. Error Responses
- 7. Common Data Types
- 8. Pagination, Sorting and Filtering
- 9. Architecture Headers
- 10. Security
- 11. Definition in OpenAPI
- 12. Subscription, Notification & Event
Term | Description |
---|---|
API | Application Programming Interface. It is a rule & specification group (code) that applications follow to communicate between them, used as interface among programs developed with different technologies. |
Body | HTTP Message body (If exists) is used to carry the entity data associated with the request or response. |
Camel Case | It is a kind of define the fields’ compound name or phrases without whitespaces among words. It uses a capital letter at the beginning of each word. There are two different uses: |
Header | HTTP Headers allow client and server send additional information joined to the request or response. A request header is divided by name (No case sensitive) followed by a colon and the header value (without line breaks). White spaces on the left hand from the value are ignored. |
HTTP | Hypertext Transfer Protocol (HTTP) is a communication protocol that allows the information transfer using files (XHTML, HTML…) in World Wide Web. |
JSON | JavaScript Object Notation |
JWT | JSON Web Token (JWT) is an open standard based on JSON proposed by IETF (RFC 7519) for access token creations allowing the identity and purposes spread. |
Kebab-case | Practice in the words denomination where the hyphen is used to separate words. |
OAuth | Open Authorization is an open standard that allows simple Authorization flows to be used in websites or applications. |
REST | Representational State Transfer. |
TLS | Transport Layer Security or TLS is a cryptographic protocol that provides secured network communications. |
URI | Uniform Resource Identifier. |
Snake_case | Practice in the words denomination where the underscore is used to separate words. |
The purpose of this document is to technically describe aspects related to the proper design of Application Programming Interfaces (hereinafter API), so that it serves as a recommended reference for the development of APIs in telco operators and related projects.
Based on principles of standardization, normalization and good practices, this document explains the guidelines for the design and definition of an API, elaborating the following points:
- How to make a proper API definition.
- What are the main aspects to cover.
- What are the best practices for designing an API and managing it properly.
This document is addressed to all the roles and people that may participate in a Programmable Interface (API) design and assumes that they have basic knowledge of what an API is.
APIs are critical components of digitization that enable us to expose the functions and capabilities existing in our systems and network in a secure and standardized way (service channels, between systems/platforms and third parties/partners) without needing to redesign or re-create them (enhancing reuse) with the consequent saving of time and investment.
The APIfication program is based on the following key principles: i) Domain Driven Design; ii) API First; and iii) Standardization.
The main idea of the Domain Driven Design (DDD) approach is "to develop software for a complex domain, we need to build a ubiquitous language that embeds the domain's terminology in the software systems we build" (Fowler, 2020).
This approach to software development focuses efforts on defining, for each defined domain, a model that includes a broad understanding of the processes and rules of that domain.
DDD approach is particularly suitable for complex domains, where a lot of messy logic needs to be organized. The DDD principles are based on:
- Placing the organization's business models and rules in the core of the application.
- Obtain a better perspective at the level of collaboration between domain experts and developers, to conceive software with very clear objectives.
As an initial reference to define the different domains and subdomains, we rely on the TM Forum SID model, illustrated in the figure below.
This set of domains and subdomains is not fixed or static and can evolve over time, depending on the needs for the development and conceptualization of new business or technological elements.
The following figure shows a representation of the concepts handled by the Domain Driven Design approach. The entities located at the bottom are those necessary to analyse the domain, while those at the top of the graph are related to the more technical part of software architecture. Therefore, Domain Driven Design is an "approach to the design of software solutions that ranges from the most holistic definition to the implementation of the objects in the code".
API First strategy consists of considering APIs and everything related to them (definition, versioning, development, promotion...) as a product.
This design strategy considers the API as the main interface of the application and initially begins with its design and documentation, to later develop the backend part, instead of setting up the entire backend first and then adapting the API to everything built.
In this way, the technological infrastructure depends directly on the design of the services instead of being a response to their implementation.
Among the main benefits of an "API First" development strategy, we can highlight:
- Development teams can work in parallel.
- Reduces the cost of application development.
- Increases speed to market.
- Ensures good developer experiences.
In order to ensure the reusability of the different integrations between elements and systems is essential the agreement of the industry (network element providers, system providers, customer service providers, ...) defining a series of interfaces that ensure specific functionality and responses.
There are different organizations, standardization forums and collaboration projects that define specific interfaces for certain domains, which are then implemented by different industry agents: integrators, software manufacturers, etc... Some of these organizations, specialized in network domains, include 3GPP, ETSI, IETF and Broadband Forum, among others.
At the systems level, the reference organization is the Telemanagement Forum (TM Forum). TM Forum is a global association that drives collaboration and collective problem solving to maximize the business success of telecommunications companies and their provider ecosystem. Its purpose is to help this ecosystem to transform and prosper in the digital age.
TM Forum Frameworx is a set of best practices and standards for evaluating and optimizing process performance, using a service approach to operations. The tools available in Frameworx help to improve end-to-end service management in complex, multi-stakeholder environments. It has been widely adopted by the telecommunications industry, providing a common language of processes, functional capabilities, and information.
The TM Forum Frameworx is composed of:
- Business Process Framework, also known to as enhanced Telecom Operations Map (
eTOM
): it is a reference framework for defining the business processes of telecommunications operators. - Application Framework Fundamentals (
TAM
): it groups functionalities into recognizable applications to automate processes that will help us to plan and optimize an application portfolio. - The Information Framework, also known to as Shared Information/Data Model (
SID
): it unifies reference data model that provides a single set of terms for business objects in telecommunications. The goal is to enable people from different departments, companies, or geographic locations to use the same terms to describe the same real-world objects, practices, and relationships.
Over the last years, TMForum is performing a complete transformation of its architecture, moving from Frameworx, whose paradigm is based on applications, to the Open Digital Architecture (ODA
), based on modular components.
TM Forum further defines a set of reference APIs (TMF Open APIs) between the different architecture components.
As a messaging standard, the use of JSON is proposed by default, since it is a light data exchange format and is commonly adopted by current web technologies, although this does not imply that other types of data cannot be used depending on functional and technical requirements.
JSON is made up with two structures:
- A collection of key/value pairs. In various languages this is known as an object, record, structure, dictionary, hash table, key list, or an associative array.
- An ordered list of values. In most languages, this is implemented as arrays, vectors, lists, or sequences. Data exchange format that is independent of the programming language is based on universal structures supported virtually in all programming languages.
In JSON, the following structures are represented.
An object is an unordered set of key/value pairs. An object starts with a "{
" opening brace and ends with a "}
" closing brace. Each name is followed by a “:
” colon and key/value pairs are separated by a “,
” comma.
An array is a collection of values. An array starts with “[
“ left bracket and ends with “]
” right bracket. Values are separated by ",
" comma.
A value can be a string with double quotes, or a number, or true or false or null, or an object or an array. These structures can be nested.
A character string is a collection of zero or more Unicode characters, enclosed in double quotes, using backslash escaping. A character is represented by a character string of a single character. A character string is similar to a C or Java character string.
A number is similar to a C or Java number, except that octal and hexadecimal formats are not used.
White spaces can be inserted between any pair of symbols.
Except for minor encoding details, the above-mentioned structures provide a full description of JSON language.
CAMARA aims to produce 'intent-based' APIs, which have two key benefits:
- for the developer: it does not assume familiarity with the network that will fulfil the API.
- for the operator: it avoids tight-coupling, meaning the API can be fulfilled by various networks (mobile, fixed, satellite etc.) and network versions (4/5/6G etc.)
To realise these benefits it is important to reduce the use of telco-specific terminology/acronyms in developer-facing APIs.
CAMARA API designers should:
- Consider and account for how the API can be fulfilled on a range of network types
- Avoid terms/types specific to a given telco domain. For example the acronym 'UE': in 3GPP terminology this refers to 'User Equipment', but 'UE' means 'User Experience' for most Web developers: 'Terminal' would be a more appropriate, and unambiguous, term. If use of a telco-specific term is unavoidable, either:
- allow a choice, so the developer can utilise other types. E.g.
MSISDN
should not be the only way to identify an end user. - use abstractions, which can evolve: e.g. an
endUserIdentifier
enumeration can support multiple identifiers. - explain the telco-specific term in the documentation, and any constraints it brings.
API definition aims to capture 100% of the functional, syntax and semantic documentation of a contract. It is a critical element for the business strategy and will tag the product offered success.
Outstanding aspects in the API definition are:
- API must be defined from a product view focused to API consumers. It means, it should be prepared a top-down definition of all APIs.
- Product should be focused on the usability.
- API contract must be correctly documented, and it will be published in a shared catalog.
- API exposure definition will be based on “resources could be used by different services and product consumers”. That means, API definition should be a 360-degree product view.
API definition must consider the next assertions:
- API resources definition contains the URI and enterprise resources.
- REST specification compliance aims make the API fully interoperable.
- API resource security.
- Product consumption experience.
- API measurement over how many, who and when it is used.
- API must be versioned, and version must figure in all operations URL. It allows a better last develops management and surveillance, it avoids out of date URLs request and it makes available coexistence of more than one version productive in the same environment.
- Expose the required fields by the API consumers only. Sometimes, all the response body is not necessary for consumers. Allows more security, less the network traffic and agile the API usability.
- One of the API Quality requirements will be the evolution and management scalability, lined with versioning and backward compatibility considerations detailed in this document.
- At API definition time is necessary to include audit parameters to allow make surveillance and next maintenances.
- English use must be applied in the OpenAPI definition.
Representational state transfer (REST) is a software architectural style that was created to guide the design and development of the architecture for the World Wide Web. REST defines a set of constraints on how the architecture of an Internet-scale distributed hypermedia system, such as the Web, should behave.
The formal REST constraints are as follows:
- Client–server architecture. The client-server design pattern enforces the principle of separation of concerns: separating the user interface concerns from the data storage concerns. Portability of the user interface is thus improved. In the case of the Web, a plethora of web browsers have been developed for most platforms without the need for knowledge of any server implementations. Separation also simplifies the server components, improving scalability, but more importantly it allows components to evolve independently (anarchic scalability), which is necessary in an Internet-scale environment that involves multiple organisational domains.
- Statelessness. In computing, a stateless protocol is a communications protocol in which no session information is retained by the receiver, usually a server. Relevant session data is sent to the receiver by the client in such a way that every packet of information transferred can be understood in isolation, without context information from previous packets in the session. This property of stateless protocols makes them ideal in high volume applications, increasing performance by removing server load caused by retention of session information.
- Cacheability: As on the World Wide Web, clients and intermediaries can cache responses. Responses must, implicitly or explicitly, define themselves as either cacheable or non-cacheable to prevent clients from providing stale or inappropriate data in response to further requests. Well-managed caching partially or eliminates some client–server interactions, further improving scalability and performance.
- Layered system: A client cannot ordinarily tell whether it is connected directly to the end server or to an intermediary along the way. If a proxy or load balancer is placed between the client and server, it won't affect their communications, and there won't be a need to update the client or server code.
- Uniform interface: The uniform interface constraint is fundamental to the design of any RESTful system. It simplifies and decouples the architecture, which enables each part to evolve independently. The four constraints for this uniform interface are:
- Resource identification in requests. Individual resources are identified in requests, for example using URIs in RESTful Web services. The resources themselves are conceptually separate from the representations that are returned to the client. For example, the server could send data from its database as HTML, XML or as JSON—none of which are the server's internal representation.
- Resource manipulation through representations. When a client holds a representation of a resource, including any metadata attached, it has enough information to modify or delete the resource's state.
- Self-descriptive messages. Each message includes enough information to describe how to process the message. For example, which parser to invoke can be specified by a media type.
- Hypermedia as the engine of application state (HATEOAS). Having accessed an initial URI for the REST application) analogous to a human Web user accessing the home page of a website) a REST client should then be able to use server-provided links dynamically to discover all the available resources it needs. As access proceeds, the server responds with text that includes hyperlinks to other resources that are currently available. There is no need for the client to be hard-coded with information regarding the structure or dynamics of the application.
HTTP verbs
HTTP standard should be followed to the verbs usability, resulting an API definition oriented to the RESTful design. Any HTTP correct verb usability are allowed.
Principal methods (Verbs) available to use are next ones:
HTTP Verb | CRUD operation | Description |
---|---|---|
GET | Read | Retrieve the actual resource status |
POST | Create | Creates a new resource in the collection. Returns the resource URL when the creation ends. |
PUT | Update | Replaces a specific resource. Returns the resource URL when the replace ends. |
DELETE | Delete | Delete a specific resource. |
PATCH | Update | Updates a specific resource, applying all changes at the same time. If a resource does not exist, it will be created. Returns the resource URL when the update ends. If any error occurs during the update, all of them will be cancelled. |
OPTIONS | Read | Returns a 200 OK with an allowed methods list in the specific resource destined to the header allowed joined to an HTML document about the resource + an API Doc link. |
In this document will be defined the principal verbs to use in the API definition.
POST
: it is used to send date to the server.GET
: it allows performing all the read and retrieve operations. GET operation should be based on data retrieving, that’s why the retrieve operation must be realized directly from the URI including some identifiers and query params.PUT
: it allows update entity data, deleting one or more fields from this entity body if there are not informed. New information to update must be informed by JSON language sent in the body request. If operation requires extra information, Query params could be used. PUT case, if registry does not exist in server data storage, it will be created, that means this operation could be used to create new resources.DELETE
: it allows deleting full entities from server. From consumer perspective, it is not a reversible action. (Rollback action is not available).PATCH
: it allows updating partial fields of a resource.
Using the GET operation to pass sensitive data potentially embeds this information in the URL if contained in query or path parameters. For example, this information can remain in browser history, could be visible to anyone who can read the URL, or could be logged by elements along the route such as gateways and load balancers. This increases the risk that the sensitive data may be acquired by unauthorised 3rd parties. Using HTTPS does not solve this vulnerability, as the TLS termination points are not necessarily the same as the API endpoints themselves.
The classification of data tagged as sensitive should be assessed for each API project, but might include the following examples:
- phone number (MSISDN) must be cautiously handled as it is not solely about the number itself, but also knowing something about what transactions are being processed for that customer
- localisation information (such as latitude & longitude) associated with a device identifier as it allows the device, and hence customer, location to be known
- physical device identifiers, such as IP addresses, MAC addresses or IMEI
In such cases, it is recommended to use one of the following methods to transfer the sensitive data:
- When using
GET
, transfer the data using headers, which are not routinely logged or cached - Use
POST
instead ofGET
, with the sensitive data being embedded in the request body which, again, is not routinely logged or cached
When the POST
method is used, the resource in the path must be a verb (e.g. retrieve-location
and not location
) to differentiate from an actual resource creation.
It is also fine to use POST instead of GET:
- to bypass technical limitations, such as URL character limits (if longer than 4k characters) or passing complex objects in the request
- for operations where the response should not be kept cached, such as anti-fraud verifications or data that can change asynchronously (such as status information)
HTTP status response codes indicate if a request has been completed successfully. Response codes are group by five classes.
- Inform responses (1XX)
- Success responses (2XX)
- Redirections (3XX)
- Client Errors (4XX)
- Server Errors (5XX)
Status codes are defined in the RFC 2616 10th section. You can get the updated specifications from RFC 7231.
Common errors are captured in the table below.
Error code | Description |
---|---|
200 | 200 (OK) status code indicates that the request result successfullyGET -> 200 HTTP Code by default.POST /PUT /PATCH -> Resource update actions, data is returned in a body from server side.DELETE -> Resource delete action, data is returned in a body from server side. |
201 | 201 (Created) HTTP code indicates that the request has created one or more resource successfully.POST /PUT -> When a resource is created successfully. |
202 | 202 (Accepted) code indicated that the request has been accepted to be processed, but it has not ended. Usually, when a DELETE is requested but the server cannot make the action immediately. It should apply to async processes. |
203 | 203 (Unauthorized information) code indicated that the request has been successfully, but the attached payload was modified from the 200 (OK) response from the origin server using a transformation proxy. It is used when data sent in the request could be modified as a third data subset. |
204 | 204 (No Content) indicated that the server has ended successfully the request and there is nothing to return in the body response.POST -> When used to modify a resource and output is not returned.PUT /PATCH -> When used to modify a resource and output is not returned.DELETE -> Resource delete action and output is not returned.NOTE: This list of levels MAY be extended with new values. The OpenID Provider (Auth Server) and the APIs used by the Relying Parties (client Applications) MUST be ready to support new values in the future. |
206 | 206 (Partial Content) The server has fulfilled the partial GET request for the resource. |
400 | 400 (Bad Request) status code indicates that the server cannot or will not process the request due to something perceived as a client error (for example, malformed request syntax, invalid request message structure, or incorrect routing). This code must be documented in all the operations in which it is necessary to receive data in the request. |
401 | 401 (Unauthorized) status code indicates that the request has not been applied because it lacks valid authentication credentials for the target resource. This code has to be documented in all API operations that require subscription by a client. |
403 | 403 (Forbidden) status code indicates that the server understood the request, but is refusing to authorize it. A server that wants to make public why the request was prohibited can describe that reason in the response payload (if applicable). This code is usually documented in the operations. It will be returned when the OAuth token access does not have the required scope or when the user fails operational security. |
404 | 404 (Not Found) status code indicates that the origin server either did not find a current representation for the target resource or is unwilling to reveal that it exists. This code will occur on GET operations when the resource is not available, so it is necessary to document this return in such situations. |
405 | 405 (Method Not Allowed) status code indicates that the origin server knows about the method received in the request line, but the target resource does not support it. This code is documented at the API portal level, it should not be documented at the API level. |
406 | 406 (Not Acceptable) status code indicates that the target resource does not have a current representation that would be acceptable to the user, based on the proactive negotiation header fields received in the request, and the server is unwilling to provide a predetermined representation. It must be reported when there is no response by default, and header fields are reported to carry out the content negotiation (Accept, Accept-Charset, Accept-Encoding, Accept-Language). |
408 | Status code 408 (Request Timeout) indicates that the server did not receive the complete request message within the expected time. This code is documented at the API portal level, it should not be documented at the API level. |
409 | The 409 (Conflict) status code indicates when a request conflicts with the current state of the server. |
415 | The 415 (Unsupported Media Type) status code indicates that the server cannot accept the format of the request body as indicated by the Content-Type or Content-Encoding request header. The API specification will state what request body format should be used. |
429 | The 429 (Too Many Requests) status code indicates that the server is temporarily unable to accept any more requests from the client due to the high number of requests recently sent. A Retry-After response header may indicate how long the client should wait before trying again. |
500 | Status code 500 (Internal Server Error) indicates that the server encountered an unexpected condition that prevented it from fulfilling the request. This code must always be documented. It should be used as a general system error. |
501 | Status code 501 (Not Implemented) indicates that the requested method is not supported by the server and cannot be handled. The only methods that servers require support (and therefore should not return this code) are GET and HEAD. |
502 | Status code 502 (Bad Gateway) indicates that the server, while working as a gateway to get a response needed to handle the request, got an invalid response. |
503 | Status code 503 (Service Unavailable) status code indicates that the server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay. |
504 | Status code 504 (Gateway Timeout) indicates that the server is acting as a gateway and cannot get a response in time. |
API query parameters can be defined as key-value pairs that appear after the question mark in the URL. Basically, they are URL extensions used to help determine the specific content or action based on the data being delivered. Query parameters are added at the end of the URL, using a "?
". The question mark is used to separate the path and the query parameters.
If you want to add multiple query parameters, an "&
" is placed between them to form a query string. You can present a lot of objects types with different lengths, such as arrays, strings, and numbers.
A path param is a unique identifier for the resource. For example:
/users/{userId}
Multiple path params can be entered if there is a logical path of mutually dependent resources.
/users/{userId}/documents/{documentId}
Good Practices
-
Path params cannot be concatenated. A path param must be preceded by the resource represented. If we did this, the URL would be incomprehensible:
/users/{userId}/{documentId}
/users/13225365/647658
-
The attribute must be identifying itself, it is not enough with "
{id}
"/users/{id}
Reason is that if this resource is "extended" in the future and includes other identifiers, we would not know which of the entities the "
{id}
" parameter refers to. For example:- Incorrect:
/users/{id}/documents/{documentId}
- Correct:
/users/{userId}/documents/{documentId}
-
It is recommended that the identifier have a similar morphology on all endpoints. For example, “
xxxxId
”, where xxx is the name of the entity it references:/users/{userId}
/accounts/{accountId}
/vehicles/{vehicleId}
/users/{userId}/vehicles/{vehicleId}
-
Care must be taken not to create ambiguities in the URIs when defining paths. For example, if the "user" entity can be identified by two unique identifiers and we will create two URIs.
/users/{userId}
/users/{nif}
-
Identifiers must be, as far as possible, of a hash type or similar so that we avoid enumeration or brute force attacks for their deduction.
Upon API invocation, one of the options would be chosen and we would not be able to distinguish which one was chosen.
Request header parameters are a great addition to the design of our API functionality. The purpose of this document is not to describe all the possibilities offered by the HTTP protocol, but to try to take the use of HTTP headers into account during the definition and design of APIs, in order to improve their characteristics.
The main HTTP headers are described below:
Accept
: this header can be used to specify certain types of data that are acceptable for the response.Accept
headers can be used to indicate that the request is specifically limited to a small set of desired types, as in the case of a request for an image.Accept-Encoding
: similar to theAccept
header, but restricting the content encodings that are acceptable in the response.Accept-Language
: the consumer defines the list of languages in order of preference. The server answer with theContent-Language
field in the header with the response language.Authorization
: it allows sending the authorization token for API access, initially OAuth and JWT.Content-Type
: it indicates the type of message sent to the recipient or, in the case of the HEAD method, the type of message that would have been sent if the request had been a GET. The MIME type of the response, or the content uploaded via POST/PUT in case it is a request.Content-Length
: it indicates the message size, in octets, sent to the recipient or, in the case of the HEAD method, the message size that would have been sent if the request had been a GET. The size of the response in octets (8 bits)Content-Encoding
: it is used as a message type modifier. The type of encoding used in the response is indicated.Host
: specifies the host and port number of the server to which the request is being sent.
Optional recommended security headers by OWASP
HTTP Strict Transport Security
: a web security policy mechanism which helps to protect websites against protocol downgrade attacks and cookie hijacking. It allows web servers to declare that web browsers (or other complying user agents) should only interact with it using secure HTTPS connections, and never via the insecure HTTP protocol.X-Frame-Options
: a response header (also named XFO) which improves the protection of web applications against clickjacking. It instructs the browser whether the content can be displayed within frames.X-Content-Type-Options
: setting this header will prevent the browser from interpreting files as a different MIME type to what is specified in the Content-Type HTTP header (e.g. treating text/plain as text/css).Content-Security-Policy
: it requires careful tuning and precise definition of the policy. If enabled, CSP has significant impact on the way browsers render pages (e.g., inline JavaScript is disabled by default and must be explicitly allowed in the policy). CSP prevents a wide range of attacks, including cross-site scripting and other cross-site injections.X-Permitted-Cross-Domain-Policies
: a cross-domain policy file is an XML document that grants a web client, such as Adobe Flash Player or Adobe Acrobat (though not necessarily limited to these), permission to handle data across domains. When clients request content hosted on a particular source domain and that content makes requests directed towards a domain other than its own, the remote domain needs to host a cross-domain policy file that grants access to the source domain, allowing the client to continue the transaction.Referrer-Policy
: it governs which referrer information (sent in the Referrer header) should be included with requests made.Clear-Site-Data
: it clears browsing data (e.g., cookies, storage, cache) associated with the requesting website. It allows web developers to have more control over the data stored locally by a browser for their origins.Cross-Origin-Embedder-Policy
: it prevents a document from loading any cross-origin resources that don’t explicitly grant the document permission.Cross-Origin-Opener-Policy
: this response header (also referred to as COOP) allows you to ensure a top-level document does not share a browsing context group with cross-origin documents. COOP will process-isolate your document and potential attackers can’t access to your global object if they were opening it in a popup, preventing a set of cross-origin attacks dubbed XS-Leaks.Cross-Origin-Resource-Policy
: this response header (also referred to as CORP) allows to define a policy that lets web sites and applications opt in to protection against certain requests from other origins (such as those issued with elements like "<script>
" and "<img>
"), to mitigate speculative side-channel attacks, like Spectre, as well as Cross-Site Script Inclusion (XSSI) attacks.Cache-Control
: it holds directives (instructions) for caching in both requests and responses. If a given directive is in a request, it does not mean this directive is in the response.
To avoid cluttering the CAMARA OAS (Swagger) definition files, the above headers must not be included explicitly in the API definition files even when supported, as it can be assumed that developers will already be familiar with their use.
The following HTTP headers are not allowed:
Server
. This header offers relevant information on the server side, including version and in-scope services. It is strongly recommended to disable this header to avoid disclosing such information.X-Powered-By
. This header describes the technology used to implement the exposed service. This information can be useful to potential attackers and should be avoided.X-Frame-Options
. This header is generally used to prevent clickjacking attacks, but there is a more standard header to do this called "Content-Security-Policy". This header is no longer needed.X-UA-Compatible
. This header was introduced by Microsoft to provide compatibility with legacy versions of IE (IE8, IE7, ...). APIs are not considered to have a web interface, so this header is not useful.Expires
. Since theCache-Control
header can offer an expiration date/time for cached values, this header is no longer needed and should be avoided.Pragma
. TheCache-Control
header will do the same job as "Pragma" too, it is more standard, so should be avoided.
During the API definition process, API MIME types must be identified, explaining how the data will be sent to the resource and how the resource will return it to the consumption.
Due to interoperability reasons and in order to comply as closely as possible with REST, it is recommended using standard mime-types, avoid the creation of new mime-types.
The standard headers that allow managing the data format are:
Accept
Content-Type
As a MIME Types example we can identify:
application/xml
application/json
The character set supported must only be UTF-8. The JSON Data Interchange Format (RFC 8259) states the following
JSON text exchanged between systems that are not part of a closed
ecosystem MUST be encoded using UTF-8 [RFC3629].
Previous specifications of JSON have not required the use of UTF-8
when transmitting JSON text. However, the vast majority of JSON-
based software implementations have chosen to use the UTF-8 encoding,
to the extent that it is the only encoding that achieves
interoperability.
Implementers may include the UTF-8 character set in the Content-Type header value, for example: "application/json; charset=utf-8". However, the preferred format is to specify only the MIME type, such as "application/json". Regardless, the interpretation of the MIME type as UTF-8 is mandatory, even when only "application/json" is provided.
The figure below shows an example of URL definition.
As seen, the full URL consist of:- Protocol: transport protocol specification. We will always use HTTPS.
- Domain: machine name or domain. It is defined at the server level.
- Context: the name of the API. Our system may have several differentiated APIs according to its objectives and the relationship of its resources.
- Version: MAJOR version. Part of semantic versioning.
- Resource: specific resource that we are accessing. It can be made up of several levels.
- Path param: part of the resource identifier that precedes it. Indicates that it is the user unequivocally identified by "1244".
- Query Param: resource filter parameters. They are preceded by "?" and as many as defined with "&" can be concatenated.
Good practices
URIs should be designed according to the following considerations:
- URI with lowercase and hyphens. URIs must be "human readable" to facilitate identification of the offered resources. Lowercase words and hyphenation (kebab-case) help achieve this best practice. For example:
/customer-segments
- URIs must contain the exposed resource.
- Verbs use is not allowed.
- URIs must contain the "major version" of the API.
- The resource chain will be defined in the API URI following a hierarchical relationship.
<Resource1>/{<id>}/<Resource2>/{<id>}
- A collection must always be followed by a member of the collection.
- The API response should return, if possible, the hypermedia resource which it refers to. Although it is recommended that the REST API be more pragmatic to return in a single call what is necessary to avoid concatenated calls from clients.
- Short URIs use is recommended for relevant entities. Failure to define a clear hierarchical model will lead to inconsistencies in publishing interfaces through the API and will make it difficult to consume.
- Avoiding usage of the package type nomenclature (e.g.,"com.mycompany.api...") in the API URI, because it makes it difficult for the developer or consumer to use the API.
- URIs are defined per entity based on CRUD operations. Generally, we should only have one operation verb per functional entity (
GET
,PUT
,POST
,PATCH
,DELETE
). - The URI at the business entity level will always be a plural noun.
- OperationIds are defined in lowerCamelCase: For example:
helloWorld
- Objects are defined in CamelCase inside properties field. For example:
Greetings
,ExampleObject
.
Hierachy
Hierarchy could be introduced with the concepts of entity and sub-entity:
- Entity: it is understood as an enough relevant business object to be identified as a product. A single entity is defined by an API.
- Sub-entity: it is understood as a business object that by itself has no business relevance. It is an object that is hierarchically related to an entity.
To make the hierarchy, the following aspects must be applied:
- Two levels of hierarchy should not exceed and should not be more than 8 resources (6-8).
- A resource has multiple operations identified by HTTP Verbs.
- Resources defined through URIs establish a hierarchical relationship with each other:
/<entity>
/<entity>/{<entityId>}
/<entity>/{<entityId>}/<subEntity>
/<entity>/{<entityId>}/<subEntity>/{<subEntityId>}
At this point, some considerations are outlined about the business input and output data of the API resources. This data can be informed by different means: QueryString, Header, Body...
These considerations are below:
-
API input and output business data will follow the camelCase notation.
-
The field names in JSON and XML will follow the same URIs standard.
- Business data must be human readable.
- commercial data name must be a noun, that means, it corresponds to an entity information.
-
Business data sent as part of the HTTP protocol will be exempt from these rules. In these cases, compliance with the standard protocol will apply.
-
Sensitive data (considered this way for security) must go in the body if it is a
POST
request and in the header if it is aGET
, encrypted by default by the HTTPs protocol. -
Description of the input and output data must have:
- Functional description
- Data Type
- Value range supported
Service versioning is a practice by which, when a change occurs in the API of a service, a new version of that service is released so that the new version and the previous one coexists for a certain period of time.
Consumers will be migrated to the new version of the service sequentially. When everyone is consuming the latest version of the service, the previous version is removed.
Consumers can distinguish between one version of the service and another, the technique of adding the API version to the context of the base URL will be used, since this technique is the most used in the main reference APIs.
The structure of the URL would have the following form:
https://host:port/api/v1/resource
When we version through the URL, only the "MAJOR version" is included since this would change when a change incompatible with the previous version occurs.
API implementation versioning will follow semantic versioning. Given a MAJOR.MINOR.PATCH
version number, it increments:
- The
MAJOR
version when you make an incompatible API change. - The
MINOR
version when you add functionality that is backwards compatible. - The
PATCH
version when you fix backward compatible bugs.
Related to the versioning of rest parts involved in Apification projects, best practises are detailed below:
SHARED CODE ON REPOSITORIES
- MAJOR - Major of API Contract
- MINOR - Minor of API Contract
- PATCH - New Updates / Contributions of shared code
MICROSERVICE DEPLOYMENTS (NOT MANDATORY BUT RECOMMENDED)
- MAJOR - Major of API Contract
- MINOR - Minor of API Contract
- PATCH - New Microservice Deployments
Avoid breaking backwards compatibility unless strictly necessary, that means, new versions should be compatible with previous versions.
Bearing in mind that APIs are continually evolving and certain operations will no longer be supported, the following considerations must be taken into account:
- Agree to discontinue an API with consumers.
- Establish the obsolescence of the API in a reasonable period (6 months).
- Monitor the use of deprecated APIs.
- Remove deprecated APIs documentation.
- Never start using already deprecated APIs.
Types of modification
Not all API changes have an impact on API consumers. These changes are often referred to as backward compatible changes. If the API undergoes changes of this type, it should not be necessary to release a new version, it will suffice to replace the current one. What would be very convenient is to notify our consumers with the new changes so that they take them into account.
This is a list of changes to an API that DO NOT affect consumers:
- Add new operations to the service. Translated to REST, it would be to add new actions on a resource (
PUT
,POST
, ...). - Add optional input parameters to requests on existing resources. For example, adding a new filter parameter in a GET on a collection of resources.
- Modify input parameters from required to optional. For example: when creating a resource, a property of said resource that was previously mandatory becomes optional.
- Add new properties in the representation of a resource returned by the server. For example, adding a new age field to a Person resource, which originally was made up of nationality and name.
This other list shows changes that DO affect consumers:
- Delete operations or actions on a resource. For example: POST requests on a resource are no longer accepted.
- Add new mandatory input parameters. For example: now, to register a resource, a new required field must be sent in the body of the request.
- Modify input parameters from optional to mandatory. For example: when creating a Person resource, the age field, which was previously optional, is now mandatory.
- Modify a parameter in existing operations (resource verbs). Also applicable to parameter removal. For example, when consulting a resource, a certain field is no longer returned. Another example: a field that was previously a string is now numeric.
- Add new responses to existing operations. For example: creating a resource can return a 412 response code.
Compatibility management
Tho ensure this compatibility, the following must be followed.
As API producer:
- New fields should always be added as optional.
- Postel's Law: “Be conservative in what you do, be liberal in what you accept from others”. When you have input fields that need to be removed, mark them as unused, so they can be ignored.
- Do not change the field’s semantics.
- Do not change the field’s order.
- Do not change the validation rules of the request fields to more restrictive ones.
- If you use collections that can be returned with no content, then answer with an empty collection and not null.
- Layout pagination support from the start.
As API consumer:
- Tolerant reader: if it does not recognize a field when faced with a response from a service, do not process it, but record it through the log (or resend it if applicable).
- Ignore fields with null values.
- Variable order rule: DO NOT rely on the order in which data appears in responses from the JSON service, unless the service explicitly specifies it.
- Clients MUST NOT transmit personally identifiable information (PII) parameters in the URL. If necessary, use headers.
In order to guarantee interoperability, one of the most important points is to carry out error management aimed at strictly complying with the error codes defined in the HTTP protocol.
An error representation must not be different from the representation of any resource. A main error message is defined, with JSON structure with the following fields:
- A field "
status
", which can be identified in the response as a standard code from a list of Hypertext Transfer Protocol (HTTP) response status codes. - A unique error "
code
", which can be identified and traced for more details. It must be human readable; therefore, it must not be a numeric code. In turn, to achieve a better location of the error, you can reference the value or field that is causing it, and include it in the message. - A detailed description of "
message
"
A JSON error structure is proposed below:
{
"status": 400,
"code": "INVALID_ARGUMENT",
"message": "A human readable description of what the event represent"
}
In error handling different cases must be considered, even at the functional level that it is possible to modify the error message returned to the API consumer. For this error handling, there are two possible alternatives listed below:
- Error handling done with custom policies in the API admin tool.
- Error management performed in a service queried by API.
The essential requirements to consider would be:
- Error handling should be centralized in a single place.
- Customization of the generated error based on the error content returned by the final core service should be contemplated.
- Latency should be minimized in its management.
In the following, we elaborate on the existing client errors. In particular, we identify the different error codes and cluster them into separate tables, depending on their nature: i) syntax exceptions, ii) service exceptions, and iii) server errors.
Syntax Exceptions
Error code | Description |
---|---|
INVALID_ARGUMENT |
A specified resource duplicate entry found. |
CONFLICT |
A specified resource duplicate entry found. |
OUT_OF_RANGE |
Client specified an invalid range. |
PERMISSION_DENIED |
Client does not have sufficient permissions to perform this action. |
ABORTED |
Concurrency conflict. |
ALREADY_EXISTS |
The resource that a client tried to create already exists. |
Service Exceptions
Error code | Description |
---|---|
UNAUTHENTICATED |
Request not authenticated due to missing, invalid, or expired credentials. |
NOT_FOUND |
The specified resource is not found. |
TOO_MANY_REQUESTS |
Either out of resource quota or reaching rate limiting. |
AUTHENTICATION_REQUIRED |
New authentication is required. |
Server Exceptions
Error code | Description |
---|---|
FAILED_PRECONDITION |
Request cannot be executed in the current system state. |
DATA_LOSS |
Unrecoverable data loss or data corruption. |
INTERNAL |
Unknown server error. Typically a server bug. |
BAD_GATEWAY |
An upstream internal service cannot be reached. |
UNAVAILABLE |
Request timeout exceeded. |
TIMEOUT |
Request timeout exceeded. |
NOT_IMPLEMENTED |
This functionality is not implemented yet |
METHOD_NOT_ALLOWED |
The requested method is not allowed/supported on the target resource. |
NOT_ACCEPTABLE |
The server cannot produce a response matching the content requested by the client through Accept-* headers. |
UNSUPPORTED_MEDIA_TYPE |
The server refuses to accept the request because the payload format is in an unsupported format. |
NOTE: When no login has been performed or no authentication has been assigned, a non-descriptive generic error will always be returned in all cases, a
UNAUTHENTICATED
401 “Request not authenticated due to missing, invalid, or expired credentials.” is returned, whatever the reason.
The aim of this clause is to detail standard data types that will be used over time in all definitions, as long as they satisfy the information that must be covered.
It should be noted that this point is open to continuous evolution over time through the addition of possible new data structures. To allow for a proper management of this ever-evolving list, an external repository has been defined to that end. This repository is referenced below.
Link to Common Data Types documentation repository
Exposing a resources collection through a single URI can cause applications to fetch large amounts of data when only a subset of the information is required. For example, suppose a client application needs to find all orders with a cost greater than a specific value. You could retrieve all orders from the /orders URI and then filter these orders on the client side. Clearly, this process is highly inefficient. It wastes network bandwidth and processing power on the server hosting the web API. To alleviate the above-mentioned issues and concerns, Pagination, Sorting and Filtering may optionally be supported by the API provider. Following subsections apply when such functionality is supported.
Services can answer with a resource or article collections. Sometimes these collections may be a partial set due to performance or security reasons. Elements must be identified and arranged consistently on all pages. Paging can be enabled by default on the server side to mitigate denial of service or similar.
Services must accept and use these query parameters when paging is supported:
-
perPage
: number of resources requested to be provided in the response -
page
: requested page number to indicate the start of the resources to be provided in the response (considering perPage page size) -
seek
: index of last result read, to create the next/previous number of results. This query parameter is used for pagination in systems with more than 1000 records.seek
parameter offers finer control thanpage
and could be used one or another as an alternative. If both are used in combination (not recommended)seek
would mark the index starting from the page number specified bypage
andperPage
[index = (page * perPage) + seek].
Services must accept and use these headers when paging is supported:
Content-Last-Key
: it allows specifying the key of the last resort provided in the responseX-Total-Count
: it allows indicating the total number of elements in the collection
If the server cannot meet any of the required parameters, it should return an error message.
The HTTP codes that the server will use as a response are:
200
: the response includes the complete list of resources206
: the response does not include the complete list of resources400
: request outside the range of the resource list
Petitions examples:
page=0 perPage=20
, which returns the first 20 resourcespage=10 perPage=20
, which returns 20 resources from the 10th page (in terms of absolute index, 10 pages and 20 elements per page, means it will start on the 200 position as 10x20=200)
Sorting the result of a query on a resources collection requires two main parameters: Note: Services must accept and use these parameters when sorting is supported. If a parameter is not supported, service should return an error message.
orderBy
: it contains the names of the attributes on which the sort is performed, with comma separated if there is more than one criteria.order
: by default, sorting is done in descending order.
If you may want to specify which sort criteria, you need to use "asc" or "desc" as query value.
For example: The list of orders is retrieved, sorted by rating, reviews and name with descending criteria.
https://api.mycompany.com/v1/orders?orderBy=rating,reviews,name&order=desc
Filtering consists of restricting the number of resources queried by specifying some attributes and their expected values. When filtering is supported, it is possible to filter a collection on multiple attributes at the same time and allow multiple values for a filtered attribute.
Next, it is specified how it should be used according to the filtering based on the type of data being searched for: a number or a date and the type of operation.
Note: Services may not support all attributes for filtering. In case a query includes an attribute for which filtering is not supported, it may be ignored by the service.
Operation | Numbers | Dates |
---|---|---|
Equal | GET .../?amount=807.24 |
GET .../?executionDate=2024-02-05T09:38:24Z |
Greater or equal | GET .../?amount.gte=807.24 |
GET.../?executionDate.gte=2018-05-30 |
Strictly greater | GET .../?amount.gt=807.24 |
GET.../?executionDate.gt=2018-05-30 |
smaller or equal | GET .../?amount.lte=807.24 |
GET.../?executionDate.lte=2018-05-30 |
Strictly smaller | GET .../?amount.lt=807.24 |
GET.../?executionDate.lt=2018-05-30 |
And according to the filtering based on string and enums data being searched for:
Operation | Strings/enums |
---|---|
equal | GET .../?name=Juan |
non equal | GET .../?name!=Jonh |
Contains | GET .../?name=~Rafa |
Additional rules:
- The operator "
&
" is evaluated as an AND between different attributes. - A Query Param (attribute) can contain 1 or n values separated by "
,
". - For operations on numeric, date or enumerated fields, the parameters with the suffixes
.(gte|gt|lte|lt)$
need to be defined, which should be used as comparators for “greater - equal to, greater than, smaller - equal to, smaller than” respectively. Only the parameters needed for given field should be defined e.g. with.gte
and.lte
suffixes only.
Examples:
- Equals: to search users with first name "david" and last name "munoz":
GET /users?name=david&surname=munoz
GET /users?name=David,Noelia
- Search for several values separating them by "
,
".
- Search for several values separating them by "
- Inclusion: if we already have a filter that searches for "equal" and we want to provide it with the possibility of searching for "inclusion", we must include the character "~"
GET /users?name=dav
- Search for the exact name "dav"
GET /users?name=~dav
- Look for names that include "dav"
- Greater than / less than: new attributes need to be created with the suffixes
.(gte|gt|lte|lt)$
and included inget
operation :
paths:
/users:
get:
parameters:
- $ref: "#/components/parameters/StartCreationDate"
- $ref: "#/components/parameters/AfterCreationDate"
- $ref: "#/components/parameters/EndCreationDate"
- $ref: "#/components/parameters/BeforeCreationDate"
...
components:
parameters:
StartCreationDate: <-- component name
in: query
name: creationDate.gte <-- query attribute for "greater - equal to" comparison
required: false
schema:
format: date-time
type: string
AfterCreationDate:
in: query
name: creationDate.gt
required: false
schema:
format: date-time
type: string
EndCreationDate:
in: query
name: creationDate.lte
required: false
schema:
format: date-time
type: string
BeforeCreationDate:
in: query
name: creationDate.lt
required: false
schema:
format: date-time
type: string
Then the parameters can be included in the query:
GET /users?creationDate.gte=2021-01-01T00:00:00
- Find users with creationDate starting from 2021
GET /users?creationDate.lt=2022-01-01T00:00:00
- Find users with creationDate before 2022
GET /users?creationDate.gte=2020-01-01T00:00:00&creationDate.lte=2021-12-31T23:59:59
- Search for users created between 2020 and 2021
With the aim of standardizing the request observability and traceability process, common headers that provide a follow-up of the E2E processes should be included. The table below captures these headers.
Name | Description | Type | Location | Required by API Consumer | Required in OAS Definition | Example |
---|---|---|---|---|---|---|
X-Correlator |
Service correlator to make E2E observability | String | Request/Response | No | Yes | b4333c46-49c0-4f62-80d7-f0ef930f1c46 |
When the API Consumer includes the "X-Correlator" header in the request, the API provider must include it in the response with the same value that was used in the request. Otherwise, it is optional to include the "X-Correlator" header in the response with any valid value. Recommendation is to use UUID for values.
In notification scenarios (i.e. POST request sent towards the listener to the webhook.notificationUrl
indicated), the use of the "X-Correlator" is supported for the same aim as well. When the API request includes the "X-Correlator" header, it is recommended for the listener to include it in the response with the same value as was used in the request. Otherwise, it is optional to include the "X-Correlator" header in the response with any valid value.
One of the key points in the API definition process is to specify and validate the security needs that will be maintained to guarantee data integrity and access control. There are multiple ways to secure a RESTful API, e.g. basic authentication, OAuth, etc., but one thing is for sure: RESTful APIs should be stateless, so authentication/authorization requests should not rely on cookies or sessions. Instead, each API request must come with some form of authentication credentials that must be validated on the server for each request.
Basic idea in terms of security is to understand that various types of data will require different levels of security, depending on the confidentiality of the data you are trying to obtain and the level of trust between the API Provider and the consumer.
REST security design principles The document "The Protection of Information in Computer Systems", by Jerome Saltzer and Michael Schroeder, outlines eight design principles to secure information in computer systems, which are listed and elaborated below:
- Least privilege. An entity should only have the set of permissions necessary to perform the actions for which it is authorized, and no more. Permissions can be added as needed and should be revoked when no longer in use.
- Fail-safe defaults. A user's default level of access to any system resource should be "denied" unless "permission" has been explicitly granted.
- Mechanism economics. The design should be as simple as possible. All component interfaces and interactions between them should be simple enough to understand.
- Full mediation. A system must validate access rights to all of its resources to ensure that they are allowed and must not rely on the cached permissions array. If the level of access to a certain resource is revoked, but that is not reflected in the permissions matrix, it would violate security.
- Open Design. This principle underlines the importance of building a system in an open way, without secret and confidential algorithms.
- Separation of privileges. Granting permissions to an entity should not be based purely on a single condition, a combination of conditions based on resource type is a better idea.
- Least Common Mechanism. It refers to the risk of sharing state between different components. If one can corrupt the shared state, then it can corrupt all other components that depend on it.
- Psychological acceptance. This principles states that the security mechanisms must not make the resource more difficult to access than if the security mechanisms were not present. In short, security should not worsen the user experience (restfulapi.net,2021)
Good practices to secure REST APIs
The following points can serve as a checklist to design the security mechanism of the REST APIs.
-
Simple Management. Securing only the APIs that need to be secure. Any time the more complex solution is made "unnecessarily", it is also likely to leave a hole.
-
HTTPs must be always used. By always using SSL, authentication credentials can be simplified to a randomly generated access token. The token is delivered in the username field of HTTP Basic Auth. It is relatively easy to use, and you get a lot of security features for free.
- If HTTP 2 is used, to improve performance, you can even send multiple requests over a single connection, this way you will avoid the complete overhead of TCP and SSL on subsequent requests.
-
Using hash password. Passwords should never be sent in API bodies, but if it is necessary it must hashed to protect the system (or minimize damage) even if it is compromised in some hacking attempts. There are many hashing algorithms that can be really effective for password security, for example PBKDF2, bcrypt and scrypt algorithms.
-
Information must not be exposed in the URLs Usernames, passwords, session tokens, and API keys should not appear in the URL, as this can be captured in web server logs, making them easily exploitable. For example, this URL (
https://api.domain.com/user-management/users/{id}/someAction?apiKey=abcd123456789
) exposes the API key. Therefore, never use this kind of security. -
Authentication and authorization must be considered
- The OAuth 2.0 authorization framework allows a third-party application to gain limited access to an HTTP service, either on behalf of a resource owner, by orchestrating an approval interaction between the resource owner and the HTTP service, or by allowing the third-party app get access on your behalf.
- OpenID Connect is built on the OAuth 2.0 protocol and uses an additional JSON Web Token (JWT), called an ID token, to standardize areas that OAuth 2.0 leaves up to choice, such as scopes and endpoint discovery. It is specifically focused on user authentication and is widely used to enable user logins on consumer websites and mobile apps.
- All the information about the authN-authZ concepts for CAMARA APIs are documented in https://github.com/camaraproject/IdentityAndConsentManagement/tree/main/documentation/CAMARA-AuthN-AuthZ-Concept.md
-
Add request time flags should be considered. Along with other request parameters, a request timestamp can be added as a custom HTTP header in API requests. The server will compare the current timestamp with the timestamp of the request and will only accept the request if it is within a reasonable time frame (1-2 minutes maybe).
- This will prevent very basic replay attacks from people trying to hack your system without changing this timestamp.
-
Entry params validation Validate the parameters of the request in the first step, before it reaches the application logic. Put strong validation checks and reject the request immediately if validation fails. In the API response, send relevant error messages and an example of the correct input format to improve the user experience.
The security implemented in an API can be divided into three different layers: i) channel security; ii) access security; and iii) data security.
Channel security
The API must ensure that the channel where the consumer and the API will exchange information is secure.
a) TLS and mutual authentication
As for today, it is commonly agreed that all communications over the Internet must be done via secure HTTP (HTTPS) using Transport Layer Security (TLS) to generate a secure and recorded communication channel. In some cases, the TLS channel must be more strictly added a mutual authentication process to identify both actors.
Protect communications with TLS is mandatory for all APIs. Any API that accepts requests without TLS will not be published to the API Manager. The TLS version to use for APIs is TLS 1.2 (for compatibility) and TLS 1.3 (for security and because it is the latest version available). The API Manager should not accept requests made with some older versions of TLS.
In case the request does not use the proper TLS, the API should send an HTTP 403 (Forbidden) code with the corresponding response body.
b) Certificate Chain Validation
When establishing a TLS with mutual authentication, the API must validate that the consumer's certificate is validated by one of the allowed CAs.
In case the request does not use the proper TLS, the API should send an HTTP 401 (Unauthorized) code with the corresponding response body.
c) Recommended Ciphers
These include:
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
Access security
The API must ensure that the consumer is known and can access the requested resources.
a) OAuth
All APIs must be protected by the OAuth 2.0 Framework. All API requests must include an HTTP header called "Authorization" with a valid OAuth access token.
The following controls will be performed on the access token:
- Check if the access token is still valid. If it is expired or revoked, the API will return an HTTP 401 (Unauthorized) code.
- Check if the access token was created to be used by the requestor. Otherwise, the API will return an HTTP 401 (Unauthorized) code.
- Check the scope of the access token, if it is expired or revoked, the API will return an HTTP 403 (Forbidden) code.
b) Scopes
The scopes allow defining the permission scopes that a system or a user has on a resource, ensuring that they can only access the parts they need and not have access to more. These restrictions are done by limiting the permissions that are granted to OAuth tokens.
Scopes should be represented as below for all Camara APIs except the APIs that offer explicit event subscriptions:
- API Name: qod, address-management, numbering-information...
- Protected Resource: sessions, orders, billings…
- Grant-level, action on resource: read, write…
For e.g. qod:sessions:read
The APIs that offer explicit event subscriptions must have a way to reflect which event types are being subscribed to, when a subscription create request is made. This will impact how consent management is handled for these APIs.
Scopes should be represented as below for APIs that offer explicit event subscriptions with action read and delete:
- API Name: device-roaming-subscriptions
- Grant-level, action on resource: read, delete This type of formulation is not needed for the create action.
For e.g. device-roaming-subscriptions:read
The format to define scopes for explicit subscriptions with action create, includes the event type in its formulation to ensure that consent is managed at the level of subscribed event types. Scopes should be represented as below for APIs that offer explicit event subscriptions with action create:
- API Name: device-roaming-subscriptions
- Event-type: org.camaraproject.device-roaming-subscriptions.v0.roaming-on
- Grant-level, action on resource: create
For e.g. device-roaming-subscriptions:org.camaraproject.device-roaming-subscriptions.v0.roaming-on:create
To correctly define the scopes, when creating them, the following recommendations should be taken:
- Appropriate granularity. Scopes should be granular enough to match the types of resources and permissions you want to grant.
- Use a common nomenclature for all resources. Scopes must be descriptive names and that there is no conflict between the different resources.
- Use the kebab-case nomenclature to define API names, resources, and scope permissions.
- Use ":" a separator between API name, protected resources, event-types and grant-levels for consistency.
See section 11.6 Security Definition for detailed guidelines on how to define scopes and other security-related properties in a OpenAPI file.
Data security
The API must ensure that the information sent is as expected and has not been altered by possible attackers.
a) Headers Validation
An attacker can collect information from an API by sending malicious data via headers, so the API must verify that:
- API must receive supported headers only.
HEADER.HACK: SELECT* FROM USERS;
Authorization: Bearer 7894df-ds8f7-sdf84-sdf878u
- Supported headers must have values and longitudes agreed.
Authorization: INSERT INTO …
To avoid this, validate that the "Authorization" header is JWT type and consists of three concatenated Base64url-encoded strings, separated by dots (.)
If a header contains a malicious value or an unaccepted header is received, the API should send an HTTP 400 response.
b) Response body validation
A more common way to attack an API is to send incorrect values or malicious codes within the data sent in the request to obtain relevant information from internal services. Mainly, the APIs do not know if the transmitted data is valid or not, so in the end the malicious data can navigate to the backend and cause serious problems.
To avoid these undesirable situations, the APIs can carry out a previous control of the payload structure. These validations are performed using JSON definitions with a description of the JSON structure, fields, and value formats in this payload.
In case the payload does not follow these definitions, the API must send an HTTP 400 response.
c) Do not repudiate
Sometimes the attacker just wants to modify the payload values to change the behavior of the Customer Service Provider ecosystem to their advantage. For example, if you are running a payment, you can modify the payment payload to change the account id to your own account. This will not change either the structure or the value format of the payload.
In these situations, it is mandatory to require the consumer to send the payload in JWT format, signed with one of the allowed consumer signing certificates.
The API must validate the signature of the JWT in the payload following next requirements:
- The API must validate that the certificate used by the consumer is accepted by one of the allowed CAs (see Certificate Chain Validation section for a list of accepted CAs).
- Validate that the payload has not been modified during its transmission. Below options should be checked:
- Encryption/decryption of the JWT signature using the appropriate consumer public key. The JWT is encoded in Base64.
- Making sure that the decrypted and decrypted signature has the following String value ("
JWT_Header.JWT_Payload
") - Making sure that the JWT payload has the same structure and values as the decrypted part of "
JWT_Signature
".
API documentation helps customers integrate with the API by explaining what it is and how to use it. All APIs must include documentation for the developer who will consume your API.
API documentation usually consists of:
- Conceptual information to introduce clients to the API and the domain.
- Practical information to help customers get involved with the API.
- Reference information to inform customers of every detail of your API.
Below considerations should be checked when an API is documented:
- The API functionalities must be implemented following the specifications of the Open API version 3.0.3 using the .yaml or .json file extension.
- The API specification structure should have the following parts:
- General information (Section 11.1)
- Published Routes (Section 11.2)
- Request Parameters (Section 11.3)
- Response Structure (Section 11.4)
- Data Definitions (Section 11.5)
- OAuth Definition (Section 11.6)
- To avoid issues with implementation using Open API generators:
- Reserved words must not be used in the following parts of an API specification:
- Path and operation names
- Path or query parameter names
- Request and response body property names
- Security schemes
- Component names
- OperationIds
- A reserved word is one whose usage is reserved by any of the following Open API generators:
- Reserved words must not be used in the following parts of an API specification:
This part must include the following information:
- API Version in the next format: X.Y.Z.
- API title with public name.
- A brief description of the main functions of the API.
- API Terms of Service.
- Contact information, with name, email and website of the API Holder.
- Licence information (name, website…)
- Schemes supported (HTTP, HTTPS…)
- Allowed response formats (“application/json”, “text/xml”…)
- Response format (“application/jwt”…)
- Global
tags
object if tags are used for API operations
This part must contain the list of published functions, with the following description:
- URI functionality.
- HTTP Methods. For each one, the following shall be included:
- Functionality summary.
- Functionality method description.
- Optionally
tags
object for each API operation - Title Case is the recommended style for tag names. - Request param list, making reference to "Request params" part.
- Supported responses list, describing success and errors cases.
This part contains a list of expected payload requests, if any. This description should have the following items:
- Parameter name, used to reference it in other sections
- Parameter description
- Parameter place (header, route…)
- Type (basic types like chains, integers, complex objects,...)
- Required field or optional flag
This part describes the list of possible messages returned by the API. It also includes the description of the error response objects:
- Name of the response object, used to refer to it in other sections.
- Response object description.
- Object type (basic types like string, integer... or even more complex objects defined in the "Data definition" part...)
- Metadata links (HATEOAS)
This part captures a detailed description of all the data structures used in the API specification. For each of these data, the specification must contain:
- Name of the data object, used to reference it in other sections.
- Data type (String, Integer, Object…).
- If the format of a string is date-time following sentence must be present in the description:
It must follow [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6) and must have time zone. Recommended format is yyyy-MM-dd'T'HH:mm:ss.SSSZ (i.e. which allows 2023-07-03T14:27:08.312+02:00 or 2023-07-03T12:27:08.312Z)
- If the data type is an object, list of required properties.
- List of properties within the object data, including:
- Property name
- Property description
- Property type (String, Integer, Object …)
- Other properties by type:
- String ones: min and max longitude
- Integer ones: Format (int32, int64…), min value.
In this part, the error response structure must also be defined, which must be as follows:
- Type (Array, Integer, Object …)
- Mandatory fields of the structure
- Properties:
- Error Code
- Type (Array, Integer…)
- Error codes supported, as Enum list
- Error description
- Type (Array)
- Min longitude
- Max longitude
- Error Code
As mentioned in OpenAPI doc here usage of discriminator may simplify serialization/deserialization process and so reduce resource consumption.
The mappings section is not mandatory in discriminator, by default ClassName are used as values to populate the property. You can use mappings to restrict usage to subset of subclasses. When it's possible, use Object Name as key in mapping section. This will simplify the work of providers and consumers who use OpenAPI code generators.
IpAddr:
type: object
properties:
addressType:
type: string
required:
- addressType
discriminator:
propertyName: addressType
mappings:
- Ipv4Addr: '#/components/schemas/Ipv4Addr' <-- use Object Name as mapping key to simplify usage
- Ipv6Addr: '#/components/schemas/Ipv6Addr'
Ipv4Addr: <-- Object Name also known as Class Name, used as JsonName by OpenAPI generator
allOf: <-- extends IpAddr (no need to define addressType because it's inherited
- $ref: '#/components/schemas/IpAddr'
- type: object
required:
- address
properties:
address:
type: string
format: ipv4
...
Ipv6Addr:
allOf: <-- extends IpAddr
- $ref: '#/components/schemas/IpAddr'
- type: object
required:
- address
properties:
address:
type: string
format: ipv6
...
To help usage of Camara object from strongly typed languages prefer to use inheritance than polymorphism ... Despite this, if you have to use it apply following rules:
- objects containing oneOf or anyOf section MUST include a discriminator defined by a propertyName
- objects involved in oneOf / anyOf section MUST include the property designed by propetyName
The following sample illustrates this usage.
IpAddr:
oneOf:
- $ref: '#/components/schemas/Ipv6Addr'
- $ref: '#/components/schemas/Ipv4Addr'
discriminator:
propertyName: objectType <-- objectType property MUST be present in the objects referenced in oneOf
Ipv4Addr: <-- object involved in oneOf MUST include the objectype property
type: object
required:
- objectType
- address
properties:
objectType:
type: string
address:
type: string
format: ipv4
...
Ipv6Addr: <-- object involved in oneOf MUST include the objectype property
type: object
required:
- objectType
- address
properties:
objectType:
type: string
address:
type: string
format: ipv6
...
When IpAddr is used in a payload, the property objectType MUST be present to indicate which schema to use
{
"ipAddr": {
"objectType": "Ipv4Addr", <-- objectType indicates to use Ipv4Addr to deserialize this IpAddr
"address": "192.168.1.1",
...
}
}
The CAMARA API Specification - Authorization and authentication common guidelines are discussed and maintained by the Identity and Consent Management Working Group. In particular, the following aspects are detailed:
- Use of openIdConnect as protocol in
securitySchemes
. - How to fill the
security
property per operation. - How to fill the "Authorization and authentication" section in
info.description
.
Regarding scope naming, the guidelines are:
- Define a scope per API operation with the structure:
api-name:[resource:]action
where
-
api-name
is the API name specified as the base path, prior to the API version, in theservers[*].url
property. For example, from/location-verification/v0
, it would belocation-verification
. -
resource
is optional. For APIs with severalpaths
, it may include the resource in the path. For example, from/qod/v0/sessions/{sessionId}
, it would besessions
. -
action
: There are two cases:- For POST operations with a verb, it will be the verb. For example, from
POST /location-verification/v0/verify
, it would beverify
.- For endpoints designed as POST but with underlying logic retrieving information, a CRUD action
read
may be added, but if it is a path with single operation and it is not expected to have more operations on it, the CRUD action is not necessary.
- For endpoints designed as POST but with underlying logic retrieving information, a CRUD action
- For CRUD operations on a resource in paths, it will be one of:
create
: For operations creating the resource, typicallyPOST
.read
: For operations accessing to details of the resource, typicallyGET
.update
: For operations modifying the resource, typicallyPUT
orPATCH
.delete
: For operations removing the resource, typicallyDELETE
.write
: For operations creating or modifying the resource, when differentiation betweencreate
andupdate
is not needed.
- For POST operations with a verb, it will be the verb. For example, from
-
Eventually we may need to add an additional level to the scope, such as
api-name:[resource:]action[:detail]
, to deal with cases when only a set of parameters/information has to be allowed to be returned. Guidelines should be enhanced when those cases happen.
API | path | method | scope |
---|---|---|---|
location-verification | /verify | POST | location-verification:verify |
qod | /sessions | POST | qod:sessions:create , orqod:sessions:write |
qod | /qos-profiles | GET | qod:qos-profiles:read |
In order to provide event-based interaction, CAMARA API could provide capabilities for subscription & notification management. A subscription allows an API consumer to request event notification reception at a given URL (callback address) for a specific context. A notification is the publication at the listener address about an occurred event. Managed event types are explicitly defined in CAMARA API OAS.
We distinguish 2 types of subscriptions:
- Instance-based subscription (implicit creation)
- Resource-based subscription (explicit creation)
An instance-based subscription is a subscription indirectly created, additionally to another resource creation. For example for a Payment request (in Carrier Billing API), in the POST/payments
, the API consumer could request to get event notification about this Payment request processing update. The subscription is not an autonomous entity and its lifecycle is linked to the managed entity (the Payment resource in this case). The subscription terminates with the managed entity.
Providing this capability is optional for any CAMARA API depending on UC requirements.
If this capability is present in CAMARA API, webhook
object attribute must be used in the POST request. The webhook
object contains following attributes :
attribute name | type | attribute description | cardinality |
---|---|---|---|
notificationUrl | string | https callback address where the notification must be POST-ed | mandatory |
notificationAuthToken | string | OAuth2 token to be used by the callback API endpoint. It MUST be indicated within HTTP Authorization header e.g. Authorization: Bearer $notificationAuthToken | optional |
{
/* Resource instance representation */
"webhook": {
"notificationUrl": "https://callback..."
"notificationAuthToken" : "sdfr5sff...lmp"
}
}
Recommended format conventions regarding notificationAuthToken
attribute, in order to provide Uniqueness, Randomness and Simplicity for its management are the following:
- It SHOULD BE an opaque attribute, meaning that should not be based in security info shared between API Consumer and API Provider
- It has to be restricted in length, a string between [20-256] characters.
- It is HIGHLY recommended to have random-based pattern (e.g. UUIDv4 or another one. Any case it is an implementation topic not design one)
A resource-based subscription is an event subscription managed as a resource. This subscription is explicit. An API endpoint is provided to request subscription creation. As this event subscription is managed as an API resource, it is identified and operations to search, retrieve and delete it must be provided.
Note: It is perfectly valid for a CAMARA API to have several event types managed. The subscription endpoint will be unique, but 'eventType' attribute is used to distinguish distinct events subscribed.
In order to ease developer adoption, the pattern for Resource-based event subscription should be consistent for all API providing this feature.
To ensure consistency across Camara subprojects, it is necessary that explicit subscriptions are handled within separate API/s. It is mandatory to append the keyword "subscriptions" at the end of the API name. For e.g. device-roaming-subscriptions.yaml
4 operations must be defined:
operation | path | description |
---|---|---|
POST | /subscriptions |
Operation to request an event subscription. (*) |
GET | /subscriptions |
Operation to retrieve a list of event subscriptions - could be an empty list. e.g. GET /subscriptions?type=org.camaraproject.device-status.v1.roaming-status&expiresAt.lt=2023-03-17 |
GET | /subscriptions/{subscriptionId} |
Operation to retrieve an event subscription (**) |
DELETE | /subscriptions/{subscriptionId} |
Operation to delete an event subscription (***) |
Notes:
(*) As the subscription could be created synchronously or asynchronously both status codes 201 and 202 must be described in the OpenAPI specification.
(**) If the GET /subscriptions/{subscriptionId}
is not able to retrieve a recently created subscription in asynchronous mode, a 404 code is sent back.
(***) As the subscription deletion could be handled synchronously or asynchronously both status codes 202 and 204 must be described in the OpenAPI specification.
Note on the operation path:
The recommended pattern is to use /subscriptions
path for the subscription operation. But API design team, for specific case, has the option to append /subscriptions
path with a prefix (e.g. /roaming/subscriptions
and /connectivity/subscriptions
). The rationale for using this alternate pattern should be explicitly provided (e.g. the notification source for each of the supported events may be completely different, in which case separating the implementations is beneficial).
The Following table provides /subscriptions
attributes
name | type | attribute description | cardinality |
---|---|---|---|
webhook | object | detail for event channel - in current version only webhook description are provided, but other event channel descriptor could be added in future | mandatory |
subscriptionId | string | Identifier of the event subscription - This attribute must not be present in the POST request as it is provided by API server | mandatory in server response |
subscriptionExpireTime | string - date-time | Date when the event subscription should end. Provided by API requester. The Server may reject the subscription if the period requested do not comply with Telco Operator policies (i.e. to avoid unlimited time subscriptions). In this case server returns exception 403 "SUBSCRIPTION_PERIOD_NOT_ALLOWED" | optional |
startsAt | string - date-time | Date when the event subscription will begin/began. This attribute must not be present in the POST request as it is provided by API server. It must be present in GET endpoints |
optional |
expiresAt | string - date-time | Date when the event subscription will expire. This attribute must not be present in the POST request as it is provided by API server. |
optional |
subscriptionMaxEvents | integer | Identifies the maximum number of event reports to be generated (>=1) - Once this number is reached, the subscription ends | optional |
subscriptionDetail | object | Object defined for each event subscription depending on the event - it could be for example the ueID targeted by the subscription | optional |
The webhook
object definition:
name | type | attribute description | cardinality |
---|---|---|---|
notificationUrl | string | https callback address where the event notification must be POST-ed | mandatory |
notificationAuthToken | string | OAuth2 token to be used by the callback API endpoint. It MUST be indicated within HTTP Authorization header e.g. Authorization: Bearer $notificationAuthToken | optional |
The subscriptionDetail
must have at least a type attribute:
name | type | attribute description | cardinality |
---|---|---|---|
type | string | Type of event subscribed. This attribute must be present in the POST request. It is required to provide an enum for this attribute. type must follow the format: org.camaraproject.<api-name>.<api-version>.<event-name> with the api-version with letter v and the major version like org.camaraproject.device-status.v1.roaming-status |
mandatory |
Error definition described in this guideline applies for subscriptions.
Following Error code must be present:
- for
POST
: 400, 401, 403, 409, 500, 503 - for
GET
: 400, 401, 403, 500, 503 - for
GET .../{subscriptionId}
: 400, 401, 403, 404, 500, 503 - for
DELETE
: 400, 401, 403, 404, 500, 503
4 scenarios of subscription termination are possible (business conditions may apply):
- case1: subscriptionExpireTime has been provided in the request and reached. The operator in this case has to terminate the subscription.
- case2: subscriptionMaxEvents has been provided in the request and reached. The operator in this case has to terminate the subscription.
- case3: subscriber requests the
DELETE
operation for the subscription (if the subscription did not have a subscriptionExpireTime or before subscriptionExpireTime expires). - case4: subscription ends on operator request.
It could be useful to provide a mechanism to inform subscriber for all cases. In this case, a specific event type could be used.
Termination rules regarding subscriptionExpireTime & subscriptionMaxEvents usage
- When client side providing a
subscriptionExpireTime
and/orsubscriptionMaxEvents
service side has to terminate the subscription without expecting aDELETE
operation. - If both
subscriptionExpireTime
andsubscriptionMaxEvents
are provided, the subscription will end when the first one is reached. - When none
subscriptionExpireTime
andsubscriptionMaxEvents
are not provided, client side has to trigger aDELETE
operation to terminate it. - It is perfectly valid for client side to trigger a DELETE operation before
subscriptionExpireTime
and/orsubscriptionMaxEvents
reached.
In this example, we illustrate a request for a device roaming status event subscription. Requester did not provide an anticipated expiration time for the subscription. In the response, server accepts this request and sets an event subscription end one year later. This is an illustration and each implementation is free to provide - or not - a subscription planned expiration date.
Request:
curl -X 'POST' \
'http://localhost:9091//device-status/v0/subscriptions' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d
{
"webhook": {
"notificationUrl": "https://application-server.com",
"notificationAuthToken": "c8974e592c2fa383d4a3960714"
}
"subscriptionDetail": {
"ueId": {,
"ipv4Addr": "192.168.0.1"
},
"uePort": 5060,
"type": "org.camaraproject.device-status.v1.roaming-status"
}
}
response:
201 Created
{
"webhook": {
"notificationUrl": "https://application-server.com",
"notificationAuthToken": "c8974e592c2fa383d4a3960714"
}
"subscriptionDetail": {
"ueId": {
"ipv4Addr": "192.168.0.1"
},
"uePort": 5060,
"type": "org.camaraproject.device-status.v1.roaming-status"
},
"subscriptionId": "456g899g",
"startsAt": "2023-03-17T16:02:41.314Z",
"expiresAt" : "2024-03-17T00:00:00.000Z"
}
Note: If the API provides both patterns (indirect and resource-based), and the API customer requests both (instance based + subscription), the 2 requests should be handled independently & autonomously. Depending on server implementation, it is acceptable, when the event occurs, that one or two notifications are sent to listener.
The event notification endpoint is used by the API server to notify the API consumer that an event occurred.
CAMARA event notification leverages CloudEvents and is based on release 1.0.2 as it is a vendor-neutral specification for defining the format of event data. A generic neutral CloudEvent notification OpenAPI specification is available in Commonalities/artifact directory (notification-as-cloud-event.yaml).
Note: The notification is the message posted on listener side. We describe the notification(s) in the CAMARA API using the callbacks
. From API consumer it could be confusing because this endpoint must be implemented on the business API consumer side. This notice should be explicitly mentioned in all CAMARA API documentation featuring notifications.
Only Operation POST is provided for event notification and the expected response code is 204
.
The URL for this POST
operation must be specified in the swagger as {$request.body#/webhook/notificationUrl}
.
The event notification is represented in the JavaScript Object Notation (JSON) Data Interchange Format (RFC8259). Such CloudEvents representation must use the media type application/cloudevents+json
.
For consistency across CAMARA APIs, the uniform CloudEvents model must be used with following rules:
name | type | attribute description | cardinality |
---|---|---|---|
id | string | identifier of this event, that must be unique in the source context. | mandatory |
source | string - URI | identifies the context in which an event happened in the specific Provider Implementation. Often this will include information such as the type of the event source, the organization publishing the event or the process that produced the event. The exact syntax and semantics behind the data encoded in the URI is defined by the event producer. | mandatory |
type | string | a value describing the type of event related to the originating occurrence. For consistency across API we mandate following pattern: org.camaraproject.<api-name>.<api-version>.<event-name> with the api-version with letter 'v' and the major version like example org.camaraproject.device-status.v1.roaming-status |
mandatory |
specversion | string | version of the specification to which this event conforms - must be "1.0". As design guideline, this field will be modeled as an enum. | mandatory |
datacontenttype | string | media-type that describes the event payload encoding, must be application/json for CAMARA APIs |
optional |
subject | string | describes the subject of the event - Not used in CAMARA notification. | optional |
time | string date-time | Timestamp of when the occurrence happened. If the time of the occurrence cannot be determined then this attribute MAY be set to some other time (such as the current time) by the CloudEvents producer, however all producers for the same source MUST be consistent in this respect. In other words, either they all use the actual time of the occurrence or they all use the same algorithm to determine the value used. (must adhere to CAMARA date-time recommendation based on RFC 3339) |
mandatory (*) |
data | object | event notification details payload described in each CAMARA API and referenced by its type |
optional (**) |
(*) Note: Attribute time
is tagged as optional in CloudEvents specification, but from CAMARA perspective we mandate to value this attributes.
(**) Event data (domain-specific information about the occurrence) are encapsulated within data
object, its occurence can be set to mandatory by given CAMARA API and its structure is dependant on each API:
name | type | attribute description | cardinality |
---|---|---|---|
subscriptionId | string | The event subscription identifier - must be valued for Resource-based subscription | optional |
... | ... | Specific attribute(s) related to the notification event | ... |
Note: For operational and troubleshooting purposes it is relevant to accommodate use of X-Correlator
header attribute. API listener implementations have to be ready to support and receive this data.
Specific event notification type "subscription-ends" is defined to inform listener about subscription termination. It is used when the subscriptionExpireTime
or subscriptionMaxEvents
has been reached, or, if the API server has to stop sending notification prematurely. For this specific event, the data
must feature terminationReason
attribute.
Note: The "subscription-ends" notification is not counted in the subscriptionMaxEvents
. (for example if a client request a subscriptionMaxEvents
to 2, later, received 2 notification, then a third notification will be send for "subscription-ends")
Error definition are described in this guideline applies for event notification.
Following Error code must be present:
- for
POST
: 400, 401, 403, 500, 503
To manage correlation between the subscription management and the event notification (as these are 2 distinct operations):
- use
subscriptionId
attribute (indata
structure in the body) - this identifier is provided in event subscription and could be valued in each event notification.
Note: There is no normative enforcement to use any of these patterns and they could be used on agreement between API consumer & provider.
As notification may carry sensitive information, privacy and security constraints has to be considered. CloudEvents specification provides some guidance there: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#privacy-and-security
Any system that allows registration of and delivery of notifications to arbitrary HTTP endpoints can potentially be abused such that someone maliciously or inadvertently registers the address of a system that does not expect such requests and for which the registering party is not authorized to perform such a registration.
In order to protect the sender, CloudEvents specification provides some guidance there: https://github.com/cloudevents/spec/blob/main/cloudevents/http-webhook.md#4-abuse-protection
Event Producers shall choose based on their internal security guidelines to implement measures based on the above guidance to ensure abuse protection. For e.g. An event producer might ask the subscriber to pre-register the notification URL at the time of app onboarding. If this registered notification URL doesn't match later with the notification URL in the request, the event producer can choose to reject the request with the relevant error code.
Example for Roaming status event notification - Request:
curl -X 'POST' \
'https://application-server.com/v0/notifications' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer c8974e592c2fa383d4a3960714' \
-H 'Content-Type: application/cloudevents+json' \
-d
{
"id": 123654,
"source": "https://notificationSendServer12.supertelco.com",
"type": "org.camaraproject.device-status.v1.roaming-status",
"specversion": "1.0",
"datacontenttype": "application/json",
"data": {
"subscriptionId": "456g899g",
"device": {
"phoneNumber": "+123456789"
},
"roaming": true,
"countryCode": 208,
"countryName": "FR"
},
"time": "2023-01-17T13:18:23.682Z"
}
response:
204 No Content
Example for subscription termination - Request:
curl -X 'POST' \
'https://application-server.com/v0/notifications' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer c8974e592c2fa383d4a3960714' \
-H 'Content-Type: application/cloudevents+json' \
-d
{
"id": 123658,
"source": "https://notificationSendServer12.supertelco.com",
"type": "org.camaraproject.api.device-status.v1.subcription-ends",
"specversion": "1.0",
"datacontenttype": "application/json",
"data": {
"subscriptionId": "456g899g",
"device": {
"phoneNumber": "+123456789"
},
"terminationReason": "SUBSCRIPTION_EXPIRED"
},
"time": "2023-01-19T13:18:23.682Z"
}
response:
204 No Content