├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── SUPPORTERS.md ├── extensions.md ├── images └── figure_protocol_flow.png ├── operating_model.md ├── security_guidelines.md └── specification.md /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | 2.0 3 | - Renamed this project 'OpenDSR' to reflect our wider scope of additional regulation: CCPA 4 | - Changed references to OpenGDPR to OpenDSR 5 | - Added regulation field of 'regulation' to request object 6 | - Added section 10.1 to provide some guidance on supporting prior versions, including the renaming of the project 7 | - Tagged V1 to branch `opengdpr_v1` for posterity: https://github.com/opengdpr/opengdpr/tree/opengdpr_v1 8 | 9 | 1.0 10 | - Added support for `extensions`: processor-defined fields in each request. Used for additional scoping information (eg processor-project-id) or processor-specific identities (eg mparticle-id). 11 | - Converted spec to markdown format for easier reading and editing. 12 | - Added `results_url` to status objects to contain the outputs from an access or portability request when in status `completed`. 13 | - Added versioning information and requirement to include major version in the OpenGDPR resource URL. 14 | 15 | 0.1.4 16 | - Removed 'agent' role throughout and added a Best Practice to use a sub-processor to distribute requests and ensure mature infrastructure (retries, security verifications & logging) 17 | - Removed 'GDPR for Lawyers' document that is no longer needed 18 | - Removed IETF text at the top of the specification. This is a specification and not a standard so the IETF format is not needed. 19 | - Corrected restful noun to "opengdpr_requests" 20 | - Added status 'cancelled' as the ending status for cancelled requests 21 | - Added versions to all API endpoints 22 | 23 | 0.1.3 24 | - Update role descriptions of Agent as a type of processor and clarified agent responsibility to send callbacks when requests are distributed 25 | - Updated flow diagram to include communication from controller to data subject 26 | - Added status of 'distributed' to represent that an Agent has sent a request downstream to all parties. 27 | - Clarified that this is a 'specification' and not a 'standard' nor a 'framework' 28 | - Updated summary doc to match changes above 29 | - Added an 'OpenGDPR for Lawyers' document aiming to clarify frequent legal questions about OpenGDPR 30 | 31 | 0.1.2 32 | - Changed ‘broker’ to ‘agent’ throughout 33 | - Renaming controller/processor to ‘data controller’ / ‘data processor’ in roles section 34 | - Added several gdpr citations for chapters / articles 35 | - Added Aurelie (mParticles DPO) as a contributor to the bottom 36 | - Added HTTP DELETE method for cancellation if request is in ‘in progress’ status 37 | - Clarified that processors only MUST try to send callbacks once and recommended but not required to retry 38 | - Added “property_id” field to request 39 | - Added “client_customer_id” to identity support list 40 | - Added “supported_subject_request_types” field to discovery 41 | - Added a request types of “portability” and “access”. No other changes specific to these request types have been made. 42 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute to OpenDSR 2 | Firstly, thank you for your interest and contribution! Here is a quick set of guidelines for contributing. These are guidelines and not hard and fast rules, please use your best judgement. 3 | 4 | Here are a few other OpenDSR resources: 5 | - Website: OpenDSR.org 6 | - Public Slack group: OpenDSR.slack.com 7 | 8 | ## Questions about the spec or how certain cases are handled? 9 | - Search the repositories issues (open and closed) and the wiki to see if it has been discussed already. 10 | - Hop into the Slack channel and chat with us, someone will help you out. 11 | - Please don't open new issues for simple questions, asking in Slack is much faster 12 | 13 | ## Have you implemented the OpenDSR spec and want to spread the word? 14 | Awesome! Add your company name to the SUPPORTERS.md and it will get pulled into the website. 15 | 16 | ## Have a suggested improvement or fix? 17 | - Open a new pull request with your improvement. 18 | - Be sure to write a thorough description. 19 | - Be sure to update all files as needed: readme, specification, etc. 20 | 21 | ## Have a major change you want to make? 22 | - Open a new issue and propose the idea to get positive feedback before spending lots of time on edits. 23 | - Once you've got some support, make your changes and submit a pull request. 24 | 25 | ## Curious about how the spec is authored or want to make changes to the operating model? 26 | - Submit a pull request to the operating_model.md and it will be reviewed in the same fashion as the spec. 27 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2018 mParticle, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | 16 | Apache License 17 | Version 2.0, January 2004 18 | http://www.apache.org/licenses/ 19 | 20 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 21 | 22 | 1. Definitions. 23 | 24 | "License" shall mean the terms and conditions for use, reproduction, 25 | and distribution as defined by Sections 1 through 9 of this document. 26 | 27 | "Licensor" shall mean the copyright owner or entity authorized by 28 | the copyright owner that is granting the License. 29 | 30 | "Legal Entity" shall mean the union of the acting entity and all 31 | other entities that control, are controlled by, or are under common 32 | control with that entity. For the purposes of this definition, 33 | "control" means (i) the power, direct or indirect, to cause the 34 | direction or management of such entity, whether by contract or 35 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 36 | outstanding shares, or (iii) beneficial ownership of such entity. 37 | 38 | "You" (or "Your") shall mean an individual or Legal Entity 39 | exercising permissions granted by this License. 40 | 41 | "Source" form shall mean the preferred form for making modifications, 42 | including but not limited to software source code, documentation 43 | source, and configuration files. 44 | 45 | "Object" form shall mean any form resulting from mechanical 46 | transformation or translation of a Source form, including but 47 | not limited to compiled object code, generated documentation, 48 | and conversions to other media types. 49 | 50 | "Work" shall mean the work of authorship, whether in Source or 51 | Object form, made available under the License, as indicated by a 52 | copyright notice that is included in or attached to the work 53 | (an example is provided in the Appendix below). 54 | 55 | "Derivative Works" shall mean any work, whether in Source or Object 56 | form, that is based on (or derived from) the Work and for which the 57 | editorial revisions, annotations, elaborations, or other modifications 58 | represent, as a whole, an original work of authorship. For the purposes 59 | of this License, Derivative Works shall not include works that remain 60 | separable from, or merely link (or bind by name) to the interfaces of, 61 | the Work and Derivative Works thereof. 62 | 63 | "Contribution" shall mean any work of authorship, including 64 | the original version of the Work and any modifications or additions 65 | to that Work or Derivative Works thereof, that is intentionally 66 | submitted to Licensor for inclusion in the Work by the copyright owner 67 | or by an individual or Legal Entity authorized to submit on behalf of 68 | the copyright owner. For the purposes of this definition, "submitted" 69 | means any form of electronic, verbal, or written communication sent 70 | to the Licensor or its representatives, including but not limited to 71 | communication on electronic mailing lists, source code control systems, 72 | and issue tracking systems that are managed by, or on behalf of, the 73 | Licensor for the purpose of discussing and improving the Work, but 74 | excluding communication that is conspicuously marked or otherwise 75 | designated in writing by the copyright owner as "Not a Contribution." 76 | 77 | "Contributor" shall mean Licensor and any individual or Legal Entity 78 | on behalf of whom a Contribution has been received by Licensor and 79 | subsequently incorporated within the Work. 80 | 81 | 2. Grant of Copyright License. Subject to the terms and conditions of 82 | this License, each Contributor hereby grants to You a perpetual, 83 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 84 | copyright license to reproduce, prepare Derivative Works of, 85 | publicly display, publicly perform, sublicense, and distribute the 86 | Work and such Derivative Works in Source or Object form. 87 | 88 | 3. Grant of Patent License. Subject to the terms and conditions of 89 | this License, each Contributor hereby grants to You a perpetual, 90 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 91 | (except as stated in this section) patent license to make, have made, 92 | use, offer to sell, sell, import, and otherwise transfer the Work, 93 | where such license applies only to those patent claims licensable 94 | by such Contributor that are necessarily infringed by their 95 | Contribution(s) alone or by combination of their Contribution(s) 96 | with the Work to which such Contribution(s) was submitted. If You 97 | institute patent litigation against any entity (including a 98 | cross-claim or counterclaim in a lawsuit) alleging that the Work 99 | or a Contribution incorporated within the Work constitutes direct 100 | or contributory patent infringement, then any patent licenses 101 | granted to You under this License for that Work shall terminate 102 | as of the date such litigation is filed. 103 | 104 | 4. Redistribution. You may reproduce and distribute copies of the 105 | Work or Derivative Works thereof in any medium, with or without 106 | modifications, and in Source or Object form, provided that You 107 | meet the following conditions: 108 | 109 | (a) You must give any other recipients of the Work or 110 | Derivative Works a copy of this License; and 111 | 112 | (b) You must cause any modified files to carry prominent notices 113 | stating that You changed the files; and 114 | 115 | (c) You must retain, in the Source form of any Derivative Works 116 | that You distribute, all copyright, patent, trademark, and 117 | attribution notices from the Source form of the Work, 118 | excluding those notices that do not pertain to any part of 119 | the Derivative Works; and 120 | 121 | (d) If the Work includes a "NOTICE" text file as part of its 122 | distribution, then any Derivative Works that You distribute must 123 | include a readable copy of the attribution notices contained 124 | within such NOTICE file, excluding those notices that do not 125 | pertain to any part of the Derivative Works, in at least one 126 | of the following places: within a NOTICE text file distributed 127 | as part of the Derivative Works; within the Source form or 128 | documentation, if provided along with the Derivative Works; or, 129 | within a display generated by the Derivative Works, if and 130 | wherever such third-party notices normally appear. The contents 131 | of the NOTICE file are for informational purposes only and 132 | do not modify the License. You may add Your own attribution 133 | notices within Derivative Works that You distribute, alongside 134 | or as an addendum to the NOTICE text from the Work, provided 135 | that such additional attribution notices cannot be construed 136 | as modifying the License. 137 | 138 | You may add Your own copyright statement to Your modifications and 139 | may provide additional or different license terms and conditions 140 | for use, reproduction, or distribution of Your modifications, or 141 | for any such Derivative Works as a whole, provided Your use, 142 | reproduction, and distribution of the Work otherwise complies with 143 | the conditions stated in this License. 144 | 145 | 5. Submission of Contributions. Unless You explicitly state otherwise, 146 | any Contribution intentionally submitted for inclusion in the Work 147 | by You to the Licensor shall be under the terms and conditions of 148 | this License, without any additional terms or conditions. 149 | Notwithstanding the above, nothing herein shall supersede or modify 150 | the terms of any separate license agreement you may have executed 151 | with Licensor regarding such Contributions. 152 | 153 | 6. Trademarks. This License does not grant permission to use the trade 154 | names, trademarks, service marks, or product names of the Licensor, 155 | except as required for reasonable and customary use in describing the 156 | origin of the Work and reproducing the content of the NOTICE file. 157 | 158 | 7. Disclaimer of Warranty. Unless required by applicable law or 159 | agreed to in writing, Licensor provides the Work (and each 160 | Contributor provides its Contributions) on an "AS IS" BASIS, 161 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 162 | implied, including, without limitation, any warranties or conditions 163 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 164 | PARTICULAR PURPOSE. You are solely responsible for determining the 165 | appropriateness of using or redistributing the Work and assume any 166 | risks associated with Your exercise of permissions under this License. 167 | 168 | 8. Limitation of Liability. In no event and under no legal theory, 169 | whether in tort (including negligence), contract, or otherwise, 170 | unless required by applicable law (such as deliberate and grossly 171 | negligent acts) or agreed to in writing, shall any Contributor be 172 | liable to You for damages, including any direct, indirect, special, 173 | incidental, or consequential damages of any character arising as a 174 | result of this License or out of the use or inability to use the 175 | Work (including but not limited to damages for loss of goodwill, 176 | work stoppage, computer failure or malfunction, or any and all 177 | other commercial damages or losses), even if such Contributor 178 | has been advised of the possibility of such damages. 179 | 180 | 9. Accepting Warranty or Additional Liability. While redistributing 181 | the Work or Derivative Works thereof, You may choose to offer, 182 | and charge a fee for, acceptance of support, warranty, indemnity, 183 | or other liability obligations and/or rights consistent with this 184 | License. However, in accepting such obligations, You may act only 185 | on Your own behalf and on Your sole responsibility, not on behalf 186 | of any other Contributor, and only if You agree to indemnify, 187 | defend, and hold each Contributor harmless for any liability 188 | incurred by, or claims asserted against, such Contributor by reason 189 | of your accepting any such warranty or additional liability. 190 | 191 | END OF TERMS AND CONDITIONS 192 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenDSR Summary 2 | 3 | # Overview 4 | This is an introductory document intended to provide a summary of the OpenDSR framework. For full reference details, please see the complete specification at https://github.com/OpenGDPR/OpenDSR. This project was formerly known as OpenGDPR and existed at https://www.OpenGDPR.org and https://github.com/OpenGDPR/OpenGDPR. 5 | 6 | # Goals and Scope 7 | The OpenDSR specification defines a common approach for data Controllers and Processors to build interoperable systems for tracking and fulfilling Data Subject requests as defined under the General Data Protection Regulation (GDPR) and California Consumer Privacy Act (CCPA). To reduce confusion, this document uses the language and terminology of the GDPR. 8 | 9 | This framework is intended to: 10 | 1. Provide a well defined JSON specification that allows Controllers and Processors to communicate and 11 | manage Data Subject Requests (DSRs) in a uniform and scalable manner. 12 | 2. Provide strong cryptographic verification of request receipts to provide chain of 13 | processing assurance and demonstrate accountability to regulatory authorities (Article 14 | 5.2). 15 | 3. Provide for a callback mechanism to enable Controllers to track the status of all DSRs. 16 | 17 | This specification does not cover: 18 | 1. Defining the technical measures to describe the fulfill of Data Subject requests. 19 | It is the responsibility of each Data Controller and Data Processor to interpret and 20 | apply relevant regulatory analysis and expertise to honor DSRs (the text of the GDPR and CCPA). 21 | 2. The protocol for communications between Controllers and Data Subjects. 22 | 3. The protocol for communications between Controllers, Processors and Supervisory 23 | Authorities. 24 | 4. The protocol for communication of the results of an access or portability request. 25 | 26 | # Roles and Request Lifecycles 27 | 28 | ## Roles 29 | **Data Subject** 30 | The person whose data is being collected and/or processed. 31 | 32 | The CCPA refers to data subjects as 'consumers'. 33 | 34 | **Data Controller** 35 | The Data Controller receives DSRs from the Data Subjects and validates them. The Controller submits requests to the Data Processors. 36 | 37 | The CCPA refers to controllers as businesses. 38 | 39 | **Data Processor** 40 | The Data processor acts on behalf of the controller and fulfills requests within the Controllers scope. 41 | 42 | The CCPA refers to processors as service providers. 43 | 44 | ## Request Sequence 45 | This diagram outlines the flow of DSRs all the way from the data subject to the fulfillment by each Processor. This flow includes optional callbacks that allow the Controller to receive status changes. 46 | 47 | ![protocol flow](images/figure_protocol_flow.png) 48 | 49 | 1. New Data Subject Request: The data subject files a request to the data controller containing appropriate information. Request may be of any type defined herein. 50 | 2. Request Distribution: The controller verifies the request and if it will be honored, it is submitted to Processors. 51 | 3. Request Fulfillment: The Processor fulfills their obligation within the scope of this request. For example, this may include deleting user data in the case of a deletion request. 52 | 4. Request Status via Callback: The processor will submit status updates to the controller if callbacks are included in the request. 53 | 5. Communication to the Data Subject: The Controller communicates the results to the data subject. 54 | 55 | ## Request Types 56 | The spec supports request types of “erasure”, "access" and “portability”. For all types, the details of how a processor fulfills the request is defined by the regulations and is out of scope for this specification. For access and portability requests, secure transmission of the resulting personal data is left up to the controller and processor. 57 | 58 | # API Summary 59 | ## Endpoints 60 | This is an overview of available HTTP methods for communicating between Controllers and Processors. The following endpoints should be provided by the Processor (to receive requests from the Controller). 61 | 62 | Restful API endpoints for the resource "request": 63 | 64 | | HTTP Method | Path | Description | Supported? | 65 | | --- | --- | --- | --- | 66 | | POST | requests/ | Create a new OpenDSR request | Yes | 67 | | GET | requests/{RequestId} | Retrieve status of a single OpenDSR request | Yes | 68 | | PUT | requests/{RequestId} | - | No, requests cannot be updated after being created | 69 | | DELETE | requests/{RequestId} | Cancel an OpenDSR request | Yes, cancellation is valid in status “pending” only | 70 | 71 | Non-Restful endpoints: 72 | 73 | | HTTP Method | Path | Description | Supported? | 74 | | --- | --- | --- | --- | 75 | | GET | /discovery | Processors describe their OpenDSR support| Yes | 76 | | POST | /callback | Sent by Processors when a request status changes| Yes | 77 | 78 | 79 | # Sample Request Object 80 | Refer to the full specification for definitions of objects and fields. 81 | ``` 82 | { 83 | "subject_request_id":"a7551968-d5d6-44b2-9831-815ac9017798", 84 | "regulation": "gdpr", 85 | "subject_request_type":"erasure", 86 | "submitted_time":"2018-10-02T15:00:00Z", 87 | "subject_identities":[ 88 | { 89 | "identity_type":"email", 90 | "identity_value":"johndoe@example.com", 91 | "identity_format":"raw" 92 | } 93 | ], 94 | "api_version":"2.0", 95 | "status_callback_urls":[ 96 | "https://example-controller.com/opendsr/callbacks" 97 | ] 98 | } 99 | ``` 100 | -------------------------------------------------------------------------------- /SUPPORTERS.md: -------------------------------------------------------------------------------- 1 | An ongoing list of companies and organizations that have implemented the OpenDSR/OpenGDPR Spec: 2 | - mParticle 3 | - AppsFlyer 4 | - Singular 5 | -------------------------------------------------------------------------------- /extensions.md: -------------------------------------------------------------------------------- 1 | # OpenDSR Extensions 2 | 3 | OpenDSR requests may contain an `extensions` object, composed of a series of child-objects, keyed by a processor domain. 4 | 5 | - The domain of each extension **MUST** match the processor's OpenDSR domain. 6 | - Extensions **MUST** not be used for or contain authentication information. 7 | - Processors **MUST** only implement an extension for items that do not already fit into the generic spec. 8 | 9 | See section the [OpenDSR spec](specification.md) for more information on the use of extensions. 10 | 11 | ## Published Extensions 12 | 13 | ### mParticle 14 | 15 | Domain: `opendsr.mparticle.com` 16 | 17 | Supported keys: 18 | 19 | - `mpids`: An array of mParticle IDs. The mParticle ID is a 64-bit signed integer. 20 | - `identities`: An array of additional mParticle-supported identity objects, each containing a key and value. Both keys and values in this object are strings. Valid keys are: 21 | - `other`: mParticle ID `other`. 22 | - `other2`: mParticle ID `other2`. 23 | - `other3`: mParticle ID `other3`. 24 | - `other4`: mParticle ID `other4`. 25 | - `other5`: mParticle ID `other5`. 26 | - `other6`: mParticle ID `other6`. 27 | - `other7`: mParticle ID `other7`. 28 | - `other8`: mParticle ID `other8`. 29 | - `other9`: mParticle ID `other9`. 30 | - `other10`: mParticle ID `other10`. 31 | - `mobile_number`: mParticle ID `phone_number_1`. 32 | - `phone_number_2`: mParticle ID `phone_number_2`. 33 | - `phone_number_3`: mParticle ID `phone_number_3`. 34 | 35 | ### Example 36 | 37 | ```json 38 | "extensions": { 39 | "opendsr.mparticle.com": { 40 | "mpids": [ 41 | 1234567890, 42 | 5678901234 43 | ], 44 | "identities": [ 45 | { 46 | "identity_type": "other1", 47 | "identity_value": "test@test1.com" 48 | } 49 | ] 50 | } 51 | } 52 | ``` 53 | 54 | ### Schema 55 | 56 | ```json 57 | { 58 | "type": "object", 59 | "properties": { 60 | "opendsr.mparticle.com": { 61 | "type": "object", 62 | "properties": { 63 | "mpids": { 64 | "type": "array", 65 | "items": { 66 | "examples": [ 67 | 120934871234, 68 | 1309487143098 69 | ] 70 | } 71 | }, 72 | "identities":{ 73 | "type":"array", 74 | "items":{ 75 | "examples":[ 76 | { 77 | "identity_type": "example_type", 78 | "identity_value":"example_id_value" 79 | } 80 | ] 81 | } 82 | } 83 | } 84 | } 85 | } 86 | } 87 | ``` 88 | -------------------------------------------------------------------------------- /images/figure_protocol_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opengdpr/OpenDSR/6c080e2f277df3d908ba9dc16c1084638315b210/images/figure_protocol_flow.png -------------------------------------------------------------------------------- /operating_model.md: -------------------------------------------------------------------------------- 1 | # OpenDSR Operating Model 2 | 3 | ## Introduction 4 | This document defines the operations of the OpenDSR group, including: 5 | - Approve changes to the specification via Git pull requests 6 | - Routinely publish new versions of the specification 7 | - Convene as a group to make operating model decisions 8 | - Collaborate towards enriching OpenDSR resources (wiki, sample code, etc.) 9 | - Co-champion the OpenDSR specification towards greater adoption 10 | 11 | ## Roles 12 | People interact with OpenDSR via two types participation: 13 | *Working Group* 14 | - People who steward and lead the OpenDSR spec. 15 | - People who operate the OpenDSR GitHub repo, publish new versions of the specification, collaborate to promote the specification in-market, etc. 16 | - All community participation is also expected from Working Group members. 17 | - Active participation is expected from working group members. 18 | 19 | *Community* 20 | - People who implement the OpenDSR spec to receive and track GDPR data subject requests. 21 | - People who ask questions on Github, submit pull requests, contribute to the wiki, etc. 22 | - Community members may submit Pull requests to add their company and logo to the public site 23 | 24 | ## Working Group 25 | This is an open specification, there are no limits to who can implement or who can apply to be in the Working Group (including competitors of any existing members). The Working Group is composed of: 26 | - Working Group is composed of members from mParticle, AppsFlyer, Braze and Amplitude. 27 | - Participants are encouraged to be from product, engineering and privacy/legal/compliance roles as the content is technical. 28 | 29 | ### What is expected of working group members? 30 | The Working Group drives the OpenDSR initiative: 31 | - Answering GitHub issues and interacting with the community at large 32 | - Reviewing and commenting on GitHub pull requests 33 | - Authoring and reviewing Github wiki pages and other resources to help the community 34 | - Participating in quarterly meetings with other working group members 35 | - Building support for OpenDSR into your products 36 | - Champion the OpenDSR initiative in your day-to-day discussions 37 | 38 | ### How are new working group members added? 39 | - New members must be proposed and receive 75% support from existing working group members 40 | - New members must be judged on the positive contributions and domain knowledge, not on existing biases or marketplace influences (competition) 41 | 42 | ### How do working group members leave? 43 | - Existing members may elect to resign their membership or may be remove due to inactivity. 44 | 45 | ## Specification Updates 46 | A few simple rules will help keep the project running and the repo clean: 47 | - The repo is the truth, anything left unpublished hasn’t actually happened yet 48 | - Goal of responding to new issues within 3 days 49 | - Normal pull requests require 2 members to approve before they are accepted 50 | - Large pull requests require 3 members to approve before being accepted 51 | - Most conversation around issues and pull requests should happen publicly in GitHub 52 | - If consensus cannot be reached via discussion, members are encouraged to collaborate offline and update the discussion threads afterwards 53 | - Specification will use Semantic Versioning (https://semver.org/) to organize changes 54 | 55 | ## Housekeeping 56 | - This operating model, license and contribution guidelines are all included in the Repo and are subject to the same change processes 57 | - Quarterly working group conference calls to discuss outstanding issues and iterate on this operating model 58 | - Contribution guidelines are defined in the CONTRIBUTING.md file in the repo 59 | -------------------------------------------------------------------------------- /security_guidelines.md: -------------------------------------------------------------------------------- 1 | # Security Guidelines 2 | Security guidelines to jumpstart implementations and avoid security pitfalls. 3 | 4 | ## Certificates 5 | 1. Purchase certificate solely for OpenDSR to prevent issues elsewhere with your infrastructure if 6 | you ever need to revoke your OpenDSR certificate for any reason. 7 | 2. Certificate must be purchased for a dedicated OpenDSR subdomain rather than using a wildcard 8 | or subdomain that is use for other purposes. We recommend following the standard form of 9 | OpenDSR.companydomain.com, for example: opendsr.mparticle.com 10 | 3. Valid CA signed certificates should be mandated where possible and self signed certificates should 11 | be avoided for this purpose to allow other parties to validate your certificate is valid. 12 | 4. Preferably obtain an EV certificate to provide increased verification of company association. 13 | 14 | ## Callback validation: 15 | All callbacks must be issued over TLS, and the server certificate must be validated to match the callback 16 | endpoint. 17 | 1. It is recommended that controllers maintain a whitelist of processor domains allowed to issue 18 | callbacks. If such a whitelist is not kept, any entity with a valid certificate will be able to send data to 19 | the controller’s callback endpoints. When a callback is received, the controller should validate that 20 | the processor’s domain (taken from the X­OpenDSR­Processor­Domain header) is in the 21 | processor whitelist. 22 | 2. If the processor’s domain is in the whitelist, the certificate must be validated before checking the 23 | signature: 24 | 25 | 1. Callback issuers must publish their signing certificate on the /discovery endpoint of their OpenDSR domain. This certificate may be cached for the lifetime of the certificate 26 | 2. Validate that the certificate hasn’t expired 27 | 3. Validate that the certificate was issued by a trusted CA 28 | 4. Validate that the certificate hasn’t been revoked 29 | 5. If the certificate has been revoked and it was cached, the controller should retry by downloading the certificate from /discovery 30 | 6. Validate that the processor’s domain is in the certificate’s SAN 31 | Steps ii through vi should be handled by a library and not validated manually. 32 | 33 | 3. If the certificate is valid, the signature (taken from the `X-OpenDSR-Signature` header) must be validated: 34 | 1. Extract the public key from the processor’s certificate 35 | 2. Validate the base64-decoded signature against the raw request body using the SHA256 digest. 36 | 37 | 4. If both the certificate and signature are valid, the payload can be processed: 38 | 1. Validate that the callback body is valid JSON 39 | 2. Process the callback 40 | 41 | 5. Respond to the callback. 42 | 1. The endpoint must return a 202 Accepted if: 43 | * All validations succeeded 44 | 2. The endpoint must return a 401 Unauthorized if: 45 | * The processor domain is not in the whitelist 46 | * The certificate fails to validate 47 | 3. The endpoint must return a 400 Bad Request if: 48 | * The signature is not valid 49 | * The payload is not valid JSON 50 | -------------------------------------------------------------------------------- /specification.md: -------------------------------------------------------------------------------- 1 | # OpenDSR 2 | 3 | An open specification for data subject request management, formerly called OpenGDPR. 4 | 5 | ### Version 2.0 6 | 7 | ### Abstract 8 | 9 | This document defines a common specification for Data Controllers and Processors to 10 | build interoperable systems for tracking and honoring Data Subject Rights requests 11 | to support the accountability principle as defined under the General Data 12 | Protection Regulation (GDPR). 13 | 14 | It defines roles, responsibilities, objects and protocols that Data Controllers 15 | and Processors can utilize to distribute, fulfill and report the status of a range 16 | of Data Subject request types. 17 | 18 | For more information on the Data Subject Rights, [see chapter 3 of the GDPR 19 | legislation](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32016R0679&from=EN). 20 | 21 | ## 1. Introduction 22 | 23 | This specification is intended to: 24 | - Provide a well defined JSON specification that allows Controllers/businesses and Processors/vendor to communicate and 25 | manage Data Subject access, portability and erasure requests in a uniform and scalable 26 | manner. 27 | - Provide strong cryptographic verification of request receipts to provide chain of 28 | processing assurance and demonstrate accountability to regulatory authorities (Article 29 | 5.2). 30 | - Provide for a callback mechanism to enable Controllers & Businesses to identify the status of 31 | all Data Subject requests. 32 | 33 | This specification does not cover: 34 | 35 | - Defining the technical measures to describe the fulfill of Data Subject requests. 36 | It is the responsibility of each Data Controller and Data Processor to interpret and 37 | apply the GDPR to honor Data Subject requests (Chapter 3). 38 | - The protocol for communications between Controllers and Data Subjects. 39 | - The protocol for communications between Controllers or Processors and Supervisory 40 | Authorities. 41 | - The protocol for communication of the results of an access or portability request. 42 | 43 | ### 1.1. Notational Conventions 44 | - The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", 45 | "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be 46 | interpreted as described in [RFC2119](https://tools.ietf.org/html/rfc2119). 47 | - Global Unique Identifiers (GUID) **MUST** be lowercase and v4 format. 48 | 49 | 50 | ## 2. Terms and Definitions 51 | This document retains much of the language from it's origins as a GDPR DSR framework and has not been fully translated to be fully regulation neutral. 52 | 53 | #### Data Subject Request 54 | 55 | A request from a Data Subject exercising their Data Subject Rights as defined within the GDPR under Chapter 3. 56 | 57 | #### Fulfillment 58 | 59 | Enacting compliance related activities to honor an OpenDSR request. 60 | 61 | #### OpenGDPR 62 | The prior name of this framework. 63 | 64 | ## 3. OpenDSR Basics 65 | 66 | ### 3.1. Roles and Responsibilities 67 | 68 | #### Data Subject 69 | 70 | The individual person to whom the data subject request pertains; A European Union resident whose personal data is being processed (GDPR) or a resident of California (CCPA). 71 | 72 | #### Data Controller (GDPR) / Business (CCPA) 73 | 74 | An entity which makes the decision about what personal data will be processed and the types of processing that will be done with respect to that personal data. The Data Controller receives Data Subject requests from the Data Subjects and validates them. The Data Controller **SHOULD** provide a callback endpoint. The Data Controller **SHOULD** verify response signatures. Referenced as "Controller." 75 | 76 | #### Data Processor (GDPR) / Vendor (CCPA) 77 | 78 | The organization that processes data pursuant to the instructions of the Controller on behalf of the Controller. The Data Processor receives data subject requests via RESTful endpoints and is responsible for fulfilling requests. The Data Processor **MUST** provide a signed response to requests. The Data Processor **MUST** honor callbacks. Data Processors **MUST** honor callbacks included in requests. 79 | 80 | Processors **MUST** provide the following endpoints: 81 | 82 | - `/discovery` 83 | - `/status` 84 | - `/requests` 85 | 86 | ### 3.2. Protocol Flow 87 | 88 | ![protocol flow](images/figure_protocol_flow.png) 89 | 90 | *Figure 1. Request Sequence Flow* 91 | 92 | The flow illustrated in figure 1 includes the following steps: 93 | 94 | 1. A new data subject request submitted to the Controller 95 | 2. Controller verifies the request and submits it to Processors 96 | 3. Processor fulfills request 97 | 4. Processor reports status to the Controller via callbacks 98 | 5. Controller reports back to the Data Subject 99 | 100 | ### 3.3. Transport 101 | 102 | Whenever Transport Layer Security (TLS) is used by this specification, the appropriate version (or versions) of TLS will vary over time, based on the 103 | widespread deployment and known security vulnerabilities. Implementations **MAY** also support additional transport layer security mechanisms that meet an individual party’s (e.g. Controller or Processor) security requirements. 104 | 105 | ### 3.4. Versions 106 | This spec will be released by major and minor increments using semantic versioning. Breaking changes will be pushed in major branches. More info is available here: https://semver.org/. 107 | 108 | URLs **MUST** include the major version number at the start, for example: https://opendsr.processor.com/v1/discovery. 109 | 110 | Resources **MAY** include the major and minor version in the "api_version" field, examples shown below. 111 | 112 | ## 4. Security 113 | 114 | ### 4.1. Certificates 115 | 116 | Digital certificates used in this protocol **MUST** be issued by a trusted 117 | certificate authority and **MUST** be issued to the organization issuing the 118 | callback. A self-signed certificate **MUST NOT** be used. 119 | 120 | ### 4.2. Signing 121 | 122 | Digital signatures **MUST** be generated and validated according to the Digital 123 | Signature Standard FIPS PUB 186-4: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf 124 | 125 | ### 4.3. Authentication 126 | 127 | API authentication for OpenDSR requests is out of scope for this document, and is left to the Processor to implement. Callbacks **MUST** be authenticated by a digital signature issued by the certificate detailed in section 4.1. 128 | 129 | ## 5. Identities 130 | 131 | The identity types and schema documented below are reused throughout all OpenDSR API contracts. 132 | 133 | ### 5.1. Identity Type Keys 134 | 135 | The following identity type keys are supported: 136 | 137 | ``` 138 | controller_customer_id 139 | android_advertising_id 140 | android_id 141 | email 142 | fire_advertising_id 143 | ios_advertising_id 144 | ios_vendor_id 145 | microsoft_advertising_id 146 | microsoft_publisher_id 147 | roku_publisher_id 148 | roku_advertising_id 149 | ``` 150 | 151 | ### 5.2 Identity Formats 152 | 153 | The following identity formats are supported: 154 | 155 | ``` 156 | raw 157 | sha1 158 | md5 159 | sha256 160 | ``` 161 | 162 | ### 5.3. Identity Object 163 | 164 | An OpenDSR request **MAY** contain one or more Identity objects used to fulfill the request. 165 | The identity object is **REQUIRED** unless identities are present in an extension object, see section 7.1.2 166 | An Identity object contains the following properties: 167 | 168 | - `identity_type` 169 | 170 | **REQUIRED** string value representing the form of identity. Supported values: See section 5.1. 171 | 172 | - `identity_value` 173 | 174 | **REQUIRED** string value representing the identity. This does not apply to discovery response objects. 175 | 176 | - `identity_format` 177 | 178 | **REQUIRED** string value representing the encoding of the identity. Supported values: See section 5.2. 179 | 180 | ## 6. OpenDSR Discovery 181 | 182 | OpenDSR service implementations **MUST** provide an endpoint that describes their support for the OpenDSR specification via HTTP GET. 183 | 184 | ## 6.1. Example Discovery Request 185 | 186 | ```http 187 | GET /discovery HTTP/1.1 188 | Host: example-processor.com 189 | Accept: application/json 190 | ``` 191 | 192 | ### 6.2. Subject Request Types 193 | 194 | The following subject request types are supported: 195 | 196 | ``` 197 | access 198 | portability 199 | erasure 200 | ``` 201 | 202 | ### 6.3. Discovery Response Properties 203 | 204 | `api_version` 205 | 206 | **REQUIRED** version string representing the supported version of the OpenDSR API. 207 | 208 | `supported_identities` 209 | 210 | **REQUIRED** array of "identity_type" and "identity_format" pairs. 211 | 212 | `supported_subject_request_types` 213 | 214 | **REQUIRED** array of "subject_request_type" strings as defined in 6.2. 215 | 216 | `processor_certificate` 217 | 218 | **REQUIRED** HTTP endpoint x.509 where certificate used to sign callbacks and OpenDSR API responses can be downloaded. The domain **MUST** match that of the discovery callback. 219 | 220 | ### 6.4. Example Discovery Response 221 | 222 | ```http 223 | HTTP/1.1 200 OK 224 | Content-Type: application/json 225 | 226 | { 227 | "api_version":"2.0", 228 | "supported_identities":[ 229 | { 230 | "identity_type":"email", 231 | "identity_format":"raw" 232 | }, 233 | { 234 | "identity_type":"email", 235 | "identity_format":"sha256" 236 | } 237 | ], 238 | "supported_subject_request_types":[ 239 | "erasure" 240 | ], 241 | "processor_certificate":"https://exampleprocessor.com/cert.pem" 242 | } 243 | ``` 244 | 245 | ## 7. OpenDSR Request 246 | 247 | ### 7.1.1 OpenDSR Request Properties 248 | 249 | OpenDSR service implementations **MUST** provide an endpoint that creates OpenDSR JSON requests via HTTP POST. Controllers **MUST** submit requests with the following parameters: 250 | 251 | `regulation` 252 | 253 | **REQUIRED** string value representing the regulation that this request is covered by: 'gdpr' or 'ccpa' are currently supported. 254 | 255 | `subject_request_id` 256 | 257 | **REQUIRED** UUID v4 string. This **MUST** be generated by the Controller at the time of request submission to a Processor. 258 | 259 | `subject_request_type` 260 | 261 | **REQUIRED** string value representing the type of OpenDSR Request. Supported values: "erasure", "portability", "access" 262 | 263 | `subject_identities` 264 | 265 | **OPTIONAL** array of Identity objects as specified in section 5.3. This is **REQUIRED** unless identities are included in an `extensions` object. 266 | 267 | `submitted_time` 268 | 269 | **REQUIRED** RFC 3339 date string representing the time of the original request by the data subject. 270 | 271 | `api_version` 272 | 273 | **OPTIONAL** Version string representing the desired version of the OpenDSR API. 274 | 275 | `status_callback_urls` 276 | 277 | **OPTIONAL** Array of urls to be invoked by the Processor on subject_request_status change. This array **SHOULD** be included to avoid polling. 278 | 279 | `extensions` 280 | 281 | **OPTIONAL** Processor-id-keyed object representing processor-specific elements in the request. See 7.1.2 below. 282 | 283 | #### 7.1.2 Extensions 284 | 285 | OpenDSR requests **MAY** contain an `extensions` object, composed of a series of child-objects, keyed by a processor domain. 286 | 287 | - The domain of each extension **MUST** match the processor's OpenDSR domain, matching the `X-OpenDSR-Processor-Domain` header in OpenDSR responses. 288 | - Extensions **MUST NOT** be used for or contain authentication information. 289 | - Processors **MUST** only implement an extension for items that do not already fit into the generic spec. 290 | 291 | [Currently known extensions can be found here](extensions.md). 292 | 293 | ### 7.2. Example OpenDSR Request 294 | 295 | ```http 296 | POST /requests HTTP/1.1 297 | Host: example-processor.com 298 | Accept: application/json 299 | Content-Type: application/json 300 | 301 | { 302 | "subject_request_id": "a7551968-d5d6-44b2-9831-815ac9017798", 303 | "subject_request_type": "erasure", 304 | "submitted_time": "2018-10-02T15:00:00Z", 305 | "subject_identities": [ 306 | { 307 | "identity_type": "email", 308 | "identity_value": "johndoe@example.com", 309 | "identity_format": "raw" 310 | } 311 | ], 312 | "api_version": "2.0", 313 | "status_callback_urls": [ 314 | "https://examplecontroller.com/opendsr/callbacks" 315 | ], 316 | "extensions": { 317 | "example-processor.com": { 318 | "foo-processor-custom-id":123456, 319 | "property_id": "123456", 320 | }, 321 | "example-other-processor.com": { 322 | "foo-other-processor-custom-id":654321 323 | } 324 | } 325 | } 326 | ``` 327 | 328 | 329 | ### 7.3. OpenDSR Response Properties 330 | 331 | For well formed requests, the OpenDSR service **MUST** respond with HTTP status 332 | code 201 and it **MUST** include the following parameters: 333 | 334 | `controller_id` 335 | 336 | **REQUIRED** string indicating the unique identity of the Controller in the Processor’s system. 337 | 338 | `expected_completion_time` 339 | 340 | **REQUIRED** RFC 3339 date string representing the time when the Processor expects to fulfill the request. 341 | 342 | `received_time` 343 | 344 | **REQUIRED** RFC 3339 date string representing the time when the Processor received the request. 345 | 346 | `encoded_request` 347 | 348 | **REQUIRED** Base64 encoding of the entire body of the OpenDSR request. Controllers **MUST NOT** log or store this. 349 | 350 | `subject_request_id` 351 | 352 | **REQUIRED** UUID v4 string from the originating OpenDSR request. 353 | 354 | `processor_signature` 355 | 356 | **REQUIRED** Base64 encoded signature of the SHA 256 digest of the body of the response. 357 | 358 | ### 7.4. Example OpenDSR Response 359 | 360 | ```http 361 | HTTP/1.1 201 Created 362 | Content-Type: application/json 363 | X-OpenDSR-Processor-Domain: example-processor.com 364 | X-OpenDSR-Signature: 365 | kiGlog3PdQx+FQmB8wYwFC1fekbJG7Dm9WdqgmXc9uKkFRSM4uPzylLi7j083461xLZ+mUloo3tpsmyI 366 | Zpt5eMfgo7ejXPh6lqB4ZgCnN6+1b6Q3NoNcn/+11UOrvmDj772wvg6uIAFzsSVSjMQxRs8LAmHqFO4c 367 | F2pbuoPuK2diHOixxLj6+t97q0nZM7u3wmgkwF9EHIo3C6G1SI04/odvyY/VdMZgj3H1fLnz+X5rc42/ 368 | wU4974u3iBrKgUnv0fcB4YB+L6Q3GsMbmYzuAbe0HpVA17ud/bVoyQZAkrW2yoSy1x4Ts6XKba6pLifI 369 | Hf446Bubsf5r7x1kg6Eo7B8zur666NyWOYrglkOzU4IYO8ifJFRZZXazOgk7ggn9obEd78GBc3kjKKZd 370 | waCrLx7WV5y9TMDCf+2FILOJM/MwTUy1dLZiaFHhGdzld2AjbjK1CfVzyPssch0iQYYtbR49GhumvkYl 371 | 11S4oDfu0c3t/xUCZWg0hoR3XL3B7NjcrlrQinB1KbyTNZccKR0F4Lk9fDgwTVkrAg152UqPyzXxpdzX 372 | jfkDkSEgAevXQwVJWBNf18bMIEgdH2usF/XauQoyrne7rcMIWBISPgtBPj3mhcrwscjGVsxqJva8KCVC 373 | KD/4Axmo9DISib5/7A6uczJxQG2Bcrdj++vQqK2succ= 374 | 375 | { 376 | "controller_id":"example_controller_id", 377 | "expected_completion_time":"2018-11-01T15:00:01Z", 378 | "received_time":"2018-10-02T15:00:01Z", 379 | "encoded_request":"", 380 | "subject_request_id":"a7551968-d5d6-44b2-9831-815ac9017798" 381 | } 382 | ``` 383 | 384 | ### 7.5. OpenDSR Error Response Properties 385 | 386 | For errors, the OpenDSR service **MUST** respond with HTTP status code 400 and 387 | **SHOULD** include the following parameters: 388 | 389 | `error` 390 | 391 | **OPTIONAL** Common error object as defined in section 7.6. 392 | 393 | ### 7.6. Error Object 394 | 395 | Processors **SHOULD** include descriptive error responses. Error 396 | responses **MUST NOT** contain sensitive information related to user identity or 397 | authentication. 398 | 399 | `code` 400 | 401 | **REQUIRED** Integer code indicating the HTTP status of the response. 402 | 403 | `message` 404 | 405 | **OPTIONAL** String description of the issue that was encountered. 406 | 407 | `errors` 408 | 409 | **OPTIONAL** array of the error detail objects including the following fields: "message" "domain", "reason". 410 | 411 | ## 7.7. Example OpenDSR Error Response 412 | 413 | ```http 414 | HTTP/1.1 400 Bad Request 415 | Content-Type: application/json;charset=UTF-8 416 | Cache-Control: no store 417 | Pragma: no cache 418 | 419 | { 420 | "error": { 421 | "code": 400, 422 | "message": "subject_request_id field is required", 423 | "errors": [ 424 | { 425 | "domain": "Validation", 426 | "reason": "IllegalArgumentException", 427 | "message": "subject_request_id field is required." 428 | } 429 | ] 430 | } 431 | } 432 | ``` 433 | 434 | ## 8. OpenDSR Status 435 | 436 | OpenDSR requests **MUST** have an associated status. The following request statuses 437 | are supported: 438 | 439 | `pending` 440 | 441 | Indicates that a well formed request has been received by the Processor. 442 | 443 | `in_progress` 444 | 445 | Indicates that a request is currently being acted on. Processors **SHOULD** indicate this request if possible. 446 | 447 | `completed` 448 | 449 | Indicates that a request has been fulfilled. 450 | 451 | `cancelled` 452 | 453 | Indicates that a request has been cancelled. 454 | 455 | ### 8.1. Request Status Endpoint 456 | 457 | OpenDSR endpoints **MUST** be queryable for request status via an HTTP GET for the 458 | `subject_request_id`. 459 | 460 | ### 8.2. Example Status Request 461 | 462 | ```http 463 | GET /requests/a7551968-d5d6-44b2-9831-815ac9017798 HTTP/1.1 464 | Host: example-processor.com 465 | Accept: application/json 466 | ``` 467 | 468 | ### 8.3. Status Response Properties 469 | 470 | The Status response **MUST** include the following headers: 471 | 472 | `X-OpenDSR-Processor-Domain` 473 | 474 | **REQUIRED** header representing the domain for which the signing 475 | certificate is issued. The domain name **MUST** match the domain on which 476 | OpenDSR requests are received. 477 | 478 | `X-OpenDSR-Signature` 479 | 480 | **REQUIRED** header Base64 encoded signature generated by a certificate 481 | matching the domain in the `X-OpenDSR-Processor-Domain` header. 482 | 483 | The Status body **MUST** include the following properties: 484 | 485 | `controller_id` 486 | 487 | **REQUIRED** string indicating the unique identity of the Controller in the 488 | Processor’s system. 489 | 490 | `expected_completion_time` 491 | 492 | **REQUIRED** RFC 3339 date string representing the time when the 493 | Processor expects to fulfill the request. 494 | 495 | `subject_request_id` 496 | 497 | **REQUIRED** UUID v4 string matching the original OpenDSR request. 498 | 499 | `request_status` 500 | 501 | **REQUIRED** string indicating the status of the request as defined in section 8. 502 | 503 | `api_version` 504 | 505 | **OPTIONAL** Version string representing the desired version of the OpenDSR API 506 | 507 | `results_url` 508 | 509 | **OPTIONAL** A valid URL where the results of the request are available. 510 | 511 | `results_count` 512 | 513 | **OPTIONAL** A count of results found when fulfilling this request. 514 | 515 | ### 8.4. Example Status Response 516 | 517 | ```http 518 | HTTP/1.1 200 OK 519 | Content-Type: application/json 520 | X-OpenDSR-Processor-Domain: example-processor.com 521 | X-OpenDSR-Signature: 522 | kiGlog3PdQx+FQmB8wYwFC1fekbJG7Dm9WdqgmXc9uKkFRSM4uPzylLi7j083461xLZ+mUloo3tpsmyI 523 | Zpt5eMfgo7ejXPh6lqB4ZgCnN6+1b6Q3NoNcn/+11UOrvmDj772wvg6uIAFzsSVSjMQxRs8LAmHqFO4c 524 | F2pbuoPuK2diHOixxLj6+t97q0nZM7u3wmgkwF9EHIo3C6G1SI04/odvyY/VdMZgj3H1fLnz+X5rc42/ 525 | wU4974u3iBrKgUnv0fcB4YB+L6Q3GsMbmYzuAbe0HpVA17ud/bVoyQZAkrW2yoSy1x4Ts6XKba6pLifI 526 | Hf446Bubsf5r7x1kg6Eo7B8zur666NyWOYrglkOzU4IYO8ifJFRZZXazOgk7ggn9obEd78GBc3kjKKZd 527 | waCrLx7WV5y9TMDCf+2FILOJM/MwTUy1dLZiaFHhGdzld2AjbjK1CfVzyPssch0iQYYtbR49GhumvkYl 528 | 11S4oDfu0c3t/xUCZWg0hoR3XL3B7NjcrlrQinB1KbyTNZccKR0F4Lk9fDgwTVkrAg152UqPyzXxpdzX 529 | jfkDkSEgAevXQwVJWBNf18bMIEgdH2usF/XauQoyrne7rcMIWBISPgtBPj3mhcrwscjGVsxqJva8KCVC 530 | KD/4Axmo9DISib5/7A6uczJxQG2Bcrdj++vQqK2succ= 531 | 532 | { 533 | "controller_id":"example_controller_id", 534 | "expected_completion_time":"2018-11-01T15:00:01Z", 535 | "subject_request_id":"a7551968-d5d6-44b2-9831-815ac9017798", 536 | "request_status":"pending", 537 | "api_version":"2.0", 538 | "results_url":"https://exampleprocessor.com/secure/d188d4ba-12db-48a0-898c-cd0f8ba7b345", 539 | "results_count":103 540 | } 541 | ``` 542 | 543 | ### 8.5. Request Status Callback 544 | 545 | OpenDSR requests **SHOULD** contain `status_callback_urls` (see section 7.1). The 546 | following rules govern their use: 547 | - All included callbacks **MUST** be invoked by the Processor on request state 548 | change. 549 | - Processors **MUST** try to send callbacks at least once. Processors **SHOULD** retry callbacks when they have failed. 550 | - Controllers **SHOULD** make all reasonable effort towards a reliable callback 551 | system. 552 | - Processors **SHOULD** monitor for failed callback requests and notify affected 553 | Controllers. 554 | - Controllers **SHOULD** authenticate the validity of the callback. 555 | 556 | ## 8.6. Callback Request 557 | 558 | Callbacks **MUST** include the following headers: 559 | 560 | `X-OpenDSR-Processor-Domain` 561 | 562 | **REQUIRED** header representing the domain for which the signing 563 | certificate is issued. The domain name **MUST** match the domain on which 564 | OpenDSR requests are received. 565 | 566 | `X-OpenDSR-Signature` 567 | 568 | **REQUIRED** header Base64 encoded signature generated by a certificate 569 | matching the domain in the X-OpenDSR-Processor-Domain header. 570 | 571 | The callback body **MUST** include the following parameters: 572 | 573 | `controller_id` 574 | 575 | **REQUIRED** string indicating the unique identity of the Controller in the 576 | Processor's system. 577 | 578 | `status_callback_url` 579 | 580 | **REQUIRED** string matching the callback URL from the OpenDSR request, see section 8. 581 | 582 | `subject_request_id` 583 | 584 | **REQUIRED** UUID v4 string matching the original OpenDSR request. 585 | 586 | `request_status` 587 | 588 | **REQUIRED** string indicating the status of the request. See section 8. 589 | 590 | `expected_completion_time` 591 | 592 | **REQUIRED** RFC 3339 date string representing the time when the Processor expects to fulfill the request. 593 | 594 | `results_url` 595 | 596 | **OPTIONAL** A valid URL where the results of the request are available. 597 | 598 | `results_count` 599 | 600 | **OPTIONAL** A count of results found when fulfilling this request. 601 | 602 | 603 | ### 8.7. Callback Request Example 604 | 605 | ```http 606 | POST /opendsr/callbacks HTTP/1.1 607 | Host: examplecontroller.com 608 | Content-Type: application/json 609 | X-OpenDSR-Processor-Domain: example-processor.com 610 | X-OpenDSR-Signature: 611 | kiGlog3PdQx+FQmB8wYwFC1fekbJG7Dm9WdqgmXc9uKkFRSM4uPzylLi7j083461xLZ+mUloo3tpsmyI 612 | Zpt5eMfgo7ejXPh6lqB4ZgCnN6+1b6Q3NoNcn/+11UOrvmDj772wvg6uIAFzsSVSjMQxRs8LAmHqFO4c 613 | F2pbuoPuK2diHOixxLj6+t97q0nZM7u3wmgkwF9EHIo3C6G1SI04/odvyY/VdMZgj3H1fLnz+X5rc42/ 614 | wU4974u3iBrKgUnv0fcB4YB+L6Q3GsMbmYzuAbe0HpVA17ud/bVoyQZAkrW2yoSy1x4Ts6XKba6pLifI 615 | Hf446Bubsf5r7x1kg6Eo7B8zur666NyWOYrglkOzU4IYO8ifJFRZZXazOgk7ggn9obEd78GBc3kjKKZd 616 | waCrLx7WV5y9TMDCf+2FILOJM/MwTUy1dLZiaFHhGdzld2AjbjK1CfVzyPssch0iQYYtbR49GhumvkYl 617 | 11S4oDfu0c3t/xUCZWg0hoR3XL3B7NjcrlrQinB1KbyTNZccKR0F4Lk9fDgwTVkrAg152UqPyzXxpdzX 618 | jfkDkSEgAevXQwVJWBNf18bMIEgdH2usF/XauQoyrne7rcMIWBISPgtBPj3mhcrwscjGVsxqJva8KCVC 619 | KD/4Axmo9DISib5/7A6uczJxQG2Bcrdj++vQqK2succ= 620 | 621 | { 622 | "controller_id":"example_controller_id", 623 | "expected_completion_time":"2018-11-01T15:00:01Z", 624 | "status_callback_url":"https://examplecontroller.com/opendsr/callbacks", 625 | "subject_request_id":"a7551968-d5d6-44b2-9831-815ac9017798", 626 | "request_status":"pending", 627 | "results_url":"https://exampleprocessor.com/secure/d188d4ba-12db-48a0-898c-cd0f8ba7b345", 628 | "results_count":340 629 | } 630 | ``` 631 | 632 | ### 8.8. Callback Authentication 633 | 634 | In order to authenticate a callback, a Party **SHOULD** perform the following actions: 635 | 1. Read the X-OpenDSR-Processor-Domain request header. 636 | 2. Fetch the public key from a cache based on identity. 637 | 3. If not present in cache, make a call to /discovery of the caller and cache the public key. The Party performing authentication **MAY** whitelist allowed 638 | endpoints. 639 | 4. Validate that the signature in the X-OpenDSR-Signature header is valid for the body of the request. The Party **SHOULD** NOT parse the payload until the signature 640 | has been validated, but rather pass the raw contents into the signature validation function. 641 | 5. Return 403 if validation fails. 642 | 6. Verify the status_callback_url matches the Party's own endpoint. Return if this check fails. 643 | 644 | ## 9. Cancellation Endpoint 645 | OpenDSR endpoints **MUST** accept request cancellations via an HTTP DELETE for the subject_request_id. OpenDSR requests **MAY** be cancelled by the Controller while in status "pending". 646 | 647 | ### 9.1. Example Cancellation Request 648 | 649 | ```http 650 | DELETE /requests/a7551968-d5d6-44b2-9831-815ac9017798 HTTP/1.1 651 | Host: example-processor.com 652 | Accept: application/json 653 | ``` 654 | 655 | ### 9.2. Cancellation Response Properties 656 | For well formed requests, the OpenDSR service **MUST** respond with HTTP status 657 | code 202, and the following parameters: 658 | 659 | `controller_id` 660 | 661 | **REQUIRED** string indicating the unique identity of the Controller in the 662 | Processor's system. 663 | 664 | `received_time` 665 | 666 | **REQUIRED** RFC 3339 date string representing the time when the Processor received the cancellation request. 667 | 668 | `subject_request_id` 669 | 670 | **REQUIRED** UUID v4 string from the originating OpenDSR request. 671 | 672 | `processor_signature` 673 | 674 | **REQUIRED** Base64 encoded signature of the SHA 256 digest of the body of the 675 | response. 676 | 677 | `api_version` 678 | 679 | **OPTIONAL** Version string representing the desired version of the OpenDSR 680 | API. 681 | 682 | 683 | ### 9.3. Example OpenDSR Cancellation Response 684 | 685 | ```http 686 | HTTP/1.1 202 Accepted 687 | Content-Type: application/json 688 | X-OpenDSR-Processor-Domain: example-processor.com 689 | X-OpenDSR-Signature: 690 | kiGlog3PdQx+FQmB8wYwFC1fekbJG7Dm9WdqgmXc9uKkFRSM4uPzylLi7j083461xLZ+mUloo3tpsmyI 691 | Zpt5eMfgo7ejXPh6lqB4ZgCnN6+1b6Q3NoNcn/+11UOrvmDj772wvg6uIAFzsSVSjMQxRs8LAmHqFO4c 692 | F2pbuoPuK2diHOixxLj6+t97q0nZM7u3wmgkwF9EHIo3C6G1SI04/odvyY/VdMZgj3H1fLnz+X5rc42/ 693 | wU4974u3iBrKgUnv0fcB4YB+L6Q3GsMbmYzuAbe0HpVA17ud/bVoyQZAkrW2yoSy1x4Ts6XKba6pLifI 694 | Hf446Bubsf5r7x1kg6Eo7B8zur666NyWOYrglkOzU4IYO8ifJFRZZXazOgk7ggn9obEd78GBc3kjKKZd 695 | waCrLx7WV5y9TMDCf+2FILOJM/MwTUy1dLZiaFHhGdzld2AjbjK1CfVzyPssch0iQYYtbR49GhumvkYl 696 | 11S4oDfu0c3t/xUCZWg0hoR3XL3B7NjcrlrQinB1KbyTNZccKR0F4Lk9fDgwTVkrAg152UqPyzXxpdzX 697 | jfkDkSEgAevXQwVJWBNf18bMIEgdH2usF/XauQoyrne7rcMIWBISPgtBPj3mhcrwscjGVsxqJva8KCVC 698 | KD/4Axmo9DISib5/7A6uczJxQG2Bcrdj++vQqK2succ= 699 | 700 | { 701 | "controller_id": "example_controller_id", 702 | "subject_request_id": "a7551968-d5d6-44b2-9831-815ac9017798", 703 | "received_time": "2018-10-02T15:00:01Z", 704 | "api_version": "2.0" 705 | } 706 | ``` 707 | 708 | ## 10. Best Practices 709 | 710 | All Parties **MUST** make best efforts to not throttle during normal operation. 711 | 712 | Controllers **MAY** use a sub-processor to manage their OpenDSR request infrastructure 713 | to ensure requests are distributed reliably, security signatures are verified and 714 | thorough logs are kept. 715 | 716 | ## 10.1 Migration from OpenGDPR 717 | To maintain backwards compatibility with the name OpenGDPR, all parties are **MUST** honor the prior versions named routes and headers. 718 | 719 | ## 11. Security Considerations 720 | 721 | The intention of this specification is to improve data subject privacy by making it 722 | easier to fulfill their GDPR rights. In doing so, there is a risk to leaking data 723 | subject identities. Implementers are encouraged to take reasonable measures to 724 | safeguard each request and it’s encapsulated identities. 725 | 726 | ## 12. References 727 | 728 | [The EU General Data Protection Regulation](https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32016R0679&from=EN) 729 | --------------------------------------------------------------------------------