diff --git a/VERSION.yaml b/VERSION.yaml new file mode 100644 index 00000000..b0f22b7e --- /dev/null +++ b/VERSION.yaml @@ -0,0 +1 @@ +version: wip diff --git a/artifacts/CAMARA_common.yaml b/artifacts/CAMARA_common.yaml index 62504590..9a032d15 100644 --- a/artifacts/CAMARA_common.yaml +++ b/artifacts/CAMARA_common.yaml @@ -5,11 +5,15 @@ info: license: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0.html - version: 0.5.0 - x-camara-commonalities: 0.4.0 + version: wip + x-camara-commonalities: wip paths: {} components: + securitySchemes: + openId: + type: openIdConnect + openIdConnectUrl: https://example.com/.well-known/openid-configuration headers: x-correlator: description: Correlation id for the different services @@ -127,7 +131,133 @@ components: format: ipv6 example: 2001:db8:85a3:8d3:1319:8a2e:370:7344 + Area: + description: Base schema for all areas + type: object + properties: + areaType: + $ref: "#/components/schemas/AreaType" + required: + - areaType + discriminator: + propertyName: areaType + mapping: + CIRCLE: "#/components/schemas/Circle" + POLYGON: "#/components/schemas/Polygon" + + AreaType: + type: string + description: | + Type of this area. + CIRCLE - The area is defined as a circle. + POLYGON - The area is defined as a polygon. + enum: + - CIRCLE + - POLYGON + + Circle: + description: Circular area + allOf: + - $ref: "#/components/schemas/Area" + - type: object + required: + - center + - radius + properties: + center: + $ref: "#/components/schemas/Point" + radius: + type: number + description: Distance from the center in meters + minimum: 1 + + Polygon: + description: Polygonal area. The Polygon should be a simple polygon, i.e. should not intersect itself. + allOf: + - $ref: "#/components/schemas/Area" + - type: object + required: + - boundary + properties: + boundary: + $ref: "#/components/schemas/PointList" + + PointList: + description: List of points defining a polygon + type: array + items: + $ref: "#/components/schemas/Point" + minItems: 3 + maxItems: 15 + + Point: + type: object + description: Coordinates (latitude, longitude) defining a location in a map + required: + - latitude + - longitude + properties: + latitude: + $ref: "#/components/schemas/Latitude" + longitude: + $ref: "#/components/schemas/Longitude" + example: + latitude: 50.735851 + longitude: 7.10066 + + Latitude: + description: Latitude component of a location + type: number + format: double + minimum: -90 + maximum: 90 + + Longitude: + description: Longitude component of location + type: number + format: double + minimum: -180 + maximum: 180 + responses: +####################################################### +####################################################### +# ERROR RESPONSE SCHEMA TEMPLATE +# - Objective: Make normative error `status` and `code` values +# - Schema Template rationale: +# - The `allOf` in content.application/json.schema allows a combination of both the generic ErrorInfo schema and the specific schema for this error response, +# which validates that `status` and `code` have only the specified values. +# This `allOf` is used without discriminator because it does not imply any hierarchy between the models, just 2 schemas that must be independently validated. +####################################################### +# ErrorResponseSchema: +# ... +# content: +# application/json: +# schema: +# allOf: +# - $ref: '#/components/schemas/ErrorInfo' +# - type: object +# properties: +# status: +# enum: +# - +# code: +# enum: +# - +# - +# examples: +# ExampleKey1: +# value: +# status: +# code: +# message: +# ExampleKey2: +# value: +# status: +# code: +# message: +####################################################### +####################################################### Generic400: description: Bad Request headers: @@ -136,7 +266,18 @@ components: content: application/json: schema: - $ref: "#/components/schemas/ErrorInfo" + allOf: + - $ref: "#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - 400 + code: + enum: + - INVALID_ARGUMENT + - OUT_OF_RANGE + - "{{SPECIFIC_CODE}}" examples: GENERIC_400_INVALID_ARGUMENT: description: Invalid Argument. Generic Syntax Exception @@ -164,7 +305,17 @@ components: content: application/json: schema: - $ref: "#/components/schemas/ErrorInfo" + allOf: + - $ref: "#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - 401 + code: + enum: + - UNAUTHENTICATED + - AUTHENTICATION_REQUIRED examples: GENERIC_401_UNAUTHENTICATED: description: Request cannot be authenticated @@ -186,7 +337,18 @@ components: content: application/json: schema: - $ref: "#/components/schemas/ErrorInfo" + allOf: + - $ref: "#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - 403 + code: + enum: + - PERMISSION_DENIED + - INVALID_TOKEN_CONTEXT + - "{{SPECIFIC_CODE}}" examples: GENERIC_403_PERMISSION_DENIED: description: Permission denied. OAuth2 token access does not have the required scope or when the user fails operational security @@ -214,7 +376,18 @@ components: content: application/json: schema: - $ref: "#/components/schemas/ErrorInfo" + allOf: + - $ref: "#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - 404 + code: + enum: + - NOT_FOUND + - IDENTIFIER_NOT_FOUND + - "{{SPECIFIC_CODE}}" examples: GENERIC_404_NOT_FOUND: description: Resource is not found @@ -222,11 +395,11 @@ components: status: 404 code: NOT_FOUND message: The specified resource is not found. - GENERIC_404_DEVICE_NOT_FOUND: + GENERIC_404_IDENTIFIER_NOT_FOUND: description: Device identifier not found value: status: 404 - code: DEVICE_NOT_FOUND + code: IDENTIFIER_NOT_FOUND message: Device identifier not found. GENERIC_404_{{SPECIFIC_CODE}}: description: Specific situation to highlight the resource/concept not found @@ -242,7 +415,16 @@ components: content: application/json: schema: - $ref: "#/components/schemas/ErrorInfo" + allOf: + - $ref: "#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - 405 + code: + enum: + - METHOD_NOT_ALLOWED examples: GENERIC_405_METHOD_NOT_ALLOWED: description: Invalid HTTP verb used with a given endpoint @@ -258,7 +440,16 @@ components: content: application/json: schema: - $ref: "#/components/schemas/ErrorInfo" + allOf: + - $ref: "#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - 406 + code: + enum: + - NOT_ACCEPTABLE examples: GENERIC_406_NOT_ACCEPTABLE: description: API Server does not accept the media type (`Accept-*` header) indicated by API client @@ -274,7 +465,19 @@ components: content: application/json: schema: - $ref: "#/components/schemas/ErrorInfo" + allOf: + - $ref: "#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - 409 + code: + enum: + - ABORTED + - ALREADY_EXISTS + - CONFLICT + - "{{SPECIFIC_CODE}}" examples: GENERIC_409_ABORTED: description: Concurreny of processes of the same nature/scope @@ -308,7 +511,16 @@ components: content: application/json: schema: - $ref: "#/components/schemas/ErrorInfo" + allOf: + - $ref: "#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - 410 + code: + enum: + - GONE examples: GENERIC_410_GONE: description: Use in notifications flow to allow API Consumer to indicate that its callback is no longer available @@ -324,10 +536,19 @@ components: content: application/json: schema: - $ref: "#/components/schemas/ErrorInfo" + allOf: + - $ref: "#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - 412 + code: + enum: + - FAILED_PRECONDITION examples: GENERIC_412_FAILED_PRECONDITION: - description: Use in notifications flow to allow API Consumer to indicate that its callback is no longer available + description: Indication by the API Server that the request cannot be processed in current system state value: status: 412 code: FAILED_PRECONDITION @@ -340,7 +561,16 @@ components: content: application/json: schema: - $ref: "#/components/schemas/ErrorInfo" + allOf: + - $ref: "#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - 415 + code: + enum: + - UNSUPPORTED_MEDIA_TYPE examples: GENERIC_415_UNSUPPORTED_MEDIA_TYPE: description: Payload format of the request is in an unsupported format by the Server. Should not happen @@ -356,26 +586,52 @@ components: content: application/json: schema: - $ref: "#/components/schemas/ErrorInfo" + allOf: + - $ref: "#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - 422 + code: + enum: + - IDENTIFIER_MISMATCH + - SERVICE_NOT_APPLICABLE + - MISSING_IDENTIFIER + - UNSUPPORTED_IDENTIFIER + - UNNECESSARY_IDENTIFIER + - "{{SPECIFIC_CODE}}" examples: - GENERIC_422_DEVICE_IDENTIFIERS_MISMATCH: - description: Inconsistency between device identifiers not pointing to the same device + GENERIC_422_IDENTIFIER_MISMATCH: + description: Inconsistency between identifiers not pointing to the same device value: status: 422 - code: DEVICE_IDENTIFIERS_MISMATCH - message: Provided device identifiers are not consistent. - GENERIC_422_DEVICE_NOT_APPLICABLE: - description: Service is not available for the provided device + code: IDENTIFIER_MISMATCH + message: Provided identifiers are not consistent. + GENERIC_422_SERVICE_NOT_APPLICABLE: + description: Service not applicable for the provided identifier value: status: 422 - code: DEVICE_NOT_APPLICABLE - message: The service is not available for the provided device. - GENERIC_422_UNIDENTIFIABLE_DEVICE: - description: The device identifier is not included in the request and the device information cannot be derived from the 3-legged access token + code: SERVICE_NOT_APPLICABLE + message: The service is not available for the provided identifier. + GENERIC_422_MISSING_IDENTIFIER: + description: An identifier is not included in the request and the device or phone number identification cannot be derived from the 3-legged access token value: status: 422 - code: UNIDENTIFIABLE_DEVICE + code: MISSING_IDENTIFIER message: The device cannot be identified. + GENERIC_422_UNSUPPORTED_IDENTIFIER: + description: None of the provided identifiers is supported by the implementation + value: + status: 422 + code: UNSUPPORTED_IDENTIFIER + message: The identifier provided is not supported. + GENERIC_422_UNNECESSARY_IDENTIFIER: + description: An explicit identifier is provided when a device or phone number has already been identified from the access token + value: + status: 422 + code: UNNECESSARY_IDENTIFIER + message: The device is already identified by the access token. GENERIC_422_{{SPECIFIC_CODE}}: description: Any semantic condition associated to business logic, specifically related to a field or data structure value: @@ -390,7 +646,17 @@ components: content: application/json: schema: - $ref: "#/components/schemas/ErrorInfo" + allOf: + - $ref: "#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - 429 + code: + enum: + - QUOTA_EXCEEDED + - TOO_MANY_REQUESTS examples: GENERIC_429_QUOTA_EXCEEDED: description: Request is rejected due to exceeding a business quota limit @@ -412,7 +678,16 @@ components: content: application/json: schema: - $ref: "#/components/schemas/ErrorInfo" + allOf: + - $ref: "#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - 500 + code: + enum: + - INTERNAL examples: GENERIC_500_INTERNAL: description: Problem in Server side. Regular Server Exception @@ -428,7 +703,16 @@ components: content: application/json: schema: - $ref: "#/components/schemas/ErrorInfo" + allOf: + - $ref: "#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - 501 + code: + enum: + - NOT_IMPLEMENTED examples: GENERIC_501_NOT_IMPLEMENTED: description: Service not implemented. The use of this code should be avoided as far as possible to get the objective to reach aligned implementations @@ -444,7 +728,16 @@ components: content: application/json: schema: - $ref: "#/components/schemas/ErrorInfo" + allOf: + - $ref: "#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - 502 + code: + enum: + - BAD_GATEWAY examples: GENERIC_502_BAD_GATEWAY: description: Internal routing problem in the Server side that blocks to manage the service properly @@ -460,7 +753,16 @@ components: content: application/json: schema: - $ref: "#/components/schemas/ErrorInfo" + allOf: + - $ref: "#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - 503 + code: + enum: + - SERVICE_UNAVAILABLE examples: GENERIC_503_UNAVAILABLE: description: Service is not available. Temporary situation usually related to maintenance process in the server side @@ -476,7 +778,16 @@ components: content: application/json: schema: - $ref: "#/components/schemas/ErrorInfo" + allOf: + - $ref: "#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - 504 + code: + enum: + - TIMEOUT examples: GENERIC_504_TIMEOUT: description: API Server Timeout diff --git a/artifacts/camara-cloudevents/event-subscription-template.yaml b/artifacts/camara-cloudevents/event-subscription-template.yaml index e28f39e8..98769daf 100644 --- a/artifacts/camara-cloudevents/event-subscription-template.yaml +++ b/artifacts/camara-cloudevents/event-subscription-template.yaml @@ -4,7 +4,9 @@ info: description: | This file is a template for CAMARA API explicit subscription endpoint and for Notification model. Additional information are provided in API Design Guidelines document. - Note on ``security`` - ``openId`` scope: The value in this yaml `api-name:event-type:grant-level` must be replaced accordingly to the format defined in the guideline document. + Note on event name convention: the event type name MUST follow: ``org.camaraproject...`` + + Note on ``security`` - ``openId`` scope: The value in this yaml `api-name:event-type1:grant-level` must be replaced accordingly to the format defined in the guideline document. license: name: Apache 2.0 @@ -16,14 +18,12 @@ externalDocs: description: Product documentation at CAMARA url: https://github.com/camaraproject/ servers: - - url: "{apiRoot}/{apiName}/v0.1" + - url: "{apiRoot}/api-name/vx.y" + # api-name and version should valued accordingly to the API variables: apiRoot: default: http://localhost:9091 - description: API root - apiName: - default: camaraAPI - description: apiName will be replaced in WG by its value and not modelled as a variable + description: API root, defined by the service provider, e.g. `api.example.com` or `api.example.com/somepath` tags: - name: Subscription description: Operations to manage event subscriptions on event-type event @@ -40,7 +40,8 @@ paths: - $ref: "#/components/parameters/x-correlator" security: - openId: - - api-name:event-type:grant-level + - api-name:event-type1:grant-level + - api-name:event-type2:grant-level requestBody: content: application/json: @@ -81,10 +82,6 @@ paths: $ref: "#/components/responses/Generic410" "429": $ref: "#/components/responses/Generic429" - "500": - $ref: "#/components/responses/Generic500" - "503": - $ref: "#/components/responses/Generic503" security: - {} - notificationsBearerAuth: [] @@ -116,16 +113,10 @@ paths: $ref: "#/components/responses/SubscriptionPermissionDenied403" "409": $ref: "#/components/responses/Generic409" - "415": - $ref: "#/components/responses/Generic415" "422": $ref: "#/components/responses/CreateSubscriptionUnprocessableEntity422" "429": $ref: "#/components/responses/Generic429" - "500": - $ref: "#/components/responses/Generic500" - "503": - $ref: "#/components/responses/Generic503" get: tags: - Subscription @@ -156,10 +147,6 @@ paths: $ref: "#/components/responses/Generic401" "403": $ref: "#/components/responses/Generic403" - "500": - $ref: "#/components/responses/Generic500" - "503": - $ref: "#/components/responses/Generic503" /subscriptions/{subscriptionId}: get: tags: @@ -191,10 +178,6 @@ paths: $ref: "#/components/responses/Generic403" "404": $ref: "#/components/responses/Generic404" - "500": - $ref: "#/components/responses/Generic500" - "503": - $ref: "#/components/responses/Generic503" delete: tags: - Subscription @@ -230,10 +213,6 @@ paths: $ref: "#/components/responses/Generic403" "404": $ref: "#/components/responses/Generic404" - "500": - $ref: "#/components/responses/Generic500" - "503": - $ref: "#/components/responses/Generic503" components: securitySchemes: openId: @@ -301,13 +280,12 @@ components: types: description: | Camara Event types eligible to be delivered by this subscription. - Note: for the Commonalities meta-release v0.4 we enforce to have only event type per subscription then for following meta-release use of array MUST be decided - at API project level. + Note: for the current Commonalities version (v0.5) only one event type per subscription is allowed, yet in the following releases use of array of event types SHALL be specified without changing this definition. type: array minItems: 1 maxItems: 1 items: - type: string + $ref: "#/components/schemas/SubscriptionEventType" config: $ref: "#/components/schemas/Config" discriminator: @@ -453,13 +431,22 @@ components: EventTypeNotification: type: string description: | - event-type - Event triggered when an event-type event occurred + event-type - Event triggered when an event-type event occurred. Several event-type could be defined. subscription-ends: Event triggered when the subscription ends enum: - - org.camaraproject.api-name.v0.event-type + - org.camaraproject.api-name.v0.event-type1 + - org.camaraproject.api-name.v0.event-type2 - org.camaraproject.api-name.v0.subscription-ends + SubscriptionEventType: + type: string + description: | + event-type that could be subscribed through this subscription. Several event-type could be defined. + enum: + - org.camaraproject.api-name.v0.event-type1 + - org.camaraproject.api-name.v0.event-type2 + Subscription: description: Represents a event-type subscription. type: object @@ -572,7 +559,8 @@ components: discriminator: propertyName: "type" mapping: - org.camaraproject.api-name.v0.event-type: "#/components/schemas/Event-typeEvent" + org.camaraproject.api-name.v0.event-type1: "#/components/schemas/EventApiSpecific1" + org.camaraproject.api-name.v0.event-type2: "#/components/schemas/EventApiSpecific2" org.camaraproject.api-name.v0.subscription-ends: "#/components/schemas/EventSubscriptionEnds" Source: @@ -597,8 +585,14 @@ components: description: Timestamp of when the occurrence happened. Must adhere to RFC 3339. example: "2018-04-05T17:31:00Z" - Event-typeEvent: - description: event structure for event-type event + EventApiSpecific1: + description: event structure for event-type event 1 + allOf: + - $ref: "#/components/schemas/CloudEvent" + - type: object + + EventApiSpecific2: + description: event structure for event-type event 2 allOf: - $ref: "#/components/schemas/CloudEvent" - type: object @@ -965,22 +959,6 @@ components: status: 410 code: GONE message: Access to the target resource is no longer available. - Generic415: - description: Unsupported Media Type - headers: - X-Correlator: - $ref: "#/components/headers/x-correlator" - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorInfo" - examples: - GENERIC_415_UNSUPPORTED_MEDIA_TYPE: - description: Payload format of the request is in an unsupported format by the Server. Should not happen - value: - status: 415 - code: UNSUPPORTED_MEDIA_TYPE - message: The server refuses to accept the request because the payload format is in an unsupported format CreateSubscriptionUnprocessableEntity422: description: Unprocessable Entity headers: @@ -1030,38 +1008,6 @@ components: status: 429 code: TOO_MANY_REQUESTS message: Either out of resource quota or reaching rate limiting. - Generic500: - description: Server error - headers: - x-correlator: - $ref: "#/components/headers/x-correlator" - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorInfo" - examples: - GENERIC_500_INTERNAL: - description: Problem in Server side. Regular Server Exception - value: - status: 500 - code: INTERNAL - message: Unknown server error. Typically a server bug. - Generic503: - description: Service unavailable. Typically the server is down. - headers: - x-correlator: - $ref: "#/components/headers/x-correlator" - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorInfo" - examples: - GENERIC_503_UNAVAILABLE: - description: Service is not available. Temporary situation usually related to maintenance process in the server side - value: - status: 503 - code: UNAVAILABLE - message: Service Unavailable. SubscriptionIdRequired: description: Problem with the client request headers: diff --git a/documentation/API-design-guidelines.md b/documentation/API-design-guidelines.md index 40a15f19..4f169c6a 100644 --- a/documentation/API-design-guidelines.md +++ b/documentation/API-design-guidelines.md @@ -13,7 +13,7 @@ This document captures guidelines for the API design in CAMARA project. These gu - [2.2 API First](#22-api-first) - [2.3 Interface standardization. Standardization fora.](#23-interface-standardization-standardization-fora) - [2.4 Information Representation Standard](#24-information-representation-standard) - - [2.5 Reduce telco-specific terminology in API definitions](#25-reduce-telco-specific-terminology-in-api-definitions) + - [2.5 Reduce telco-specific terminology in API definitions](#25-reduce-telco-specific-terminology-in-api-definitions) - [3. API Definition](#3-api-definition) - [3.1 API REST](#31-api-rest) - [POST or GET for transferring sensitive or complex data](#post-or-get-for-transferring-sensitive-or-complex-data) @@ -66,22 +66,27 @@ This document captures guidelines for the API design in CAMARA project. These gu - [Examples](#examples) - [APIs which deal with explicit subscriptions](#apis-which-deal-with-explicit-subscriptions) - [API-level scopes (sometimes referred to as wildcard scopes in CAMARA)](#api-level-scopes-sometimes-referred-to-as-wildcard-scopes-in-camara) + - [11.7 Resource access restriction](#117-resource-access-restriction) - [12. Subscription, Notification \& Event](#12-subscription-notification--event) - [12.1 Subscription](#121-subscription) - [Instance-based (implicit) subscription](#instance-based-implicit-subscription) - [Instance-based (implicit) subscription example](#instance-based-implicit-subscription-example) - [Resource-based (explicit) subscription](#resource-based-explicit-subscription) + - [Operations](#operations) + - [Rules for subscriptions data minimization](#rules-for-subscriptions-data-minimization) + - [Subscriptions data model](#subscriptions-data-model) - [Error definition for resource-based (explicit) subscription](#error-definition-for-resource-based-explicit-subscription) - [Termination for resource-based (explicit) subscription](#termination-for-resource-based-explicit-subscription) - [Resource-based (explicit) example](#resource-based-explicit-example) - [12.2 Event notification](#122-event-notification) - [Event notification definition](#event-notification-definition) + - [subscription-ends event](#subscription-ends-event) - [Error definition for event notification](#error-definition-for-event-notification) - [Correlation Management](#correlation-management) - [Security Considerations](#security-considerations) - [Abuse Protection](#abuse-protection) - [Notification examples](#notification-examples) - - [Appendix A: `info.description` template for `device` identification from access token](#appendix-a-infodescription-template-for-device-identification-from-access-token) + - [Appendix A (Normative): `info.description` template for when User identification can be from either an access token or explicit identifier](#appendix-a-normative-infodescription-template-for-when-user-identification-can-be-from-either-an-access-token-or-explicit-identifier) ## Common Vocabulary and Acronyms @@ -89,7 +94,8 @@ This document captures guidelines for the API design in CAMARA project. These gu | **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. | +|**api-name** | `api-name` is a kebab-case string used to create unique names or values of objects and parameters related to given API. For example, for Device Location Verification API, api-name is `location-verification`.| +| **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:
  • Upper Camel Case: When the first letter of each word is capital.
  • Lower Camel Case: Same to that Upper one, but with the first word in lowercase.
  • | | **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. | @@ -551,23 +557,25 @@ API versions use a numbering scheme in the format: `x.y.z` ### 5.1 API version (OAS info object) -The API version is defined in the `version` field (in the `info` object) of the OAS definition file of an API.  +The API version is defined in the `version` field (in the `info` object) of the OAS definition file of an API. ```yaml info: title: Number Verification description: text describing the API - version: 2.2.0   + version: 2.2.0 #... ``` -In line with Semantic Versioning 2.0.0, the API with MAJOR.MINOR.PATCH version number, increments as follows: +In line with Semantic Versioning 2.0.0, the API with MAJOR.MINOR.PATCH version number, increments as follows: -1. The MAJOR version when an incompatible / breaking API change is introduced -2. The MINOR version when functionality is added that is backwards compatible -3. The PATCH version when backward compatible bugs are fixed +1. The MAJOR version when an incompatible / breaking API change is introduced +2. The MINOR version when functionality is added that is backwards compatible +3. The PATCH version when backward compatible bugs are fixed + + +For more details on MAJOR, MINOR and PATCH versions, and how to evolve API versions, please see [API versioning](https://lf-camaraproject.atlassian.net/wiki/x/3yLe) in the CAMARA wiki.  -For more details on MAJOR, MINOR and PATCH versions, and how to evolve API versions, please see [API versioning](https://wiki.camaraproject.org/x/a4BaAQ) in the CAMARA wiki.  It is recommended to avoid breaking backward compatibility unless strictly necessary: new versions should be backwards compatible with previous versions. More information on how to avoid breaking changes can be found below. @@ -579,7 +587,7 @@ The API version in the `url` field only includes the "x" (MAJOR version) number ```yaml servers: -    url: {apiRoot}/qod/v2 + url: {apiRoot}/qod/v2 ``` --- @@ -590,7 +598,7 @@ IMPORTANT: CAMARA public APIs with x=0 (`v0.x.y`) MUST use both the MAJOR and th ```yaml servers: -    url: {apiRoot}/number-verification/v0.3 + url: {apiRoot}/number-verification/v0.3 ``` This allows for both test and commercial usage of initial API versions as they are evolving rapidly, e.g. `/qod/v0.10alpha1`, `/qod/v0.10rc1`, or `/qod/v0.10`. However, it should be acknowledged that any initial API version may change. @@ -604,7 +612,7 @@ Overall, an API can have any of the following versions: * work-in-progress (`wip`) API versions used during the development of an API before the first pre-release or in between pre-releases. Such API versions cannot be released and are not usable by API consumers. * alpha (`x.y.z-alpha.m`) API versions (with extensions) for CAMARA internal API rapid development purposes * release-candidate (`x.y.z-rc.n`) API versions (with extensions) for CAMARA internal API release bug fixing purposes -* public (`x.y.z`) API versions for usage in commercial contexts. These API versions only have API version number x.y.z (semver 2.0), no extension. Public APIs can have one of two maturity states (used in release management):  +* public (`x.y.z`) API versions for usage in commercial contexts. These API versions only have API version number x.y.z (semver 2.0), no extension. Public APIs can have one of two maturity states (used in release management): * initial - indicating that the API is still not fully stable (x=0) * stable - indicate that the API has reached a certain level of maturity (x>0) @@ -614,7 +622,7 @@ The following table gives the values of the API version (Info object) and the AP |-------------------|:-------------:|:--------------------------------:|:-------------------------------:|:---------------------------:| | work-in-progress | wip | vwip | vwip | No | | alpha | x.y.z-alpha.m | v0.yalpham | vxalpham | Yes (internal pre-release) | -| release-candidate |  x.y.z-rc.n | v0.yrcn | vxrcn | Yes (internal pre-release) | +| release-candidate | x.y.z-rc.n | v0.yrcn | vxrcn | Yes (internal pre-release) | | public | x.y.z | v0.y | vx | Yes | Precedence examples: @@ -623,7 +631,7 @@ Precedence examples: * 0.1.0 < 0.2.0-alpha.1 < 0.2.0-alpha.2 < 0.2.0-rc.1 < 0.2.0-rc.2 < 0.2.0 (initial public API version) * 1.0.0 < 1.1.0-alpha.1 < 1.1.0-alpha.2 < 1.1.0-rc.1 < 1.1.0-rc.2 < 1.1.0 (stable public API version) -For more information, please see [API versioning](https://wiki.camaraproject.org/x/a4BaAQ) in the Release Management project Wiki. +For more information, please see [API versioning](https://lf-camaraproject.atlassian.net/wiki/x/3yLe) in the Release Management project wiki. ### 5.4 Backward and forward compatibility @@ -666,7 +674,7 @@ Compatibility management: To ensure this compatibility, the following guidelines must be applied. **As API provider**: -- Never change an endpoint name; instead, add a new one and mark the original one for deprecation in a MINOR change and remove it in a later MAJOR change (see semver FAQ entry: https://semver.org/#how-should-i-handle-deprecating-functionality) +- Never change an endpoint name; instead, add a new one and mark the original one for deprecation in a MINOR change and remove it in a later MAJOR change (see semver FAQ entry: https://semver.org/#how-should-i-handle-deprecating-functionality) - If possible, do the same for attributes - 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. @@ -701,6 +709,8 @@ An error representation must not be different from the representation of any res All these aforementioned fields are mandatory in Error Responses. `status` and `code` fields have normative nature, so as their use has to be standardized (see [Section 6.1](#61-standardized-use-of-camara-error-responses)). On the other hand, `message` is informative and within this document an example is shown. +Fields `status` and `code` values are normative (i.e. they have a set of allowed values), as defined in [CAMARA_common.yaml](../artifacts/CAMARA_common.yaml). + A JSON error structure is proposed below: ```json @@ -784,8 +794,8 @@ In the following, we elaborate on the existing client errors. In particular, we **Mandatory Errors** to be **documented in CAMARA API Spec YAML** are the following: -- For event notifications flow, the ones defined in [notification-as-cloud-event.yaml](/artifacts/notification-as-cloud-event.yaml) -- For event subscriptions APIs, the ones defined in [event-subscription-template.yaml](/artifacts/camara-cloudevents/event-subscription-template.yaml) +- For event subscriptions APIs, the ones defined in [12.1 Subscription](#error-definition-for-resource-based-explicit-subscription) +- For event notifications flow, the ones defined in [12.2 Event notification](#error-definition-for-event-notification) - For the rest of APIs: - Error status 401 - Error status 403 @@ -802,12 +812,12 @@ The Following table compiles the guidelines to be adopted: | **Case #** | **Description** | **Error status** | **Error code** | **Message example** | |:----------:|:---------------------------------------------------------------------------|:----------------:|:------------------------------:|:---------------------------------------------------------| | 0 | The request body does not comply with the schema | 400 | INVALID_ARGUMENT | Request body does not comply with the schema. | -| 1 | None of the provided device identifiers is supported by the implementation | 422 | UNSUPPORTED_IDENTIFIER | The identifier provided is not supported. | +| 1 | None of the provided identifiers is supported by the implementation | 422 | UNSUPPORTED_IDENTIFIER | The identifier provided is not supported. | | 2 | Some identifier cannot be matched to a device | 404 | IDENTIFIER_NOT_FOUND | Device identifier not found. | -| 3 | Device identifiers mismatch | 422 | IDENTIFIER_MISMATCH | Provided identifiers are not consistent. | -| 4 | An explicit identifier is provided when an API subject has already been identified from the access token | 422 | UNNECESSARY_IDENTIFIER | The device is already identified by the access token. | -| 5 | Service not applicable for the provided identifier | 422 | SERVICE_NOT_APPLICABLE | The service is not available for the provided identifier. | -| 6 | An identifier is not included in the request and the API subject cannot be derived from the 3-legged access token | 422 | MISSING_IDENTIFIER | The device cannot be identified. | +| 3 | Inconsistency between identifiers not pointing to the same device | 422 | IDENTIFIER_MISMATCH | Provided identifiers are not consistent. | +| 4 | An explicit identifier is provided when a device or phone number has already been identified from the access token | 422 | UNNECESSARY_IDENTIFIER | The device is already identified by the access token. | +| 5 | Service not applicable for the provided identifier | 422 | SERVICE_NOT_APPLICABLE | The service is not available for the provided identifier. | +| 6 | An identifier is not included in the request and the device or phone number identification cannot be derived from the 3-legged access token | 422 | MISSING_IDENTIFIER | The device cannot be identified. | @@ -826,7 +836,17 @@ headers: content: application/json: schema: - $ref: "#/components/schemas/ErrorInfo" + allOf: + - $ref: "#/components/schemas/ErrorInfo" + - type: object + properties: + status: + enum: + - + code: + enum: + - + - examples: {{case_1}}: $ref: ""#/components/examples/{{case_1}}" @@ -917,6 +937,12 @@ Filtering consists of restricting the number of resources queried by specifying 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. + +#### Security Considerations +As filtering may reveal sensitive information, privacy and security constraints have to be considered when defining query parameters, e.g. it should not be possible to filter using personal information (such as name, phone number or IP address). + + +#### Filtering operations | **Operation** | Numbers | Dates | |-------------------|------------------------------|-----------------------------------------------| @@ -929,28 +955,35 @@ Note: Services may not support all attributes for filtering. In case a query in 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` | +| **Operation** | **Strings/enums** | +| ----- | ----- | +| equal | `GET .../?type=mobile` | +| non equal | `GET .../?type!=mobile` | +| Contains | `GET .../?type=~str` | +For boolean parameters the filter can be set for True or False value: + +| **Operation** | **Booleans** | +|---------------|-----------------------| +| True | `GET .../?boolAttr=true` | +| False | `GET .../?boolAttr=false` | **Additional rules**: - The operator "`&`" is evaluated as an AND between different attributes. - A Query Param (attribute) can contain one 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 the first name "david" and last name "munoz": - - `GET /users?name=david&surname=munoz` - - `GET /users?name=David,Noelia` +- Equals: to search devices with a particular operating system and version or type: + - `GET /device?os=ios&version=17.0.1` + - `GET /device?type=apple,android` - 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" + - `GET /device?version=17.0.1` + - Search for the exact version "17.0.1" + - `GET /device?version=~17.0` + - Look for version strings that include "17.0" - Greater than / less than: new attributes need to be created with the suffixes `.(gte|gt|lte|lt)$` and included in `get` operation : ```yaml paths: @@ -1233,7 +1266,7 @@ API documentation usually consists of: - 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](https://spec.openapis.org/oas/v3.0.3) using the .yaml or .json file extension. +- The API functionalities must be implemented following the specifications of the [Open API version 3.0.3](https://spec.openapis.org/oas/v3.0.3) using `api-name` as the filename and the `.yaml` or `.json` file extension. - The API specification structure should have the following parts: - General information ([Section 11.1](#111-general-information)) - Published Routes ([Section 11.2](#112-published-routes)) @@ -1310,6 +1343,7 @@ servers: default: http://localhost:9091 description: API root, defined by the service provider, e.g. `api.example.com` or `api.example.com/somepath` ``` +If more than one server object instance is listed, the `servers[*].url` property of each instance must have the same string for the `api-name` and `api-version`'. ### 11.2 Published Routes @@ -1474,11 +1508,11 @@ In general, all APIs must be secured to ensure who has access to what and for wh Camara uses OIDC and CIBA for authentication and consent collection and to determine whether the user has, e.g. opted out of some API access. -The [Camara Security and Interoperability Profile](https://github.com/camaraproject/IdentityAndConsentManagement/blob/main/documentation/CAMARA-Security-Interoperability.md#purpose) defines that a single purpose is encoded in the list of scope values. The purpose is defined by W3C Privacy Vocabulary in the [purpose section](https://w3c.github.io/dpv/dpv/#vocab-purposes). +The [Camara Security and Interoperability Profile](https://github.com/camaraproject/IdentityAndConsentManagement/blob/main/documentation/CAMARA-Security-Interoperability.md#purpose) defines that a single purpose is encoded in the list of scope values. The purpose values are defined by W3C Data Privacy Vocabulary as indicated in the [Profile](https://github.com/camaraproject/IdentityAndConsentManagement/blob/main/documentation/CAMARA-Security-Interoperability.md#purpose-as-a-scope). #### OpenAPI security schemes definition -[Security schemes](https://spec.openapis.org/oas/v3.0.3#security-scheme-object)express security in OpenAPI. +[Security schemes](https://spec.openapis.org/oas/v3.0.3#security-scheme-object) express security in OpenAPI. Security can be expressed for the API as a whole or for each endpoint. As specified in [Use of openIdConnect for securitySchemes](https://github.com/camaraproject/IdentityAndConsentManagement/blob/main/documentation/CAMARA-API-access-and-user-consent.md#use-of-openidconnect-for-securityschemes), all Camara OpenAPI files must include the following scheme definition, with an adapted `openIdConnectUrl` in its components section. The schema definition is repeated in this document for illustration purposes, the correct format must be extracted from the link above. @@ -1580,6 +1614,22 @@ The decision on the API-level scopes was made within the [Identity and Consent M The scopes will always be those defined in the API Specs YAML files. Thus, a scope would only provide access to all endpoints and resources of an API if it is explicitly defined in the API Spec YAML file and agreed in the corresponding API subproject. +### 11.7 Resource access restriction + +In some CAMARA APIs there are functions to create resource (via POST) and then later query them via id and/or list (with GET) or delete them (via DELETE). For example we have sessions, payments, subscriptions, etc.. + +For the GET and DELETE operations we must restrict the resource(s) targeted depending on the information provided in the request. Basically we consider 2 filters: +* API client (aka ClientId) +* access token + +| Operation | 3-legged access token is used | 2-legged access token is used | +|-----------|--------------------------------|-------------------------------| +| GET/{id} | - The resource queried must have been created for the end user associated with the access token.
    - The resource queried must have been created by the same API client given in the access token. | - The resource queried must have been created by the same API client given in the access token. | +| GET/ | - Return all resource(s) created by the API consumer that are associated with both the end user identified by the access token and the same API client given in the access token. | - Return all resource(s) created by the same API client given in the access token. | +| DELETE/{id} | - The resource to be deleted must have been created for the end user associated with the access token.
    - The resource to be deleted must have been created by the same API client given in the access token. | - The resource to be deleted must have been created by the same API client given in the access token. | + + + ## 12. Subscription, Notification & Event To provide event-based interaction, CAMARA API could provide capabilities for subscription & notification management. @@ -1657,6 +1707,7 @@ CAMARA subscription model leverages **[CloudEvents](https://cloudevents.io/)** a 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 +##### Operations Four operations must be defined: | operation | path | description | @@ -1684,18 +1735,27 @@ 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). +##### Rules for subscriptions data minimization + +These rules apply for subscription with device identifier +- If 3-legged access token is used, the POST and GET responses must not provide any device identifier. +- If 2-legged access token is used, the presence of a device identifier in the response is mandatory and should be the same identifier type than the one provided in the request. + +Application of data minimization design must be considered by the API Sub Project for event structure definition. + +##### Subscriptions data model The following table provides `/subscriptions` attributes | name | type | attribute description | cardinality | |----------------|--------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------| | protocol | string | Identifier of a delivery protocol. **Only** `HTTP` **is allowed for now**. | Mandatory | -| sink | string | https callback address where the notification must be POST-ed | mandatory | -| sinkCredential | object | Sink credential provides authorization information necessary to enable delivery of events to a target. In order to be updated in future this object is polymorphic. See detail below. To protect the notification endpoint providing sinkCredential is RECOMMENDED. | optional | +| sink | string | The address to which events shall be delivered, using the HTTP protocol. | mandatory | +| sinkCredential | object | Sink credential provides authorization information necessary to enable delivery of events to a target. In order to be updated in future this object is polymorphic. See detail below. To protect the notification endpoint providing sinkCredential is RECOMMENDED.
    The sinkCredential must **not** be present in `POST` and `GET` responses. | optional | | types | string | Type of event subscribed. This attribute **must** be present in the `POST` request. It is required by API project to provide an enum for this attribute. `type` must follow the format: `org.camaraproject...` with the `api-version` with letter `v` and the major version like ``org.camaraproject.device-roaming-subscriptions.v1.roaming-status`` - Note: An array of types could be passed **but as of now only one value MUST passed**. Use of multiple value will be open later at API level. | mandatory | | config | object | Implementation-specific configuration parameters needed by the subscription manager for acquiring events. In CAMARA we have predefined attributes like ``subscriptionExpireTime``, ``subscriptionMaxEvents`` or ``initialEvent``. See detail below. | mandatory | | id | 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 | | 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 (optionally) by API server. | 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 (optionally) by API server. This attribute must be provided by the server if subscriptionExpireTime is provided in the request and server is not able to handle it. | optional | | status | string | Current status of the subscription - Management of Subscription state engine is not mandatory for now. Note: not all statuses may be considered to be implemented. See below status table. | optional | @@ -1727,7 +1787,11 @@ Remark: This action will trigger a subscription-ends event with terminationReaso | initialEvent | boolean | Set to true by API consumer if consumer wants to get an event as soon as the subscription is created and current situation reflects event request. Example: Consumer request Roaming event. If consumer sets initialEvent to true and device is in roaming situation, an event is triggered. Up to API project decision to keep it. | optional | -Subscription status value table: +**Note** on combined usage of initialEvent and subscriptionMaxEvents: +Unless explicitly decided otherwise by the API Sub Project, if an event is triggered following initialEvent set to `true`, this event will be counted towards subscriptionMaxEvents (if provided). +It is recommended to provide this clarification in all subscription APIs featuring subscriptionMaxEvents and initialEvent. + +**Subscription status value table**: Managing subscription is a draft feature, and it is not mandatory for now. An API project could decide to use/not use it. A list of status is provided for global consistency. @@ -1745,10 +1809,10 @@ Managing subscription is a draft feature, and it is not mandatory for now. An AP Error definition described in this guideline applies for subscriptions. The Following Error codes must be present: -* for `POST`: 400, 401, 403, 409, 415, 429, 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 +* for `POST`: 400, 401, 403, 409, 429 +* for `GET`: 400, 401, 403 +* for `GET .../{subscriptionId}`: 400, 401, 403, 404 +* for `DELETE`: 400, 401, 403, 404 Please see in [Commonalities/artifacts/camara-cloudevents](/artifacts/camara-cloudevents) directory ``event-subscription-template.yaml`` for more information and error examples. @@ -1766,6 +1830,7 @@ It could be useful to provide a mechanism to inform subscriber for all cases. In _Termination rules regarding subscriptionExpireTime & subscriptionMaxEvents usage_ * When client side providing a `subscriptionExpireTime` and/or `subscriptionMaxEvents` service side has to terminate the subscription without expecting a `DELETE` operation. +* CAMARA does not impose limitations for `subscriptionExpireTime` or `subscriptionMaxEvents` but API providers may enforce limitations and must document them accordingly. * If both `subscriptionExpireTime` and `subscriptionMaxEvents` are provided, the subscription will end when the first one is reached. * When none `subscriptionExpireTime` and `subscriptionMaxEvents` are not provided, client side has to trigger a `DELETE` operation to terminate it. * It is perfectly valid for client side to trigger a DELETE operation before `subscriptionExpireTime` and/or `subscriptionMaxEvents` reached. @@ -1886,7 +1951,11 @@ For consistency across CAMARA APIs, the uniform CloudEvents model must be used w 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. +#### subscription-ends event +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 notifications prematurely, or if the subscription request is managed asynchronously by the server and it is not able to provide the service. For this specific event, the `data` must feature `terminationReason` attribute. An enumeration of `terminationReason` is provided in event-subscription-template.yaml (see in [Commonalities/artifacts/camara-cloudevents](/artifacts/camara-cloudevents) directory ``event-subscription-template.yaml``). + Note: The "subscription-ends" notification is not counted in the `subscriptionMaxEvents`. (for example, if a client request a `subscriptionMaxEvents` to 2, later, received second notification, then a third notification will be sent for "subscription-ends") #### Error definition for event notification @@ -1988,40 +2057,46 @@ response: 204 No Content ``` -## Appendix A: `info.description` template for `device` identification from access token +## Appendix A (Normative): `info.description` template for when User identification can be from either an access token or explicit identifier -The documentation template below is recommended to be used as part of the API documentation in `info.description` property in the CAMARA API specs which use the `device`object defined in [CAMARA_common.yaml](/artifacts/CAMARA_common.yaml) artifact. This template provides guidance on how to handle device information in API requests **when using 3-legged access tokens and the device can be uniquely identified by the token**. +When an API requires a User (as defined by the [ICM Glossary](https://github.com/camaraproject/IdentityAndConsentManagement/blob/main/documentation/CAMARA-API-access-and-user-consent.md#glossary-of-terms-and-concepts)) to be identified in order to get access to that User's data (as Resource Owner), the User can be identified in one of two ways: +- If the access token is a Three-Legged Access Token, then the User will already have been associated with that token by the API provider, which in turn may be identified from the physical device that calls the `/authorize` endpoint for the OIDC authorisation code flow, or from the `login_hint` parameter of the OIDC CIBA flow (which can be a device IP, phone number or operator token). The `sub` claim of the ID token returned with the access token will confirm that an association with the User has been made, although this will not identify the User directly given that the `sub` will not be a globally unique identifier nor contain PII as per the [CAMARA Security and Interoperability Profile](https://github.com/camaraproject/IdentityAndConsentManagement/blob/main/documentation/CAMARA-Security-Interoperability.md#id-token-sub-claim) requirements. +- If the access token is a Two-Legged Access Token, no User is associated with the token, an hence an explicit identifier MUST be provided. This is typically either a `Device` object named `device`, or a `PhoneNumber` string named `phoneNumber`. Both of these schema are defined in the [CAMARA_common.yaml](/artifacts/CAMARA_common.yaml) artifact. In both cases, it is the User that is being identified, although the `device` identifier allows this indirectly by identifying an active physical device. -Note: With the current 3-legged authorization flows used by CAMARA, only a single end user can be associated with the access token. For the OIDC authorization code flow, only a single device can call the `/authorize` endpoint and get the code. And for CIBA, `login_hint` is currently limited to a single phone number or IP address (which can optionally include a port). - -```md -# Identifying a device from the access token +If an API provider issues Three-Legged Access Tokens for use with the API, the following error may occur: +- **Both a Three-Legged Access Token and an explicit User identifier (device or phone number) are provided by the API consumer.** -This specification defines the `device` object field as optional in API requests, specifically in cases where the API is accessed using a 3-legged access token, and the device can be uniquely identified by the token. This approach simplifies API usage for API consumers by relying on the device information associated with the access token used to invoke the API. + Whilst it might be considered harmless to proceed if both identify the same User, returning an error only when the two do not match would allow the API consumer to confirm the identity of the User associated with the access token, which they might otherwise not know. Although this functionality is supported by some APIs (e.g. Number Verification, KYC Match), for others it may exceed the scope consented to by the User. -## Handling of device information: + In this case, a `422 UNNECESSARY_IDENTIFIER` error code MUST be returned unless the scope of the API allows it to explicitly confirm whether or not the supplied identity matches that bound to the Three-Legged Access Token. -### Optional device object for 3-legged tokens: +If an API provider issues Two-Legged Access Tokens for use with the API, the following error may occur: +- **Neither a Three-legged Access Token nor an explicit User identifier (device or phone number) are provided by the API consumer.** -- When using a 3-legged access token, the device associated with the access token must be considered as the device for the API request. This means that the device object is not required in the request, and if included it must identify the same device, therefore **it is recommended NOT to include it in these scenarios** to simplify the API usage and avoid additional validations. + One or other MUST be provided to identify the User. -### Validation mechanism: + In this case, a `422 MISSING_IDENTIFIER` error code MUST be returned, indicating that the API provider cannot identify the User from the provided information. -- The server will extract the device identification from the access token, if available. -- If the API request additionally includes a `device` object when using a 3-legged access token, the API will validate that the device identifier provided matches the one associated with the access token. -- If there is a mismatch, the API will respond with a 403 - INVALID_TOKEN_CONTEXT error, indicating that the device information in the request does not match the token. +The documentation template below is RECOMMENDED to be used as part of the `info.description` API documentation to explain to the API consumer how the pattern works. -### Error handling for unidentifiable devices: +This template is applicable to CAMARA APIs which: +- require the User (i.e. Resource Owner) to be identified; and +- may have implementations which accept Two-Legged Access Tokens; and +- do NOT allow the API to confirm whether or not the optional User identifier (`device` or `phoneNumber`) matches that associated with the Three-Legged Access Token -- If the `device` object is not included in the request and the device information cannot be derived from the 3-legged access token, the server will return a 422 `UNIDENTIFIABLE_DEVICE` error. +The template SHOULD be customised for each API using it by deleting one of the options where marked by (*) +```md +# Identifying the [ device | phone number ](*) from the access token -### Restrictions for tokens without an associated authenticated identifier: +This API requires the API consumer to identify a [ device | phone number ](*) as the subject of the API as follows: +- When the API is invoked using a two-legged access token, the subject will be identified from the optional [`device` object | `phoneNumber` field](*), which therefore MUST be provided. +- When a three-legged access token is used however, this optional identifier MUST NOT be provided, as the subject will be uniquely identified from the access token. -- For scenarios which do not have a single device identifier associated to the token during the authentication flow, e.g. 2-legged access tokens, the `device` object MUST be provided in the API request. This ensures that the device identification is explicit and valid for each API call made with these tokens. -``` +This approach simplifies API usage for API consumers using a three-legged access token to invoke the API by relying on the information that is associated with the access token and was identified during the authentication process. -By following these guidelines, API consumers can use the authenticated device identifier associated with 3-legged access tokens, simplifying implementation and validation. This mechanism ensures that device identification is handled efficiently and securely, and appropriately accommodates different token types. +## Error handling: -Depending on the functionality provided by the CAMARA API, some API subprojects may still define other specific identifiers that differs from the common `device` object definition. Not all APIs necessarily have to refer to a device, e.g. Carrier Billing API only defines a phone number as a way to identify the mobile account to be billed, Know Your Costumer only defines a phone number as a way to identify the associated account data or Home Devices QoD API defines public IP v4 address as a way to identify the user home network. +- If the subject cannot be identified from the access token and the optional [`device` object | `phoneNumber` field](*) is not included in the request, then the server will return an error with the `422 MISSING_IDENTIFIER` error code. -Therefore, the mechanism described in this template is not applicable to all APIs, but could be used as way to make `device` object more interoperable and usable for API consumers. +- If the subject can be identified from the access token and the optional [`device` object | `phoneNumber` field](*) is also included in the request, then the server will return an error with the `422 UNNECESSARY_IDENTIFIER` error code. This will be the case even if the same [ device | phone number ](*) is identified by these two methods, as the server is unable to make this comparison. +```