├── .gitignore ├── .travis.yml ├── .vscode ├── launch.json └── tasks.json ├── LICENSE ├── README.md ├── docs ├── assets │ ├── css │ │ ├── main.css │ │ └── main.css.map │ ├── images │ │ ├── icons.png │ │ ├── icons@2x.png │ │ ├── widgets.png │ │ └── widgets@2x.png │ └── js │ │ ├── main.js │ │ └── search.js ├── classes │ ├── commit.html │ ├── commitsigner.html │ ├── commitstrategybasic.html │ ├── hubcommitqueryrequest.html │ ├── hubcommitqueryresponse.html │ ├── hubcommitwriterequest.html │ ├── huberror.html │ ├── hubobjectqueryrequest.html │ ├── hubobjectqueryresponse.html │ ├── hubrequest.html │ ├── hubsession.html │ ├── hubwriteresponse.html │ └── signedcommit.html ├── globals.html ├── index.html └── interfaces │ ├── commitsigneroptions.html │ ├── hubsessionoptions.html │ ├── icommitfields.html │ └── icommitsigner.html ├── package-lock.json ├── package.json ├── src ├── Commit.ts ├── CommitStrategyBasic.ts ├── HubError.ts ├── HubSession.ts ├── SignedCommit.ts ├── crypto │ ├── CommitSigner.ts │ └── ICommitSigner.ts ├── example.ts ├── index.ts ├── requests │ ├── HubCommitQueryRequest.ts │ ├── HubObjectQueryRequest.ts │ ├── HubRequest.ts │ └── HubWriteRequest.ts └── responses │ ├── HubCommitQueryResponse.ts │ ├── HubObjectQueryResponse.ts │ └── HubWriteResponse.ts ├── tests ├── Commit.spec.ts ├── CommitStrategyBasic.spec.ts ├── HubError.spec.ts ├── HubSession.spec.ts ├── MockHub.ts ├── MockResolver.ts ├── SignedCommit.spec.ts ├── TestUtils.ts ├── crypto │ └── CommitSigner.spec.ts ├── helpers │ └── reporter.js ├── jasmine.json ├── requests │ ├── HubCommitQueryRequest.spec.ts │ ├── HubObjectQueryRequest.spec.ts │ └── HubWriteRequest.spec.ts └── responses │ ├── HubCommitQueryResponse.spec.ts │ ├── HubObjectQueryResponse.spec.ts │ └── HubWriteResponse.spec.ts ├── tsconfig.json ├── tslint.json └── typedoc.json /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | out/ 3 | node_modules/ 4 | 5 | .nyc_output/ 6 | coverage/ 7 | nunitresults.xml -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "10" 4 | 5 | cache: 6 | directories: 7 | - node_modules 8 | 9 | install: 10 | - npm i 11 | 12 | script: 13 | - npm run build 14 | - npm run check-lint 15 | - npm run test 16 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Run example.ts", 11 | "program": "${workspaceFolder}/out/src/example.js", 12 | "preLaunchTask": "run-npm-build", 13 | }, 14 | { 15 | "type": "node", 16 | "request": "launch", 17 | "name": "Jasmine - All tests", 18 | "program": "${workspaceFolder}/node_modules/jasmine-ts/lib/index.js", 19 | "env": { 20 | "JASMINE_CONFIG_PATH": "${workspaceFolder}/tests/jasmine.json" 21 | }, 22 | "console": "internalConsole", 23 | "smartStep": true, 24 | "protocol": "inspector" 25 | }, 26 | { 27 | "type": "node", 28 | "request": "launch", 29 | "name": "Jasmine - Current file", 30 | "program": "${workspaceFolder}/node_modules/jasmine-ts/lib/index", 31 | "args": ["${file}"], 32 | "env": { 33 | "JASMINE_CONFIG_PATH": "${workspaceFolder}/tests/jasmine.json" 34 | }, 35 | "console": "internalConsole" 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "run-npm-build", 8 | "type": "npm", 9 | "script": "build", 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | }, 14 | "problemMatcher": [ 15 | "$tsc" 16 | ] 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![DIF Logo](https://raw.githubusercontent.com/decentralized-identity/decentralized-identity.github.io/master/images/logo-small.png) 2 | 3 | # Identity Hub JavaScript SDK 4 | 5 | This library provides functionality to read, write, and modify data in a user's identity hub. For an overview of identity hubs, please see the [explanation document](https://github.com/decentralized-identity/identity-hub/blob/master/explainer.md). 6 | 7 | ## API Reference 8 | 9 | For documentation of the available classes and methods, please see the [API documentation](http://decentralized-identity.github.io/hub-sdk-js). 10 | 11 | ## Installation 12 | 13 | Install the library into your project with npm: 14 | 15 | ``` 16 | npm install @decentralized-identity/hub-sdk-js 17 | ``` 18 | 19 | ## Getting Started 20 | 21 | Please see the [example file](https://github.com/decentralized-identity/hub-sdk-js/blob/master/src/example.ts) for a sample of using the SDK to communicate with a Hub. 22 | -------------------------------------------------------------------------------- /docs/assets/images/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decentralized-identity/hub-sdk-js/2b7714f462c88ed17b7bc26c687c34f5b5c55548/docs/assets/images/icons.png -------------------------------------------------------------------------------- /docs/assets/images/icons@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decentralized-identity/hub-sdk-js/2b7714f462c88ed17b7bc26c687c34f5b5c55548/docs/assets/images/icons@2x.png -------------------------------------------------------------------------------- /docs/assets/images/widgets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decentralized-identity/hub-sdk-js/2b7714f462c88ed17b7bc26c687c34f5b5c55548/docs/assets/images/widgets.png -------------------------------------------------------------------------------- /docs/assets/images/widgets@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decentralized-identity/hub-sdk-js/2b7714f462c88ed17b7bc26c687c34f5b5c55548/docs/assets/images/widgets@2x.png -------------------------------------------------------------------------------- /docs/assets/js/search.js: -------------------------------------------------------------------------------- 1 | var typedoc = typedoc || {}; 2 | typedoc.search = typedoc.search || {}; 3 | typedoc.search.data = {"kinds":{"32":"Variable","128":"Class","256":"Interface","512":"Constructor","1024":"Property","2048":"Method","2097152":"Object literal"},"rows":[{"id":0,"kind":128,"name":"SignedCommit","url":"classes/signedcommit.html","classes":"tsd-kind-class"},{"id":1,"kind":512,"name":"constructor","url":"classes/signedcommit.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class","parent":"SignedCommit"},{"id":2,"kind":1024,"name":"json","url":"classes/signedcommit.html#json","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"SignedCommit"},{"id":3,"kind":2048,"name":"toFlattenedJson","url":"classes/signedcommit.html#toflattenedjson","classes":"tsd-kind-method tsd-parent-kind-class","parent":"SignedCommit"},{"id":4,"kind":2048,"name":"getProtectedHeaders","url":"classes/signedcommit.html#getprotectedheaders","classes":"tsd-kind-method tsd-parent-kind-class","parent":"SignedCommit"},{"id":5,"kind":2048,"name":"getPayload","url":"classes/signedcommit.html#getpayload","classes":"tsd-kind-method tsd-parent-kind-class","parent":"SignedCommit"},{"id":6,"kind":2048,"name":"getRevision","url":"classes/signedcommit.html#getrevision","classes":"tsd-kind-method tsd-parent-kind-class","parent":"SignedCommit"},{"id":7,"kind":2048,"name":"getObjectId","url":"classes/signedcommit.html#getobjectid","classes":"tsd-kind-method tsd-parent-kind-class","parent":"SignedCommit"},{"id":8,"kind":256,"name":"ICommitSigner","url":"interfaces/icommitsigner.html","classes":"tsd-kind-interface"},{"id":9,"kind":2048,"name":"sign","url":"interfaces/icommitsigner.html#sign","classes":"tsd-kind-method tsd-parent-kind-interface","parent":"ICommitSigner"},{"id":10,"kind":256,"name":"CommitSignerOptions","url":"interfaces/commitsigneroptions.html","classes":"tsd-kind-interface tsd-is-not-exported"},{"id":11,"kind":1024,"name":"did","url":"interfaces/commitsigneroptions.html#did","classes":"tsd-kind-property tsd-parent-kind-interface tsd-is-not-exported","parent":"CommitSignerOptions"},{"id":12,"kind":1024,"name":"key","url":"interfaces/commitsigneroptions.html#key","classes":"tsd-kind-property tsd-parent-kind-interface tsd-is-not-exported","parent":"CommitSignerOptions"},{"id":13,"kind":1024,"name":"cryptoSuite","url":"interfaces/commitsigneroptions.html#cryptosuite","classes":"tsd-kind-property tsd-parent-kind-interface tsd-is-not-exported","parent":"CommitSignerOptions"},{"id":14,"kind":128,"name":"CommitSigner","url":"classes/commitsigner.html","classes":"tsd-kind-class"},{"id":15,"kind":1024,"name":"did","url":"classes/commitsigner.html#did","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"CommitSigner"},{"id":16,"kind":1024,"name":"key","url":"classes/commitsigner.html#key","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"CommitSigner"},{"id":17,"kind":1024,"name":"cryptoSuite","url":"classes/commitsigner.html#cryptosuite","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"CommitSigner"},{"id":18,"kind":512,"name":"constructor","url":"classes/commitsigner.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class","parent":"CommitSigner"},{"id":19,"kind":2048,"name":"sign","url":"classes/commitsigner.html#sign","classes":"tsd-kind-method tsd-parent-kind-class","parent":"CommitSigner"},{"id":20,"kind":128,"name":"HubRequest","url":"classes/hubrequest.html","classes":"tsd-kind-class"},{"id":21,"kind":1024,"name":"requestType","url":"classes/hubrequest.html#requesttype","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"HubRequest"},{"id":22,"kind":1024,"name":"requestBody","url":"classes/hubrequest.html#requestbody","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"HubRequest"},{"id":23,"kind":512,"name":"constructor","url":"classes/hubrequest.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class","parent":"HubRequest"},{"id":24,"kind":2048,"name":"getRequestJson","url":"classes/hubrequest.html#getrequestjson","classes":"tsd-kind-method tsd-parent-kind-class","parent":"HubRequest"},{"id":25,"kind":128,"name":"HubObjectQueryRequest","url":"classes/hubobjectqueryrequest.html","classes":"tsd-kind-class"},{"id":26,"kind":1024,"name":"_isObjectQueryRequest","url":"classes/hubobjectqueryrequest.html#_isobjectqueryrequest","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"HubObjectQueryRequest"},{"id":27,"kind":512,"name":"constructor","url":"classes/hubobjectqueryrequest.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class tsd-is-overwrite","parent":"HubObjectQueryRequest"},{"id":28,"kind":2048,"name":"getRequestJson","url":"classes/hubobjectqueryrequest.html#getrequestjson","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"HubObjectQueryRequest"},{"id":29,"kind":128,"name":"HubCommitQueryRequest","url":"classes/hubcommitqueryrequest.html","classes":"tsd-kind-class"},{"id":30,"kind":1024,"name":"_isCommitQueryRequest","url":"classes/hubcommitqueryrequest.html#_iscommitqueryrequest","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"HubCommitQueryRequest"},{"id":31,"kind":512,"name":"constructor","url":"classes/hubcommitqueryrequest.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class tsd-is-overwrite","parent":"HubCommitQueryRequest"},{"id":32,"kind":2048,"name":"getRequestJson","url":"classes/hubcommitqueryrequest.html#getrequestjson","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"HubCommitQueryRequest"},{"id":33,"kind":128,"name":"HubCommitWriteRequest","url":"classes/hubcommitwriterequest.html","classes":"tsd-kind-class"},{"id":34,"kind":1024,"name":"_isWriteRequest","url":"classes/hubcommitwriterequest.html#_iswriterequest","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"HubCommitWriteRequest"},{"id":35,"kind":512,"name":"constructor","url":"classes/hubcommitwriterequest.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class tsd-is-overwrite","parent":"HubCommitWriteRequest"},{"id":36,"kind":2048,"name":"getRequestJson","url":"classes/hubcommitwriterequest.html#getrequestjson","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"HubCommitWriteRequest"},{"id":37,"kind":128,"name":"HubObjectQueryResponse","url":"classes/hubobjectqueryresponse.html","classes":"tsd-kind-class"},{"id":38,"kind":1024,"name":"response","url":"classes/hubobjectqueryresponse.html#response","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"HubObjectQueryResponse"},{"id":39,"kind":512,"name":"constructor","url":"classes/hubobjectqueryresponse.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class","parent":"HubObjectQueryResponse"},{"id":40,"kind":2048,"name":"getObjects","url":"classes/hubobjectqueryresponse.html#getobjects","classes":"tsd-kind-method tsd-parent-kind-class","parent":"HubObjectQueryResponse"},{"id":41,"kind":2048,"name":"hasSkipToken","url":"classes/hubobjectqueryresponse.html#hasskiptoken","classes":"tsd-kind-method tsd-parent-kind-class","parent":"HubObjectQueryResponse"},{"id":42,"kind":2048,"name":"getSkipToken","url":"classes/hubobjectqueryresponse.html#getskiptoken","classes":"tsd-kind-method tsd-parent-kind-class","parent":"HubObjectQueryResponse"},{"id":43,"kind":128,"name":"HubCommitQueryResponse","url":"classes/hubcommitqueryresponse.html","classes":"tsd-kind-class"},{"id":44,"kind":512,"name":"constructor","url":"classes/hubcommitqueryresponse.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class","parent":"HubCommitQueryResponse"},{"id":45,"kind":1024,"name":"response","url":"classes/hubcommitqueryresponse.html#response","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"HubCommitQueryResponse"},{"id":46,"kind":2048,"name":"getCommits","url":"classes/hubcommitqueryresponse.html#getcommits","classes":"tsd-kind-method tsd-parent-kind-class","parent":"HubCommitQueryResponse"},{"id":47,"kind":2048,"name":"hasSkipToken","url":"classes/hubcommitqueryresponse.html#hasskiptoken","classes":"tsd-kind-method tsd-parent-kind-class","parent":"HubCommitQueryResponse"},{"id":48,"kind":2048,"name":"getSkipToken","url":"classes/hubcommitqueryresponse.html#getskiptoken","classes":"tsd-kind-method tsd-parent-kind-class","parent":"HubCommitQueryResponse"},{"id":49,"kind":128,"name":"HubWriteResponse","url":"classes/hubwriteresponse.html","classes":"tsd-kind-class"},{"id":50,"kind":512,"name":"constructor","url":"classes/hubwriteresponse.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class","parent":"HubWriteResponse"},{"id":51,"kind":1024,"name":"response","url":"classes/hubwriteresponse.html#response","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"HubWriteResponse"},{"id":52,"kind":2048,"name":"getRevisions","url":"classes/hubwriteresponse.html#getrevisions","classes":"tsd-kind-method tsd-parent-kind-class","parent":"HubWriteResponse"},{"id":53,"kind":128,"name":"CommitStrategyBasic","url":"classes/commitstrategybasic.html","classes":"tsd-kind-class"},{"id":54,"kind":2048,"name":"resolveObject","url":"classes/commitstrategybasic.html#resolveobject","classes":"tsd-kind-method tsd-parent-kind-class","parent":"CommitStrategyBasic"},{"id":55,"kind":2048,"name":"compareCommits","url":"classes/commitstrategybasic.html#comparecommits","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-protected","parent":"CommitStrategyBasic"},{"id":56,"kind":128,"name":"HubError","url":"classes/huberror.html","classes":"tsd-kind-class"},{"id":57,"kind":1024,"name":"__hubError","url":"classes/huberror.html#__huberror","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"HubError"},{"id":58,"kind":2048,"name":"is","url":"classes/huberror.html#is","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-static","parent":"HubError"},{"id":59,"kind":512,"name":"constructor","url":"classes/huberror.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class","parent":"HubError"},{"id":60,"kind":1024,"name":"body","url":"classes/huberror.html#body","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"HubError"},{"id":61,"kind":2048,"name":"getErrorCode","url":"classes/huberror.html#geterrorcode","classes":"tsd-kind-method tsd-parent-kind-class","parent":"HubError"},{"id":62,"kind":2048,"name":"getTarget","url":"classes/huberror.html#gettarget","classes":"tsd-kind-method tsd-parent-kind-class","parent":"HubError"},{"id":63,"kind":2048,"name":"getRawError","url":"classes/huberror.html#getrawerror","classes":"tsd-kind-method tsd-parent-kind-class","parent":"HubError"},{"id":64,"kind":1024,"name":"name","url":"classes/huberror.html#name","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-inherited","parent":"HubError"},{"id":65,"kind":1024,"name":"message","url":"classes/huberror.html#message","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-inherited","parent":"HubError"},{"id":66,"kind":1024,"name":"stack","url":"classes/huberror.html#stack","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-overwrite tsd-is-inherited","parent":"HubError"},{"id":67,"kind":1024,"name":"Error","url":"classes/huberror.html#error","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-static","parent":"HubError"},{"id":68,"kind":256,"name":"HubSessionOptions","url":"interfaces/hubsessionoptions.html","classes":"tsd-kind-interface"},{"id":69,"kind":1024,"name":"clientDid","url":"interfaces/hubsessionoptions.html#clientdid","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"HubSessionOptions"},{"id":70,"kind":1024,"name":"clientPrivateKeyReference","url":"interfaces/hubsessionoptions.html#clientprivatekeyreference","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"HubSessionOptions"},{"id":71,"kind":1024,"name":"targetDid","url":"interfaces/hubsessionoptions.html#targetdid","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"HubSessionOptions"},{"id":72,"kind":1024,"name":"hubDid","url":"interfaces/hubsessionoptions.html#hubdid","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"HubSessionOptions"},{"id":73,"kind":1024,"name":"hubEndpoint","url":"interfaces/hubsessionoptions.html#hubendpoint","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"HubSessionOptions"},{"id":74,"kind":1024,"name":"resolver","url":"interfaces/hubsessionoptions.html#resolver","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"HubSessionOptions"},{"id":75,"kind":1024,"name":"cryptoSuites","url":"interfaces/hubsessionoptions.html#cryptosuites","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"HubSessionOptions"},{"id":76,"kind":1024,"name":"keyStore","url":"interfaces/hubsessionoptions.html#keystore","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"HubSessionOptions"},{"id":77,"kind":128,"name":"HubSession","url":"classes/hubsession.html","classes":"tsd-kind-class"},{"id":78,"kind":1024,"name":"clientDid","url":"classes/hubsession.html#clientdid","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"HubSession"},{"id":79,"kind":1024,"name":"hubDid","url":"classes/hubsession.html#hubdid","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"HubSession"},{"id":80,"kind":1024,"name":"hubEndpoint","url":"classes/hubsession.html#hubendpoint","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"HubSession"},{"id":81,"kind":1024,"name":"targetDid","url":"classes/hubsession.html#targetdid","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"HubSession"},{"id":82,"kind":1024,"name":"currentAccessToken","url":"classes/hubsession.html#currentaccesstoken","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"HubSession"},{"id":83,"kind":1024,"name":"authentication","url":"classes/hubsession.html#authentication","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"HubSession"},{"id":84,"kind":512,"name":"constructor","url":"classes/hubsession.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class","parent":"HubSession"},{"id":85,"kind":2048,"name":"send","url":"classes/hubsession.html#send","classes":"tsd-kind-method tsd-parent-kind-class","parent":"HubSession"},{"id":86,"kind":2048,"name":"makeRequest","url":"classes/hubsession.html#makerequest","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-private","parent":"HubSession"},{"id":87,"kind":2048,"name":"callFetch","url":"classes/hubsession.html#callfetch","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-private","parent":"HubSession"},{"id":88,"kind":2048,"name":"getAccessToken","url":"classes/hubsession.html#getaccesstoken","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-private","parent":"HubSession"},{"id":89,"kind":2048,"name":"refreshAccessToken","url":"classes/hubsession.html#refreshaccesstoken","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-private","parent":"HubSession"},{"id":90,"kind":2097152,"name":"responseTypes","url":"classes/hubsession.html#responsetypes","classes":"tsd-kind-object-literal tsd-parent-kind-class tsd-is-private tsd-is-static","parent":"HubSession"},{"id":91,"kind":32,"name":"WriteResponse","url":"classes/hubsession.html#responsetypes.writeresponse","classes":"tsd-kind-variable tsd-parent-kind-object-literal","parent":"HubSession.responseTypes"},{"id":92,"kind":32,"name":"ObjectQueryResponse","url":"classes/hubsession.html#responsetypes.objectqueryresponse","classes":"tsd-kind-variable tsd-parent-kind-object-literal","parent":"HubSession.responseTypes"},{"id":93,"kind":32,"name":"CommitQueryResponse","url":"classes/hubsession.html#responsetypes.commitqueryresponse","classes":"tsd-kind-variable tsd-parent-kind-object-literal","parent":"HubSession.responseTypes"},{"id":94,"kind":2048,"name":"mapResponseToObject","url":"classes/hubsession.html#mapresponsetoobject","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-private tsd-is-static","parent":"HubSession"},{"id":95,"kind":256,"name":"ICommitFields","url":"interfaces/icommitfields.html","classes":"tsd-kind-interface"},{"id":96,"kind":1024,"name":"protected","url":"interfaces/icommitfields.html#protected","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"ICommitFields"},{"id":97,"kind":1024,"name":"header","url":"interfaces/icommitfields.html#header","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"ICommitFields"},{"id":98,"kind":1024,"name":"payload","url":"interfaces/icommitfields.html#payload","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"ICommitFields"},{"id":99,"kind":128,"name":"Commit","url":"classes/commit.html","classes":"tsd-kind-class"},{"id":100,"kind":1024,"name":"fields","url":"classes/commit.html#fields","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"Commit"},{"id":101,"kind":512,"name":"constructor","url":"classes/commit.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class","parent":"Commit"},{"id":102,"kind":2048,"name":"validate","url":"classes/commit.html#validate","classes":"tsd-kind-method tsd-parent-kind-class","parent":"Commit"},{"id":103,"kind":2048,"name":"isValid","url":"classes/commit.html#isvalid","classes":"tsd-kind-method tsd-parent-kind-class","parent":"Commit"},{"id":104,"kind":2048,"name":"getProtectedHeaders","url":"classes/commit.html#getprotectedheaders","classes":"tsd-kind-method tsd-parent-kind-class","parent":"Commit"},{"id":105,"kind":2048,"name":"getUnprotectedHeaders","url":"classes/commit.html#getunprotectedheaders","classes":"tsd-kind-method tsd-parent-kind-class","parent":"Commit"},{"id":106,"kind":2048,"name":"getPayload","url":"classes/commit.html#getpayload","classes":"tsd-kind-method tsd-parent-kind-class","parent":"Commit"},{"id":107,"kind":2048,"name":"sign","url":"classes/commit.html#sign","classes":"tsd-kind-method tsd-parent-kind-class","parent":"Commit"}]}; -------------------------------------------------------------------------------- /docs/classes/hubcommitwriterequest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | HubCommitWriteRequest | @decentralized-identity/hub-sdk-js 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 62 |

Class HubCommitWriteRequest

63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |

Represents a request to commit the given Commit object to an Identity Hub.

73 |
74 |
75 |
76 |
77 |

Hierarchy

78 |
    79 |
  • 80 | HubRequest 81 |
      82 |
    • 83 | HubCommitWriteRequest 84 |
    • 85 |
    86 |
  • 87 |
88 |
89 |
90 |

Index

91 |
92 |
93 |
94 |

Constructors

95 | 98 |
99 |
100 |

Properties

101 | 104 |
105 |
106 |

Methods

107 | 110 |
111 |
112 |
113 |
114 |
115 |

Constructors

116 |
117 | 118 |

constructor

119 | 122 | 139 |
140 |
141 |
142 |

Properties

143 |
144 | 145 |

Private _isWriteRequest

146 |
_isWriteRequest: true = true
147 | 152 |
153 |
154 |
155 |

Methods

156 |
157 | 158 |

getRequestJson

159 |
    160 |
  • getRequestJson(): Promise<any>
  • 161 |
162 |
    163 |
  • 164 | 170 |
    171 |
    172 |

    Returns the raw request JSON which will be sent to the Hub.

    173 |
    174 |
    175 |

    Returns Promise<any>

    176 |
  • 177 |
178 |
179 |
180 |
181 | 260 |
261 |
262 | 321 |
322 |

Generated using TypeDoc

323 |
324 |
325 | 326 | 327 | 328 | -------------------------------------------------------------------------------- /docs/classes/hubwriteresponse.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | HubWriteResponse | @decentralized-identity/hub-sdk-js 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 62 |

Class HubWriteResponse

63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |

Represents the response to a HubWriteRequest.

73 |
74 |
75 |
76 |
77 |

Hierarchy

78 |
    79 |
  • 80 | HubWriteResponse 81 |
  • 82 |
83 |
84 |
85 |

Index

86 |
87 |
88 |
89 |

Constructors

90 | 93 |
94 |
95 |

Properties

96 | 99 |
100 |
101 |

Methods

102 | 105 |
106 |
107 |
108 |
109 |
110 |

Constructors

111 |
112 | 113 |

constructor

114 |
    115 |
  • new HubWriteResponse(response: IHubWriteResponse): HubWriteResponse
  • 116 |
117 | 133 |
134 |
135 |
136 |

Properties

137 |
138 | 139 |

Private response

140 |
response: IHubWriteResponse
141 | 146 |
147 |
148 |
149 |

Methods

150 |
151 | 152 |

getRevisions

153 |
    154 |
  • getRevisions(): string[]
  • 155 |
156 |
    157 |
  • 158 | 163 |
    164 |
    165 |

    Returns the list of known revisions for the object which was created/modified.

    166 |
    167 |
    168 |

    Returns string[]

    169 |
  • 170 |
171 |
172 |
173 |
174 | 253 |
254 |
255 | 314 |
315 |

Generated using TypeDoc

316 |
317 |
318 | 319 | 320 | 321 | -------------------------------------------------------------------------------- /docs/globals.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @decentralized-identity/hub-sdk-js 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 59 |

@decentralized-identity/hub-sdk-js

60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |

Index

68 |
69 |
70 |
71 |

Classes

72 | 87 |
88 |
89 |

Interfaces

90 | 96 |
97 |
98 |
99 |
100 |
101 | 165 |
166 |
167 | 226 |
227 |

Generated using TypeDoc

228 |
229 |
230 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @decentralized-identity/hub-sdk-js 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 59 |

@decentralized-identity/hub-sdk-js

60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |

DIF Logo

68 |

Identity Hub JavaScript SDK

69 |

This library provides functionality to read, write, and modify data in a user's identity hub. For an overview of identity hubs, please see the explanation document.

70 |

API Reference

71 |

For documentation of the available classes and methods, please see the API documentation.

72 |

Installation

73 |

Install the library into your project with npm:

74 |
npm install @decentralized-identity/hub-sdk-js

Getting Started

75 |

Please see the example file for a sample of using the SDK to communicate with a Hub.

76 |
77 |
78 | 142 |
143 |
144 | 203 |
204 |

Generated using TypeDoc

205 |
206 |
207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /docs/interfaces/commitsigneroptions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CommitSignerOptions | @decentralized-identity/hub-sdk-js 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 62 |

Interface CommitSignerOptions

63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |

Hierarchy

71 |
    72 |
  • 73 | CommitSignerOptions 74 |
  • 75 |
76 |
77 |
78 |

Index

79 |
80 |
81 |
82 |

Properties

83 | 88 |
89 |
90 |
91 |
92 |
93 |

Properties

94 |
95 | 96 |

Optional cryptoSuite

97 |
cryptoSuite: CryptoSuite
98 | 103 |
104 |
105 |

The CryptoSuite to be used to for the algorithm to use to sign the commit

106 |
107 |
108 |
109 |
110 | 111 |

did

112 |
did: string
113 | 118 |
119 |
120 |

The DID of the identity that will the commit.

121 |
122 |
123 |
124 |
125 | 126 |

key

127 |
key: PrivateKey
128 | 133 |
134 |
135 |

The private key to be used to sign the commit.

136 |
137 |
138 |
139 |
140 |
141 | 220 |
221 |
222 | 281 |
282 |

Generated using TypeDoc

283 |
284 |
285 | 286 | 287 | 288 | -------------------------------------------------------------------------------- /docs/interfaces/icommitfields.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ICommitFields | @decentralized-identity/hub-sdk-js 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 62 |

Interface ICommitFields

63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |

Fields that can be specified when creating a new commit.

73 |
74 |
75 |
76 |
77 |

Hierarchy

78 |
    79 |
  • 80 | ICommitFields 81 |
  • 82 |
83 |
84 |
85 |

Index

86 |
87 |
88 |
89 |

Properties

90 | 95 |
96 |
97 |
98 |
99 |
100 |

Properties

101 |
102 | 103 |

Optional header

104 |
header: Partial<ICommitUnprotectedHeaders>
105 | 110 |
111 |
112 |

Fields to include in the unprotected (unverified) commit header.

113 |
114 |
115 |
116 |
117 | 118 |

payload

119 |
payload: object | string
120 | 125 |
126 |
127 |

The application-specific commit payload.

128 |
129 |
130 |
131 |
132 | 133 |

protected

134 |
protected: Partial<ICommitProtectedHeaders>
135 | 140 |
141 |
142 |

Fields to include in the protected (signed) commit header.

143 |
144 |
145 |
146 |
147 |
148 | 227 |
228 |
229 | 288 |
289 |

Generated using TypeDoc

290 |
291 |
292 | 293 | 294 | 295 | -------------------------------------------------------------------------------- /docs/interfaces/icommitsigner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ICommitSigner | @decentralized-identity/hub-sdk-js 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 62 |

Interface ICommitSigner

63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |

Interface representing an object which can sign a commit.

73 |
74 |
75 |
76 |
77 |

Hierarchy

78 |
    79 |
  • 80 | ICommitSigner 81 |
  • 82 |
83 |
84 |
85 |

Implemented by

86 | 89 |
90 |
91 |

Index

92 |
93 |
94 |
95 |

Methods

96 | 99 |
100 |
101 |
102 |
103 |
104 |

Methods

105 |
106 | 107 |

sign

108 | 111 |
    112 |
  • 113 | 118 |
    119 |
    120 |

    Signs the given commit.

    121 |
    122 |
    123 |

    Parameters

    124 |
      125 |
    • 126 |
      commit: Commit
      127 |
      128 |

      The commit to sign.

      129 |
      130 |
    • 131 |
    132 |

    Returns Promise<SignedCommit>

    133 |
  • 134 |
135 |
136 |
137 |
138 | 211 |
212 |
213 | 272 |
273 |

Generated using TypeDoc

274 |
275 |
276 | 277 | 278 | 279 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@decentralized-identity/hub-sdk-js", 3 | "version": "0.1.3", 4 | "description": "JavaScript SDK for communicating with Identity Hub instances.", 5 | "main": "out/src/index.js", 6 | "scripts": { 7 | "precommit": "npm run lint", 8 | "build": "tsc", 9 | "test": "nyc jasmine-ts --config=./tests/jasmine.json", 10 | "lint": "tslint --fix --project .", 11 | "check-lint": "tslint --project .", 12 | "docs": "typedoc ./src" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/decentralized-identity/hub-sdk-js.git" 17 | }, 18 | "author": "Decentralized Identity Foundation", 19 | "license": "Apache-2.0", 20 | "bugs": { 21 | "url": "https://github.com/decentralized-identity/hub-sdk-js/issues" 22 | }, 23 | "homepage": "https://github.com/decentralized-identity/hub-sdk-js#readme", 24 | "dependencies": { 25 | "@decentralized-identity/did-auth-jose": "0.1.12", 26 | "@decentralized-identity/did-common-typescript": "0.1.18", 27 | "@decentralized-identity/did-crypto-typescript": "0.1.6", 28 | "@decentralized-identity/hub-common-js": "0.1.2", 29 | "node-fetch": "2.3.0", 30 | "object-assign": "4.1.1" 31 | }, 32 | "devDependencies": { 33 | "@types/jasmine": "3.3.8", 34 | "@types/lodash": "4.14.120", 35 | "@types/node": "10.12.19", 36 | "@types/node-fetch": "2.1.4", 37 | "@types/object-assign": "4.0.30", 38 | "jasmine": "3.3.1", 39 | "jasmine-reporters": "2.3.2", 40 | "jasmine-spec-reporter": "4.2.1", 41 | "jasmine-ts": "0.3.0", 42 | "lodash": "4.17.11", 43 | "nyc": "14.1.0", 44 | "ts-node": "7.0.1", 45 | "tslint": "5.12.1", 46 | "tslint-config-airbnb": "5.11.1", 47 | "typedoc": "0.14.2", 48 | "typescript": "3.2.2" 49 | }, 50 | "nyc": { 51 | "extension": [ 52 | ".ts", 53 | ".tsx" 54 | ], 55 | "exclude": [ 56 | "**/*.d.ts" 57 | ], 58 | "include": [ 59 | "src/**" 60 | ], 61 | "reporter": [ 62 | "text", 63 | "cobertura", 64 | "html" 65 | ] 66 | }, 67 | "types": "out/src/index.d.ts", 68 | "files": [ 69 | "out/src/**/*" 70 | ] 71 | } 72 | -------------------------------------------------------------------------------- /src/Commit.ts: -------------------------------------------------------------------------------- 1 | import { ICommitProtectedHeaders, ICommitUnprotectedHeaders, CommitOperation } from '@decentralized-identity/hub-common-js'; 2 | import ICommitSigner from './crypto/ICommitSigner'; 3 | import { SignedCommit } from './index'; 4 | 5 | /** 6 | * Fields that can be specified when creating a new commit. 7 | */ 8 | export interface ICommitFields { 9 | 10 | /** Fields to include in the protected (signed) commit header. */ 11 | protected: Partial; 12 | 13 | /** Fields to include in the unprotected (unverified) commit header. */ 14 | header?: Partial; 15 | 16 | /** The application-specific commit payload. */ 17 | payload: object | string; 18 | 19 | } 20 | 21 | /** 22 | * Represents a new (i.e pending, unsigned) commit which will create, update, or delete an object in 23 | * a user's Identity Hub. 24 | */ 25 | export default class Commit { 26 | 27 | private fields: ICommitFields; 28 | 29 | constructor(fields: ICommitFields) { 30 | this.fields = fields; 31 | } 32 | 33 | /** 34 | * Verifies whether the currently set fields constitute a valid commit which can be 35 | * signed/encrypted and stored in an Identity Hub. 36 | * 37 | * Throws an error if the commit is not valid. 38 | * 39 | * TODO: Move validation logic to hub-common-js repository to be shared with hub-node-core. 40 | */ 41 | public validate() { 42 | if (!this.fields.protected) { 43 | throw new Error("Commit must specify the 'protected' field."); 44 | } 45 | 46 | const protectedHeaders = this.fields.protected as any; 47 | 48 | const requiredStrings = ['interface', 'context', 'type', 'committed_at', 'commit_strategy', 'sub']; 49 | requiredStrings.forEach((field) => { 50 | if (!protectedHeaders[field] || typeof protectedHeaders[field] !== 'string' || protectedHeaders[field].length === 0) { 51 | throw new Error(`Commit 'protected.${field}' field must be a non-zero-length string.`); 52 | } 53 | }); 54 | 55 | if (!this.fields.protected.operation) { 56 | throw new Error("Commit 'protected.operation' field must be specified."); 57 | } 58 | 59 | if (['create', 'update', 'delete'].indexOf(this.fields.protected.operation) === -1) { 60 | throw new Error("Commit 'protected.operation' field must be one of create, update, or delete."); 61 | } 62 | 63 | if (this.fields.protected.operation === 'create') { 64 | if (this.fields.protected.object_id !== undefined) { 65 | throw new Error("Commit 'protected.object_id' field must not be specified when operation is 'create'."); 66 | } 67 | } else { 68 | if (!this.fields.protected.object_id) { 69 | throw new Error(`Commit 'protected.object_id' field must be specified when operation is '${this.fields.protected.operation}'.`); 70 | } 71 | } 72 | 73 | if (!this.fields.payload) { 74 | throw new Error("Commit must specify the 'payload' field."); 75 | } 76 | 77 | if (['string', 'object'].indexOf(typeof this.fields.payload) === -1) { 78 | throw new Error(`Commit payload must be string or object, ${typeof this.fields.payload} given.`); 79 | } 80 | } 81 | 82 | /** 83 | * Returns true if the validate() method would pass without error. 84 | */ 85 | public isValid() { 86 | try { 87 | this.validate(); 88 | return true; 89 | } catch (err) { 90 | return false; 91 | } 92 | } 93 | 94 | /** 95 | * Returns the headers which will be signed/encrypted. 96 | */ 97 | getProtectedHeaders(): Partial { 98 | return this.fields.protected; 99 | } 100 | 101 | /** 102 | * Returns the (optional) headers which will not be signed/encrypted. 103 | */ 104 | getUnprotectedHeaders(): {[key: string]: any} { 105 | return this.fields.header || {}; 106 | } 107 | 108 | /** 109 | * Returns the application-specific payload for this commit. 110 | */ 111 | getPayload(): any { 112 | return this.fields.payload; 113 | } 114 | 115 | /** 116 | * Returns a copy of this commit signed with the given signer. 117 | * 118 | * @param signer The signer to use to sign the commit. 119 | */ 120 | sign(signer: ICommitSigner): Promise { 121 | return signer.sign(this); 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/CommitStrategyBasic.ts: -------------------------------------------------------------------------------- 1 | import SignedCommit from './SignedCommit'; 2 | 3 | /** 4 | * Resolves the final state of an object from the constituent set of commits for that object. 5 | * 6 | * This class works only with objects using the `basic` commit strategy. 7 | */ 8 | export default class CommitStrategyBasic { 9 | 10 | /** 11 | * Resolves the current state of an object with the `basic` commit strategy. 12 | * 13 | * TODO: This class currently returns only the raw object payload. Once we add an object instance 14 | * class to the SDK (e.g. `HubObject`), this method will no longer be called directly, and will 15 | * also need to return the app-readable object metadata. 16 | * 17 | * Currently returns `null` if the object was deleted, otherwise returns the most recent payload. 18 | * 19 | * @param commits The entire known set of commits for the object. 20 | */ 21 | async resolveObject(commits: SignedCommit[]) { 22 | 23 | if (!commits || commits.length === 0) { 24 | return null; 25 | } 26 | 27 | // tslint:disable:align 28 | const currentState = commits.reduce((latest, candidate) => { 29 | return this.compareCommits(latest, candidate) < 0 30 | ? candidate 31 | : latest; 32 | }, commits[0]); 33 | // tslint:enable:align 34 | 35 | return currentState.getProtectedHeaders().operation === 'delete' 36 | ? null 37 | : currentState.getPayload(); 38 | } 39 | 40 | /** 41 | * Compares two commits (which must belong to the same object) in order to evaulate which one is 42 | * more recent. 43 | * 44 | * Follows the conventions of the JavaScript sort() method: 45 | * - `-1` indicates that a comes before (i.e. is older than b) 46 | * - `1` indicates that a comes after (i.e. is newer than b) 47 | * 48 | * @param a The first commit to compare. 49 | * @param b The second commit to compare. 50 | */ 51 | protected compareCommits(a: SignedCommit, b: SignedCommit) { 52 | if (a.getObjectId() !== b.getObjectId()) { 53 | throw new Error('Cannot compare commits from different objects.'); 54 | } 55 | 56 | const aHeaders = a.getProtectedHeaders(); 57 | const bHeaders = b.getProtectedHeaders(); 58 | 59 | if (aHeaders.operation === 'create') return -1; 60 | if (bHeaders.operation === 'create') return 1; 61 | 62 | if (aHeaders.operation === 'delete') return 1; 63 | if (bHeaders.operation === 'delete') return -1; 64 | 65 | const aDate = Date.parse(aHeaders.committed_at); 66 | const bDate = Date.parse(bHeaders.committed_at); 67 | 68 | if (aDate !== bDate) { 69 | return aDate < bDate ? -1 : 1; 70 | } 71 | 72 | return a.getRevision() < b.getRevision() ? -1 : 1; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/HubError.ts: -------------------------------------------------------------------------------- 1 | import { IHubError, HubErrorCode } from '@decentralized-identity/hub-common-js'; 2 | 3 | /** 4 | * Represents an error returned by an Identity Hub. 5 | */ 6 | export default class HubError extends Error { 7 | 8 | // tslint:disable-next-line:variable-name 9 | private __hubError = true; 10 | 11 | /** 12 | * Indicates whether the passed-in object is a HubError instance. 13 | */ 14 | public static is(err: any): err is HubError { 15 | return err.__hubError || false; 16 | } 17 | 18 | constructor (private body: IHubError) { 19 | super(`Identity Hub Error: ${body.developer_message || body.error_code || 'Unknown error'}`); 20 | 21 | // NOTE: Extending 'Error' breaks prototype chain since TypeScript 2.1. 22 | // The following line restores prototype chain. 23 | if ((Object as any).setPrototypeOf) (Object as any).setPrototypeOf(this, new.target.prototype); 24 | } 25 | 26 | /** 27 | * Returns the error code given by the Hub. 28 | */ 29 | public getErrorCode(): HubErrorCode { 30 | return this.body.error_code; 31 | } 32 | 33 | /** 34 | * Returns the error target (e.g. the property which is invalid). 35 | */ 36 | public getTarget() { 37 | return this.body.target; 38 | } 39 | 40 | /** 41 | * Returns the raw error JSON as provided by the Hub. 42 | */ 43 | public getRawError() { 44 | return this.body; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/HubSession.ts: -------------------------------------------------------------------------------- 1 | import { IHubError, IHubResponse, HubErrorCode } from '@decentralized-identity/hub-common-js'; 2 | import { Authentication, CryptoSuite, IKeyStore } from '@decentralized-identity/did-auth-jose'; 3 | import { HttpResolver, IDidResolver } from '@decentralized-identity/did-common-typescript'; 4 | import HubRequest from './requests/HubRequest'; 5 | import HubError from './HubError'; 6 | import HubWriteRequest from './requests/HubWriteRequest'; 7 | import HubWriteResponse from './responses/HubWriteResponse'; 8 | import HubObjectQueryRequest from './requests/HubObjectQueryRequest'; 9 | import HubObjectQueryResponse from './responses/HubObjectQueryResponse'; 10 | import HubCommitQueryRequest from './requests/HubCommitQueryRequest'; 11 | import HubCommitQueryResponse from './responses/HubCommitQueryResponse'; 12 | 13 | // tslint:disable-next-line:import-name 14 | import fetch, { Request, RequestInit } from 'node-fetch'; 15 | 16 | /** 17 | * Options for instantiating a new Hub session. 18 | */ 19 | export interface HubSessionOptions { 20 | 21 | /** The DID of the client, i.e the identity of the user/app using this SDK. */ 22 | clientDid: string; 23 | 24 | /** 25 | * The private key to use for decrypting/signing when communicating with the Hub. Must be 26 | * registered in the DID document of the clientDid. 27 | */ 28 | clientPrivateKeyReference: string; 29 | 30 | /** The DID owning the Hub with which we will be communicating. */ 31 | targetDid: string; 32 | 33 | /** The DID of the Hub, for addressing request envelopes. */ 34 | hubDid: string; 35 | 36 | /** The HTTPS endpoint of the Hub. */ 37 | hubEndpoint: string; 38 | 39 | /** A DID resolver instance to be used during authentication. */ 40 | resolver: IDidResolver; 41 | 42 | /** 43 | * An array of CryptoSuites to use during authentication (optional). Allows the client to provide 44 | * a suite which delegates operations to a secure enclave, rather than using a private key 45 | * available in-memory. 46 | */ 47 | cryptoSuites?: CryptoSuite[]; 48 | 49 | /** 50 | * Instance of KeyStore than can be used 51 | * to get and save keys. 52 | */ 53 | keyStore: IKeyStore; 54 | } 55 | 56 | /** 57 | * Represents a communication session with a particular Hub instance. 58 | */ 59 | export default class HubSession { 60 | 61 | private clientDid: string; 62 | private hubDid: string; 63 | private hubEndpoint: string; 64 | private targetDid: string; 65 | private currentAccessToken: string | undefined; 66 | private authentication: Authentication; 67 | 68 | constructor(options: HubSessionOptions) { 69 | this.clientDid = options.clientDid; 70 | this.hubDid = options.hubDid; 71 | this.hubEndpoint = options.hubEndpoint; 72 | this.targetDid = options.targetDid; 73 | 74 | // Setup authentication context 75 | this.authentication = new Authentication({ 76 | resolver: options.resolver, 77 | keyStore: options.keyStore, 78 | keyReferences: [options.clientPrivateKeyReference], 79 | }); 80 | 81 | } 82 | 83 | /** 84 | * Sends the given request to the Hub instance, and returns the associated response. 85 | * 86 | * @param request An instance or subclass of HubRequest to be sent. 87 | */ 88 | send(request: HubWriteRequest): Promise; 89 | send(request: HubObjectQueryRequest): Promise; 90 | send(request: HubCommitQueryRequest): Promise; 91 | async send(request: HubRequest): Promise { 92 | 93 | const rawRequestJson = await request.getRequestJson(); 94 | 95 | rawRequestJson.iss = this.clientDid; 96 | rawRequestJson.aud = this.hubDid; 97 | rawRequestJson.sub = this.targetDid; 98 | 99 | const rawRequestString = JSON.stringify(rawRequestJson); 100 | const accessToken = await this.getAccessToken(); 101 | 102 | let responseString: string; 103 | 104 | try { 105 | responseString = await this.makeRequest(rawRequestString, accessToken); 106 | } catch (e) { 107 | // If the access token has expired, renew access token and retry 108 | if (HubError.is(e) && e.getErrorCode() === HubErrorCode.AuthenticationFailed) { 109 | const newAccessToken = await this.refreshAccessToken(); 110 | responseString = await this.makeRequest(rawRequestString, newAccessToken); 111 | } else { 112 | throw e; 113 | } 114 | } 115 | 116 | let responseObject: IHubResponse; 117 | 118 | try { 119 | responseObject = JSON.parse(responseString); 120 | } catch (e) { 121 | throw new HubError({ 122 | error_code: HubErrorCode.ServerError, 123 | developer_message: `Unexpected error decoding JSON response: ${e.message}`, 124 | inner_error: e, 125 | }); 126 | } 127 | 128 | return HubSession.mapResponseToObject(responseObject); 129 | } 130 | 131 | /** 132 | * Sends a raw (string) request body to the Hub and receives a response. 133 | * 134 | * @param message The raw request body to send. 135 | * @param accessToken The access token to include in the request, if any. 136 | */ 137 | private async makeRequest(message: string, accessToken?: string): Promise { 138 | 139 | const requestBuffer = await this.authentication.getAuthenticatedRequest(message, this.hubDid, accessToken); 140 | 141 | const res = await this.callFetch(this.hubEndpoint, { 142 | method: 'POST', 143 | body: requestBuffer, 144 | headers: { 145 | 'Content-Type': 'application/jwt', 146 | 'Content-Length': requestBuffer.length.toString(), 147 | }, 148 | }); 149 | 150 | if (res.status !== 200) { 151 | const errorResponse = await res.json(); 152 | throw new HubError(errorResponse); 153 | } 154 | 155 | const response = await res.buffer(); 156 | const plainResponse = await this.authentication.getVerifiedRequest(response, false); 157 | if (plainResponse instanceof Buffer) { 158 | // This should never happen as it means we are trying to return an access token in response 159 | throw new Error('Internal error during decryption.'); 160 | } 161 | 162 | return plainResponse.request; 163 | } 164 | 165 | /** 166 | * Fetch API wrapper, to allow unit testing. 167 | * 168 | * @param url The URL to make a request to. 169 | * @param init Request initialization details. 170 | */ 171 | private async callFetch(url: string | Request, init?: RequestInit) { 172 | return fetch(url, init); 173 | } 174 | 175 | /** 176 | * Returns the current access token for the Hub, requesting one if necessary. 177 | */ 178 | private async getAccessToken(): Promise { 179 | if (this.currentAccessToken) { 180 | return this.currentAccessToken; 181 | } 182 | 183 | return this.refreshAccessToken(); 184 | } 185 | 186 | /** 187 | * Requests an updated access token from the Hub. 188 | */ 189 | private async refreshAccessToken(): Promise { 190 | this.currentAccessToken = await this.makeRequest(''); 191 | return this.currentAccessToken!; 192 | } 193 | 194 | /** Mapping of known response types. */ 195 | private static responseTypes = { 196 | WriteResponse: HubWriteResponse, 197 | ObjectQueryResponse: HubObjectQueryResponse, 198 | CommitQueryResponse: HubCommitQueryResponse, 199 | }; 200 | 201 | /** 202 | * Transforms a JSON blob returned by the Hub into a subclass of HubResponse, based on the `@type` 203 | * field of the response. 204 | * 205 | * @param response The Hub response to be transformed. 206 | */ 207 | private static mapResponseToObject(response: IHubResponse) { 208 | const responseTypeString = response['@type']; 209 | const responseType = (HubSession.responseTypes as any)[responseTypeString]; 210 | 211 | if (responseType) { 212 | return new responseType(response); 213 | } 214 | 215 | if (response['@type'] === 'ErrorResponse') { 216 | throw new HubError(response as any as IHubError); 217 | } 218 | 219 | throw new HubError({ 220 | error_code: HubErrorCode.NotImplemented, 221 | developer_message: `Unexpected response type ${responseTypeString}`, 222 | }); 223 | } 224 | 225 | } 226 | -------------------------------------------------------------------------------- /src/SignedCommit.ts: -------------------------------------------------------------------------------- 1 | import { ICommitProtectedHeaders, IFlattenedJws } from '@decentralized-identity/hub-common-js'; 2 | import base64url from 'base64url'; 3 | import * as crypto from 'crypto'; 4 | 5 | /** 6 | * Class representing a signed commit. 7 | */ 8 | export default class SignedCommit { 9 | 10 | constructor(private json: IFlattenedJws) { 11 | 12 | } 13 | 14 | /** 15 | * Returns the signed commit data in the Flattened JWS JSON Serialization. 16 | */ 17 | toFlattenedJson(): IFlattenedJws { 18 | return this.json; 19 | } 20 | 21 | /** 22 | * Returns the decoded protected headers for this commit. TODO TODO NO REV 23 | */ 24 | getProtectedHeaders(): ICommitProtectedHeaders { 25 | if (this.json && this.json.protected) { 26 | return JSON.parse(base64url.decode(this.json.protected)); 27 | } 28 | 29 | throw new Error('Commit does not have a protected field.'); 30 | } 31 | 32 | /** 33 | * Returns the decoded payload for this commit. 34 | */ 35 | getPayload(): any { 36 | if (this.json && this.json.payload) { 37 | const decoded = base64url.decode(this.json.payload); 38 | try { 39 | return JSON.parse(decoded); 40 | } catch (e) { 41 | // Not JSON, return directly 42 | return decoded; 43 | } 44 | } 45 | 46 | throw new Error('Commit does not have a payload field.'); 47 | } 48 | 49 | /** 50 | * Retrieves the revision ID for this commit. 51 | */ 52 | getRevision(): string { 53 | // TODO: Verify signature; cache result 54 | const sha256 = crypto.createHash('sha256'); 55 | sha256.update(`${this.json.protected}.${this.json.payload}`); 56 | return sha256.digest('hex'); 57 | } 58 | 59 | /** 60 | * Retrieves the ID of the object to which this commit belongs. 61 | */ 62 | getObjectId(): string { 63 | const headers = this.getProtectedHeaders(); 64 | 65 | if (headers.operation === 'create') { 66 | return this.getRevision(); 67 | } 68 | 69 | return headers.object_id!; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/crypto/CommitSigner.ts: -------------------------------------------------------------------------------- 1 | import { PrivateKey, JwsToken, CryptoFactory, RsaCryptoSuite, CryptoSuite } from '@decentralized-identity/did-auth-jose'; 2 | import ICommitSigner from './ICommitSigner'; 3 | import Commit from '../Commit'; 4 | import SignedCommit from '../SignedCommit'; 5 | import objectAssign = require('object-assign'); 6 | 7 | interface CommitSignerOptions { 8 | 9 | /** The DID of the identity that will the commit. */ 10 | did: string; 11 | 12 | /** The private key to be used to sign the commit. */ 13 | key: PrivateKey; 14 | 15 | /** The CryptoSuite to be used to for the algorithm to use to sign the commit */ 16 | cryptoSuite?: CryptoSuite; 17 | 18 | } 19 | 20 | /** 21 | * Class which can apply a signature to a commit. 22 | */ 23 | export default class CommitSigner implements ICommitSigner { 24 | 25 | private did: string; 26 | private key: PrivateKey; 27 | private cryptoSuite: CryptoSuite; 28 | 29 | constructor(options: CommitSignerOptions) { 30 | this.did = options.did; 31 | this.key = options.key; 32 | if (!options.cryptoSuite) { 33 | this.cryptoSuite = new RsaCryptoSuite(); 34 | } else { 35 | this.cryptoSuite = options.cryptoSuite; 36 | } 37 | } 38 | 39 | /** 40 | * Signs the given commit. 41 | * 42 | * @param commit The commit to sign. 43 | */ 44 | public async sign(commit: Commit): Promise { 45 | 46 | commit.validate(); 47 | 48 | const protectedHeaders = commit.getProtectedHeaders(); 49 | const finalProtectedHeaders = objectAssign({}, protectedHeaders, { 50 | iss: this.did, 51 | }); 52 | 53 | const jws = new JwsToken(commit.getPayload(), new CryptoFactory([this.cryptoSuite])); 54 | const signed = await jws.sign(this.key, finalProtectedHeaders as any); // TODO: Need to broaden TypeScript definition of JwsToken.sign(). 55 | 56 | const [outputHeaders, outputPayload, outputSignature] = signed.split('.'); 57 | 58 | return new SignedCommit({ 59 | protected: outputHeaders, 60 | payload: outputPayload, 61 | header: commit.getUnprotectedHeaders(), 62 | signature: outputSignature, 63 | }); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/crypto/ICommitSigner.ts: -------------------------------------------------------------------------------- 1 | import Commit from '../Commit'; 2 | import SignedCommit from '../SignedCommit'; 3 | 4 | /** 5 | * Interface representing an object which can sign a commit. 6 | */ 7 | export default interface ICommitSigner { 8 | 9 | /** 10 | * Signs the given commit. 11 | * 12 | * @param commit The commit to sign. 13 | */ 14 | sign(commit: Commit): Promise; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/example.ts: -------------------------------------------------------------------------------- 1 | import RsaPrivateKey from '@decentralized-identity/did-auth-jose/dist/lib/crypto/rsa/RsaPrivateKey'; 2 | import { HttpResolver } from '@decentralized-identity/did-common-typescript'; 3 | import HubSession from './HubSession'; 4 | import HubWriteRequest from './requests/HubWriteRequest'; 5 | import CommitSigner from './crypto/CommitSigner'; 6 | import Commit from './Commit'; 7 | import HubObjectQueryRequest from './requests/HubObjectQueryRequest'; 8 | import HubCommitQueryRequest from './requests/HubCommitQueryRequest'; 9 | import CommitStrategyBasic from './CommitStrategyBasic'; 10 | import { KeyStoreMem, IKeyStore } from './index'; 11 | 12 | // Fill these in with specific values. 13 | const HTTP_RESOLVER = 'HTTP_RESOLVER_ENDPOINT_HERE'; 14 | const HUB_ENDPOINT = 'HUB_ENDPOINT_HERE'; 15 | const HUB_DID = 'HUB_DID_HERE'; 16 | 17 | // Fill in the DID to use 18 | const DID = 'did:example:YOUR_DID_HERE'; 19 | 20 | /** 21 | * Fill in your full private key, including the `kid` field. The key must: 22 | * - Be an RSA private key in JWK format 23 | * - Match one of the public keys registered for your DID 24 | * - Include a "kid" field with the plain (not fully-qualified) key ID, e.g. "key-1" 25 | */ 26 | const PRIVATE_KEY = { kid:'key-1' }; 27 | 28 | async function runExample() { 29 | 30 | try { 31 | 32 | const kid = `${DID}#${PRIVATE_KEY.kid}`; 33 | const privateKey = RsaPrivateKey.wrapJwk(kid, PRIVATE_KEY); 34 | 35 | const keyStore: IKeyStore = new KeyStoreMem(); 36 | keyStore.save(kid, privateKey); 37 | 38 | const session = new HubSession({ 39 | keyStore, 40 | hubEndpoint: HUB_ENDPOINT, 41 | hubDid: HUB_DID, 42 | resolver: new HttpResolver(HTTP_RESOLVER), 43 | clientDid: DID, 44 | clientPrivateKeyReference: kid, 45 | targetDid: DID, 46 | }); 47 | 48 | // 49 | // Write a new Commit to the Hub, creating a new object. 50 | // 51 | 52 | const commit = new Commit({ 53 | protected: { 54 | committed_at: (new Date()).toISOString(), 55 | iss: DID, 56 | sub: DID, 57 | interface: 'Collections', 58 | context: 'http://schema.org', 59 | type: 'MusicPlaylist', 60 | operation: 'create', 61 | commit_strategy: 'basic', 62 | }, 63 | payload: { 64 | title: 'My Playlist', 65 | }, 66 | }); 67 | 68 | const signer = new CommitSigner({ 69 | did: DID, 70 | key: privateKey, 71 | }); 72 | 73 | const signedCommit = await signer.sign(commit); 74 | 75 | const commitRequest = new HubWriteRequest(signedCommit); 76 | const commitResponse = await session.send(commitRequest); 77 | console.log(commitResponse); 78 | 79 | // 80 | // Read available objects from the Hub. 81 | // 82 | 83 | const queryRequest = new HubObjectQueryRequest({ 84 | interface: 'Collections', 85 | context: 'http://schema.org', 86 | type: 'MusicPlaylist', 87 | }); 88 | 89 | const queryResponse = await session.send(queryRequest); 90 | console.log(queryResponse); 91 | 92 | const objects = queryResponse.getObjects(); 93 | 94 | // 95 | // Read the contents of a single object. 96 | // 97 | 98 | if (objects.length > 0) { 99 | const objectMetadata = objects[0]; 100 | 101 | if (objectMetadata.commit_strategy !== 'basic') { 102 | throw new Error('Currently only the basic commit strategy is supported.'); 103 | } 104 | 105 | const commitQueryRequest = new HubCommitQueryRequest({ 106 | object_id: [objectMetadata.id], 107 | }); 108 | 109 | const commitQueryResponse = await session.send(commitQueryRequest); 110 | const commits = commitQueryResponse.getCommits(); 111 | 112 | const strategy = new CommitStrategyBasic(); 113 | const objectState = await strategy.resolveObject(commits); 114 | 115 | console.log(objectState); 116 | } 117 | 118 | } catch (e) { 119 | console.error(e); 120 | } 121 | 122 | } 123 | 124 | runExample(); 125 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // Cryptography 2 | export { default as CommitSigner } from './crypto/CommitSigner'; 3 | export { default as ICommitSigner } from './crypto/ICommitSigner'; 4 | import { IKeyStore, KeyStoreMem, ProtectionFormat } from '@decentralized-identity/did-auth-jose'; 5 | export { IKeyStore, KeyStoreMem, ProtectionFormat } ; 6 | 7 | // Identifier 8 | 9 | // Requests 10 | export { default as HubRequest } from './requests/HubRequest'; 11 | export { default as HubObjectQueryRequest } from './requests/HubObjectQueryRequest'; 12 | export { default as HubCommitQueryRequest } from './requests/HubCommitQueryRequest'; 13 | export { default as HubWriteRequest } from './requests/HubWriteRequest'; 14 | 15 | // Responses 16 | export { default as HubObjectQueryResponse } from './responses/HubObjectQueryResponse'; 17 | export { default as HubCommitQueryResponse } from './responses/HubCommitQueryResponse'; 18 | export { default as HubWriteResponse } from './responses/HubWriteResponse'; 19 | 20 | // Root 21 | export { default as Commit } from './Commit'; 22 | export { default as CommitStrategyBasic } from './CommitStrategyBasic'; 23 | export { default as HubError } from './HubError'; 24 | export { default as HubSession } from './HubSession'; 25 | export { default as SignedCommit } from './SignedCommit'; 26 | -------------------------------------------------------------------------------- /src/requests/HubCommitQueryRequest.ts: -------------------------------------------------------------------------------- 1 | import { IHubCommitQueryOptions } from '@decentralized-identity/hub-common-js'; 2 | import HubRequest from './HubRequest'; 3 | 4 | /** 5 | * Represents a request to a Hub for a set of commits. 6 | */ 7 | export default class HubCommitQueryRequest extends HubRequest { 8 | 9 | // Needed for correctly determining type of HubSession#send(), to ensure 10 | // the different request classes aren't structurally compatible. 11 | private readonly _isCommitQueryRequest = true; 12 | 13 | constructor(queryOptions: IHubCommitQueryOptions) { 14 | super('CommitQueryRequest', { 15 | query: queryOptions, 16 | }); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/requests/HubObjectQueryRequest.ts: -------------------------------------------------------------------------------- 1 | import { IHubObjectQueryOptions } from '@decentralized-identity/hub-common-js'; 2 | import HubRequest from './HubRequest'; 3 | 4 | /** 5 | * Represents a request to a Hub to query the available objects. 6 | */ 7 | export default class HubObjectQueryRequest extends HubRequest { 8 | 9 | // Needed for correctly determining type of HubSession#send(), to ensure 10 | // the different request classes aren't structurally compatible. 11 | private readonly _isObjectQueryRequest = true; 12 | 13 | constructor(queryOptions: IHubObjectQueryOptions) { 14 | super('ObjectQueryRequest', { 15 | query: queryOptions, 16 | }); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/requests/HubRequest.ts: -------------------------------------------------------------------------------- 1 | import * as objectAssign from 'object-assign'; 2 | 3 | /** 4 | * The base class for all requests to an Identity Hub. 5 | */ 6 | export default class HubRequest { 7 | 8 | private requestType: string; 9 | private requestBody: any; 10 | 11 | constructor(requestType: string, requestBody?: any) { 12 | this.requestType = requestType; 13 | this.requestBody = requestBody; 14 | } 15 | 16 | /** 17 | * Returns the raw request JSON which will be sent to the Hub. 18 | */ 19 | public async getRequestJson() { 20 | return objectAssign( 21 | { 22 | '@context': 'https://schema.identity.foundation/0.1', 23 | '@type': this.requestType, 24 | }, 25 | this.requestBody, 26 | ); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/requests/HubWriteRequest.ts: -------------------------------------------------------------------------------- 1 | import HubRequest from './HubRequest'; 2 | import SignedCommit from '../SignedCommit'; 3 | 4 | /** 5 | * Represents a request to commit the given Commit object to an Identity Hub. 6 | */ 7 | export default class HubCommitWriteRequest extends HubRequest { 8 | 9 | // Needed for correctly determining type of HubSession#send(), to ensure 10 | // the different request classes aren't structurally compatible. 11 | private readonly _isWriteRequest = true; 12 | 13 | constructor(commit: SignedCommit) { 14 | super('WriteRequest', { 15 | commit: commit.toFlattenedJson(), 16 | }); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/responses/HubCommitQueryResponse.ts: -------------------------------------------------------------------------------- 1 | import { IHubCommitQueryResponse } from '@decentralized-identity/hub-common-js'; 2 | import SignedCommit from '../SignedCommit'; 3 | 4 | /** 5 | * Represents the response to a `HubCommitQueryRequest`. 6 | */ 7 | export default class HubCommitQueryResponse { 8 | 9 | constructor(private response: IHubCommitQueryResponse) { 10 | if (response['@type'] !== 'CommitQueryResponse') { 11 | throw new Error('Unexpected response type; expected CommitQueryResponse'); 12 | } 13 | } 14 | 15 | /** 16 | * Returns the set of commits returned by the Hub. 17 | */ 18 | public getCommits() { 19 | return this.response.commits.map((commit) => { 20 | return new SignedCommit(commit); 21 | }); 22 | } 23 | 24 | /** 25 | * Indicates whether additional pages of results are available. 26 | */ 27 | public hasSkipToken() { 28 | return !!this.response.skip_token; 29 | } 30 | 31 | /** 32 | * Retrieves a token which can be used to fetch subsequent result pages. 33 | */ 34 | public getSkipToken() { 35 | return this.response.skip_token; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/responses/HubObjectQueryResponse.ts: -------------------------------------------------------------------------------- 1 | import { IHubObjectQueryResponse } from '@decentralized-identity/hub-common-js'; 2 | 3 | /** 4 | * Represents the response to a `HubObjectQueryRequest`. 5 | */ 6 | export default class HubObjectQueryResponse { 7 | 8 | private response: IHubObjectQueryResponse; 9 | 10 | constructor(json: IHubObjectQueryResponse) { 11 | if (json['@type'] !== 'ObjectQueryResponse') { 12 | throw new Error('Unexpected response type; expected ObjectQueryResponse'); 13 | } 14 | 15 | this.response = json; 16 | } 17 | 18 | /** 19 | * Returns the set of objects returned by the Hub. 20 | * 21 | * TODO: Map JSON into useful objects, as done for commits. 22 | */ 23 | public getObjects() { 24 | return this.response.objects || []; 25 | } 26 | 27 | /** 28 | * Indicates whether additional pages of results are available. 29 | */ 30 | public hasSkipToken() { 31 | return !!this.response.skip_token; 32 | } 33 | 34 | /** 35 | * Retrieves a token which can be used to fetch subsequent result pages. 36 | */ 37 | public getSkipToken() { 38 | return this.response.skip_token; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/responses/HubWriteResponse.ts: -------------------------------------------------------------------------------- 1 | import { IHubWriteResponse } from '@decentralized-identity/hub-common-js'; 2 | 3 | /** 4 | * Represents the response to a `HubWriteRequest`. 5 | */ 6 | export default class HubWriteResponse { 7 | 8 | constructor (private response: IHubWriteResponse) { 9 | 10 | } 11 | 12 | /** 13 | * Returns the list of known revisions for the object which was created/modified. 14 | */ 15 | public getRevisions() { 16 | return this.response.revisions; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /tests/Commit.spec.ts: -------------------------------------------------------------------------------- 1 | import { alter, explain } from './TestUtils'; 2 | import Commit, { ICommitFields } from '../src/Commit'; 3 | import ICommitSigner from './crypto/ICommitSigner'; 4 | import SignedCommit from '../src/SignedCommit'; 5 | 6 | const commitFields: ICommitFields = { 7 | protected: { 8 | interface: 'Collections', 9 | context: 'schema.org', 10 | type: 'MusicPlaylist', 11 | operation: 'create', 12 | committed_at: '2019-01-01', 13 | commit_strategy: 'basic', 14 | sub: 'did:example:sub.id' 15 | }, 16 | payload: { 17 | title: 'My Playlist', 18 | }, 19 | header: { 20 | rev: 'abc', 21 | }, 22 | }; 23 | 24 | const commit = new Commit(commitFields); 25 | 26 | describe('Commit', () => { 27 | 28 | describe('validate', () => { 29 | 30 | const invalidStrings = ['', null, undefined, true, false, 7, [], [''], {}]; 31 | 32 | const invalidCases: {[field: string]: any[]} = { 33 | 'protected.interface': invalidStrings, 34 | 'protected.context': invalidStrings, 35 | 'protected.type': invalidStrings, 36 | 'protected.committed_at': invalidStrings, 37 | 'protected.commit_strategy': invalidStrings, 38 | 'protected.sub': invalidStrings, 39 | 'protected.operation': [...invalidStrings, 'other'], 40 | 'protected': [true, false, null, undefined, {}, [], 77], 41 | 'payload': [true, false, null, undefined, 77], 42 | }; 43 | 44 | Object.keys(invalidCases).forEach((field) => { 45 | const cases = invalidCases[field]; 46 | cases.forEach(invalidValue => { 47 | it(`should reject '${field}' set to ${explain(invalidValue)}`, async () => { 48 | const alteredFields = alter(commitFields, { [field]: invalidValue }); 49 | const commit = new Commit(alteredFields); 50 | expect(commit.isValid()).toBeFalsy(); 51 | }); 52 | }); 53 | }); 54 | 55 | it('should ensure object_id is not set for a create commit', async () => { 56 | 57 | const alteredFields = alter(commitFields, { 58 | 'protected.operation': 'create', 59 | 'protected.object_id': 'abc' 60 | }); 61 | 62 | const commit = new Commit(alteredFields); 63 | expect(commit.isValid()).toBeFalsy(); 64 | 65 | }); 66 | 67 | it('should ensure object_id is set for an update/delete commit', async () => { 68 | 69 | ['update', 'delete'].forEach((operation) => { 70 | const alteredFields = alter(commitFields, { 71 | 'protected.operation': operation, 72 | 'protected.object_id': undefined 73 | }); 74 | const commit = new Commit(alteredFields); 75 | expect(commit.isValid()).toBeFalsy(); 76 | }); 77 | 78 | }); 79 | 80 | it('should validate a correct commit', async () => { 81 | const commit = new Commit(commitFields); 82 | expect(commit.isValid()).toBeTruthy(); 83 | }); 84 | 85 | }); 86 | 87 | describe('getProtectedHeaders()', () => { 88 | it('should return the protected headers', async () => { 89 | expect(commit.getProtectedHeaders()).toEqual(commitFields.protected); 90 | }); 91 | }); 92 | 93 | describe('getUnprotectedHeaders()', () => { 94 | it('should return the unprotected headers', async () => { 95 | expect(commit.getUnprotectedHeaders()).toEqual(commitFields.header as any); 96 | }); 97 | }); 98 | 99 | describe('getPayload()', () => { 100 | it('should return the payload', async () => { 101 | expect(commit.getPayload()).toEqual(commitFields.payload); 102 | }); 103 | }); 104 | 105 | describe('sign()', () => { 106 | it('should call sign() on the given signer', async () => { 107 | 108 | const signedCommit = new SignedCommit({ 109 | protected: '', 110 | payload: '', 111 | signature: '', 112 | }); 113 | 114 | const signer: ICommitSigner = { 115 | sign: async (): Promise => { 116 | return signedCommit; 117 | }, 118 | }; 119 | 120 | spyOn(signer, 'sign').and.callThrough(); 121 | const returnValue = await commit.sign(signer as any); 122 | 123 | expect(signer.sign).toHaveBeenCalled(); 124 | expect(returnValue).toEqual(signedCommit); 125 | }); 126 | }); 127 | 128 | }); 129 | -------------------------------------------------------------------------------- /tests/CommitStrategyBasic.spec.ts: -------------------------------------------------------------------------------- 1 | import base64url from 'base64url'; 2 | import { ICommitProtectedHeaders } from '@decentralized-identity/hub-common-js'; 3 | import { alter } from './TestUtils'; 4 | import CommitStrategyBasic from '../src/CommitStrategyBasic'; 5 | import SignedCommit from '../src/SignedCommit'; 6 | 7 | const commitHeaders: Partial = { 8 | interface: 'Collections', 9 | context: 'schema.org', 10 | type: 'MusicPlaylist', 11 | operation: 'update', 12 | commit_strategy: 'basic', 13 | sub: 'did:example:sub.id', 14 | kid: 'did:example:iss.id#key-1', 15 | iss: 'did:example:iss.id', 16 | // object_id and committed_at intentionally omitted 17 | }; 18 | 19 | let strategy: CommitStrategyBasic; 20 | 21 | /** 22 | * Helper to build a mock signed commit using the specified headers and a default payload. 23 | * 24 | * @param headers Headers to be merged with the default headers. 25 | */ 26 | const buildSignedCommit = (headers: Partial, payload?: any) => { 27 | const finalHeaders = alter(commitHeaders, headers); 28 | const finalPayload = payload || { title: 'My Playlist' }; 29 | return new SignedCommit({ 30 | protected: base64url(JSON.stringify(finalHeaders)), 31 | payload: base64url(JSON.stringify(finalPayload)), 32 | signature: 'abcdef', 33 | }); 34 | }; 35 | 36 | /** 37 | * Helper to expect that one commit precedes another. 38 | * 39 | * @param a The commit expected to come first. 40 | * @param b The commit expected to come second. 41 | */ 42 | const expectBefore = async (a: SignedCommit, b: SignedCommit) => { 43 | expect(strategy['compareCommits'](a, b)).toEqual(-1); 44 | expect(strategy['compareCommits'](b, a)).toEqual(1); 45 | }; 46 | 47 | describe('CommitStrategyBasic', () => { 48 | 49 | beforeEach(async () => { 50 | strategy = new CommitStrategyBasic(); 51 | }); 52 | 53 | describe('compareCommits()', () => { 54 | 55 | it('should throw if given commits from different objects', async () => { 56 | try { 57 | await expectBefore( 58 | buildSignedCommit({ object_id: '123' }), 59 | buildSignedCommit({ object_id: '456' }) 60 | ); 61 | fail('Not expected to reach this point.') 62 | } catch (err) { 63 | // Expected 64 | } 65 | }); 66 | 67 | it('should respect create, update, delete ordering', async () => { 68 | const create = buildSignedCommit({ operation: 'create' }); 69 | const update = buildSignedCommit({ operation: 'update', object_id: create.getObjectId() }); 70 | const del = buildSignedCommit({ operation: 'delete', object_id: create.getObjectId() }); 71 | 72 | await expectBefore(create, update); 73 | await expectBefore(update, del); 74 | await expectBefore(create, del); 75 | }); 76 | 77 | it('should respect date ordering', async () => { 78 | const a = buildSignedCommit({ committed_at: '1995-12-17T03:24:00' }); 79 | const b = buildSignedCommit({ committed_at: '1995-12-17T03:25:00' }); 80 | 81 | await expectBefore(a, b); 82 | }); 83 | 84 | it('should respect revision ordering', async () => { 85 | for (let i = 0; i < 100; i++) { 86 | const a = buildSignedCommit({ 87 | committed_at: '1995-12-17T03:24:00', 88 | iss: Math.random().toString() 89 | }); 90 | const b = buildSignedCommit({ 91 | committed_at: '1995-12-17T03:24:00', 92 | iss: Math.random().toString() 93 | }); 94 | 95 | (a.getRevision() < b.getRevision()) 96 | ? await expectBefore(a, b) 97 | : await expectBefore(b, a); 98 | } 99 | }); 100 | 101 | describe('resoloveObject()', () => { 102 | 103 | it('should return null for an empty commit list', async () => { 104 | expect(await strategy.resolveObject(undefined as any)).toBeNull(); 105 | expect(await strategy.resolveObject([])).toBeNull(); 106 | }); 107 | 108 | it('should respect create, update, delete ordering', async () => { 109 | const create = buildSignedCommit({ operation: 'create' }, { op: 'create' }); 110 | const update = buildSignedCommit({ operation: 'update', object_id: create.getObjectId() }, { op: 'update' }); 111 | 112 | expect(await strategy.resolveObject([update, create])).toEqual({ op: 'update' }); 113 | expect(await strategy.resolveObject([create, update])).toEqual({ op: 'update' }); 114 | }); 115 | 116 | it('should return null for a deleted object', async () => { 117 | const createCommit = buildSignedCommit({ operation: 'create' }, { op: 'create' }); 118 | const deleteCommit = buildSignedCommit({ operation: 'delete', object_id: createCommit.getObjectId() }, {}); 119 | 120 | expect(await strategy.resolveObject([createCommit, deleteCommit])).toBeNull(); 121 | expect(await strategy.resolveObject([deleteCommit, createCommit])).toBeNull(); 122 | }); 123 | 124 | }); 125 | 126 | }); 127 | }); 128 | -------------------------------------------------------------------------------- /tests/HubError.spec.ts: -------------------------------------------------------------------------------- 1 | import { HubErrorCode } from '@decentralized-identity/hub-common-js'; 2 | import HubError from '../src/HubError'; 3 | 4 | const hubErrorBody = { 5 | error_code: HubErrorCode.NotFound, 6 | target: 'example', 7 | }; 8 | 9 | const hubError = new HubError(hubErrorBody); 10 | 11 | describe('HubError', () => { 12 | 13 | describe('is', () => { 14 | it('should indicate whether an object is a HubError', async () => { 15 | expect(HubError.is(hubError)).toBeTruthy(); 16 | expect(HubError.is(new Error())).toBeFalsy(); 17 | }); 18 | }); 19 | 20 | describe('constructor', () => { 21 | it('should fix the prototype chain', async () => { 22 | expect(hubError instanceof HubError).toBeTruthy(); 23 | expect(hubError instanceof Error).toBeTruthy(); 24 | }); 25 | }); 26 | 27 | describe('getErrorCode()', () => { 28 | it('should return the error code', async () => { 29 | expect(hubError.getErrorCode()).toEqual(hubErrorBody.error_code); 30 | }); 31 | }); 32 | 33 | describe('getTarget()', () => { 34 | it('should return the taret', async () => { 35 | expect(hubError.getTarget()).toEqual(hubErrorBody.target); 36 | }); 37 | }); 38 | 39 | describe('getRawError()', () => { 40 | it('should return the raw error body', async () => { 41 | expect(hubError.getRawError()).toEqual(hubErrorBody); 42 | }); 43 | }); 44 | 45 | }); 46 | -------------------------------------------------------------------------------- /tests/HubSession.spec.ts: -------------------------------------------------------------------------------- 1 | import { HubErrorCode } from '@decentralized-identity/hub-common-js'; 2 | import HubSession from '../src/HubSession'; 3 | import HubWriteRequest from '../src/requests/HubWriteRequest'; 4 | import HubWriteResponse from '../src/responses/HubWriteResponse'; 5 | import HubCommitQueryResponse from '../src/responses/HubCommitQueryResponse'; 6 | import HubObjectQueryResponse from '../src/responses/HubObjectQueryResponse'; 7 | import HubError from '../src/HubError'; 8 | import SignedCommit from '../src/SignedCommit'; 9 | import RsaPrivateKey from '@decentralized-identity/did-auth-jose/dist/lib/crypto/rsa/RsaPrivateKey'; 10 | import { PrivateKey, KeyStoreMem, IKeyStore } from '@decentralized-identity/did-auth-jose'; 11 | import MockResolver from './MockResolver'; 12 | import { Request, Response } from 'node-fetch'; 13 | import MockHub from './MockHub'; 14 | 15 | let clientPrivateKey: PrivateKey; 16 | const clientDid = 'did:fake:client.id'; 17 | const clientKid = `${clientDid}#key-1`; 18 | 19 | let hubPrivateKey: RsaPrivateKey; 20 | const hubDid = 'did:fake:hub.id'; 21 | const hubKid = `${hubDid}#key-1`; 22 | 23 | let mockResolver: MockResolver; 24 | 25 | const signedCommit = new SignedCommit({ 26 | protected: "test", 27 | payload: "test", 28 | signature: "test" 29 | }); 30 | 31 | describe('HubSession', () => { 32 | 33 | beforeEach(async () => { 34 | mockResolver = new MockResolver(); 35 | 36 | clientPrivateKey = await RsaPrivateKey.generatePrivateKey(clientKid); 37 | hubPrivateKey = await RsaPrivateKey.generatePrivateKey(hubKid); 38 | 39 | mockResolver.setKey(hubDid, hubPrivateKey.getPublicKey()); 40 | mockResolver.setKey(clientDid, clientPrivateKey.getPublicKey()); 41 | }); 42 | 43 | describe('send()', () => { 44 | 45 | let session: HubSession; 46 | let mockHub: MockHub; 47 | 48 | beforeEach(async () => { 49 | 50 | mockHub = new MockHub({ 51 | hubDid, 52 | hubPrivateKey, 53 | resolver: mockResolver 54 | }); 55 | 56 | const kid = 'testkey'; 57 | const keyStore: IKeyStore = new KeyStoreMem(); 58 | keyStore.save(kid, clientPrivateKey); 59 | 60 | session = new HubSession({ 61 | clientDid: 'did:fake:client.id', 62 | clientPrivateKeyReference: kid, 63 | targetDid: 'did:fake:target.id', 64 | hubDid, 65 | hubEndpoint: 'https://example.com', 66 | resolver: mockResolver, 67 | keyStore: keyStore 68 | }); 69 | 70 | // Redirect fetch() calls to mockHub 71 | spyOn(session, 'callFetch').and.callFake((url: string | Request, init?: RequestInit) => { 72 | return mockHub.handleFetch(url, init); 73 | }); 74 | 75 | }); 76 | 77 | it('should send a valid request', async () => { 78 | const request = new HubWriteRequest(signedCommit); 79 | 80 | const mockWriteResponse = JSON.stringify({ 81 | '@context': 'https://schema.identity.foundation/0.1', 82 | '@type': 'WriteResponse', 83 | 'revisions': ['abc', '123'], 84 | }); 85 | 86 | // Set Hub behavior 87 | mockHub.setHandler(async (callDetails) => { 88 | 89 | // Expect an auth token request 90 | if (callDetails.isAuthTokenRequest) { 91 | return callDetails.authTokenResponse; 92 | } 93 | 94 | // Then return real response 95 | return mockWriteResponse; 96 | 97 | }); 98 | 99 | const response = await session.send(request); 100 | expect(response.getRevisions()).toEqual(['abc', '123']); 101 | }); 102 | 103 | it('should refresh an invalid access token', async () => { 104 | const request = new HubWriteRequest(signedCommit); 105 | 106 | session['currentAccessToken'] = 'invalid-access-token'; 107 | 108 | const mockWriteResponse = JSON.stringify({ 109 | '@context': 'https://schema.identity.foundation/0.1', 110 | '@type': 'WriteResponse', 111 | 'revisions': ['abc', '123'], 112 | }); 113 | 114 | let firstRequest = true; 115 | 116 | mockHub.setPreAuthHandler(async (_) => { 117 | if (firstRequest) { 118 | firstRequest = false; 119 | return new Response(JSON.stringify({ 120 | '@type': 'ErrorResponse', 121 | error_code: HubErrorCode.AuthenticationFailed 122 | }), { status: 500 }); 123 | } 124 | return; 125 | }); 126 | 127 | // Set Hub behavior 128 | mockHub.setHandler(async (callDetails) => { 129 | 130 | // First (invalid) token request handled with preAuthHandler 131 | 132 | // Expect an auth token request 133 | if (callDetails.isAuthTokenRequest) { 134 | return callDetails.authTokenResponse; 135 | } 136 | 137 | // Then return real response 138 | return mockWriteResponse; 139 | 140 | }); 141 | 142 | const response = await session.send(request); 143 | expect(response.getRevisions()).toEqual(['abc', '123']); 144 | }); 145 | 146 | it('should pass through a hub error', async () => { 147 | const request = new HubWriteRequest(signedCommit); 148 | 149 | const errorResponse = { 150 | '@type': 'ErrorResponse', 151 | error_code: HubErrorCode.TooManyRequests 152 | }; 153 | 154 | // Set Hub behavior 155 | mockHub.setHandler(async (callDetails) => { 156 | 157 | // Expect an auth token request 158 | if (callDetails.isAuthTokenRequest) { 159 | return callDetails.authTokenResponse; 160 | } 161 | 162 | // Then return real response 163 | return new Response(JSON.stringify(errorResponse), { status: 500 }); 164 | 165 | }); 166 | 167 | try { 168 | console.log('test'); 169 | const response = await session.send(request); 170 | fail('Not expected to reach this point.'); 171 | } catch (e) { 172 | expect(HubError.is(e)).toBeTruthy(); 173 | expect((e as HubError).getErrorCode()).toEqual(HubErrorCode.TooManyRequests); 174 | } 175 | }); 176 | 177 | it('should handle invalid json', async () => { 178 | const request = new HubWriteRequest(signedCommit); 179 | 180 | // Set Hub behavior 181 | mockHub.setHandler(async (callDetails) => { 182 | // Expect an auth token request 183 | if (callDetails.isAuthTokenRequest) { 184 | return callDetails.authTokenResponse; 185 | } 186 | 187 | // Then return real response 188 | return "not-json"; 189 | }); 190 | 191 | try { 192 | const response = await session.send(request); 193 | fail('Not expected to reach this point.'); 194 | } catch (e) { 195 | expect(HubError.is(e)).toBeTruthy(); 196 | expect((e as HubError).getErrorCode()).toEqual(HubErrorCode.ServerError); 197 | } 198 | }); 199 | 200 | }); 201 | 202 | describe('mapResponseToObject()', () => { 203 | it('should correctly map responses', () => { 204 | const method = HubSession['mapResponseToObject']; 205 | 206 | const mapping: {[key: string]: any} = { 207 | CommitQueryResponse: HubCommitQueryResponse, 208 | ObjectQueryResponse: HubObjectQueryResponse, 209 | WriteResponse: HubWriteResponse, 210 | }; 211 | 212 | for (const key in mapping) { 213 | const response = method({ 214 | '@context': 'https://schema.identity.foundation/0.1', 215 | '@type': key, 216 | }); 217 | expect(response instanceof mapping[key]).toBeTruthy(); 218 | } 219 | }); 220 | 221 | it('should map and throw an error response', () => { 222 | try { 223 | HubSession['mapResponseToObject']({ 224 | '@context': 'https://schema.identity.foundation/0.1', 225 | '@type': 'ErrorResponse', 226 | error_code: HubErrorCode.AuthenticationFailed, 227 | } as any); 228 | fail('Not expected to reach this point.'); 229 | } catch (e) { 230 | expect(HubError.is(e)).toBeTruthy(); 231 | expect((e as HubError).getErrorCode()).toEqual(HubErrorCode.AuthenticationFailed); 232 | } 233 | }); 234 | 235 | it('should throw an error for an unknown response type', () => { 236 | try { 237 | HubSession['mapResponseToObject']({ 238 | '@context': 'https://schema.identity.foundation/0.1', 239 | '@type': 'UnsupportedResponse', 240 | }); 241 | fail('Not expected to reach this point.'); 242 | } catch (e) { 243 | expect(HubError.is(e)).toBeTruthy(); 244 | expect((e as HubError).getErrorCode()).toEqual(HubErrorCode.NotImplemented); 245 | } 246 | }); 247 | }); 248 | 249 | }); 250 | -------------------------------------------------------------------------------- /tests/MockHub.ts: -------------------------------------------------------------------------------- 1 | import { PrivateKey, Authentication, VerifiedRequest } from "@decentralized-identity/did-auth-jose"; 2 | import { IDidResolver } from "@decentralized-identity/did-common-typescript"; 3 | import { Response, Request } from 'node-fetch'; 4 | 5 | /** Handler to intercept requests before they are authenticated. */ 6 | type MockHubPreAuthHandler = (body: Buffer) => Promise; 7 | 8 | interface MockHubHandlerAuthRequestParameters { 9 | isAuthTokenRequest: true; 10 | authTokenResponse: Response; 11 | } 12 | 13 | interface MockHubHandlerClientRequestParameters { 14 | isAuthTokenRequest: false; 15 | clientRequest: VerifiedRequest; 16 | } 17 | 18 | type MockHubHandlerParameters = MockHubHandlerAuthRequestParameters | MockHubHandlerClientRequestParameters; 19 | 20 | /** Handler to intercept requests after they are authenticated. */ 21 | type MockHubHandler = (params: MockHubHandlerParameters) => Promise; 22 | 23 | interface MockHubOptions { 24 | hubDid: string; 25 | hubPrivateKey: PrivateKey; 26 | resolver: IDidResolver; 27 | } 28 | 29 | /** 30 | * Mock Hub implementation for testing requests/responses. 31 | * 32 | * This class handles the authentication/encryption wrapping and unwrapping, and calls a provided 33 | * handler function to decide on the actual response. 34 | */ 35 | export default class MockHub { 36 | 37 | private authentication: Authentication; 38 | 39 | private preAuthHandler: MockHubPreAuthHandler | undefined; 40 | private handler: MockHubHandler | undefined; 41 | 42 | constructor(options: MockHubOptions) { 43 | 44 | this.authentication = new Authentication({ 45 | resolver: options.resolver, 46 | keys: { 47 | [options.hubPrivateKey.kid]: options.hubPrivateKey 48 | } 49 | }); 50 | 51 | } 52 | 53 | /** 54 | * Configures a test handler callback which will be called before the incoming request is 55 | * validated. Return a Buffer to short-circuit the response; or undefined to continue processing 56 | * the request normally. 57 | */ 58 | async setPreAuthHandler(handler: MockHubPreAuthHandler) { 59 | this.preAuthHandler = handler; 60 | } 61 | 62 | /** 63 | * Configures a test handler callback which will be called after the incoming request is 64 | * validated. This callback plays the role of the Hub and decides how to respond. 65 | */ 66 | async setHandler(handler: MockHubHandler) { 67 | this.handler = handler; 68 | } 69 | 70 | /** 71 | * Handles an intercepted call to fetch() by processing the request and calling the configured 72 | * mock callback to handle the response. 73 | */ 74 | async handleFetch(_: string | Request, init?: RequestInit): Promise { 75 | 76 | if (!init) throw new Error('MockHub: The RequestInit fetch parameter was not present.'); 77 | if (!Buffer.isBuffer(init.body)) throw new Error('MockHub: The request body was not a Buffer.'); 78 | 79 | if (this.preAuthHandler) { 80 | const preAuthResponse = await this.preAuthHandler(init.body); 81 | if (preAuthResponse) return preAuthResponse; 82 | } 83 | 84 | let verifiedRequest = await this.authentication.getVerifiedRequest(init.body); 85 | 86 | // let isAuthTokenRequest = Buffer.isBuffer(verifiedRequest); 87 | 88 | let handlerParameters: MockHubHandlerParameters; 89 | 90 | if (Buffer.isBuffer(verifiedRequest)) { 91 | // Auth token request 92 | handlerParameters = { 93 | isAuthTokenRequest: true, 94 | authTokenResponse: new Response(verifiedRequest) 95 | } as MockHubHandlerAuthRequestParameters; 96 | } else { 97 | // Client request 98 | handlerParameters = { 99 | isAuthTokenRequest: false, 100 | clientRequest: verifiedRequest 101 | } as MockHubHandlerClientRequestParameters; 102 | } 103 | 104 | if (!this.handler) { 105 | throw new Error('MockHub: Handler not set.'); 106 | } 107 | 108 | let handlerResponse = await this.handler(handlerParameters); 109 | 110 | if (typeof handlerResponse === 'string') { 111 | // Returned a real response 112 | let responseBuffer = await this.authentication.getAuthenticatedResponse(verifiedRequest as VerifiedRequest, handlerResponse); 113 | return new Response(responseBuffer); 114 | } 115 | 116 | // handlerResponse instanceof Response 117 | return handlerResponse; 118 | } 119 | 120 | } -------------------------------------------------------------------------------- /tests/MockResolver.ts: -------------------------------------------------------------------------------- 1 | import { IDidResolver, DidDocument } from '@decentralized-identity/did-common-typescript'; 2 | import { PublicKey } from '@decentralized-identity/did-auth-jose'; 3 | 4 | /** 5 | * Mock implementation of a DidResolver which will return the configured DID documents. 6 | */ 7 | export default class MockResolver implements IDidResolver { 8 | 9 | private keys: {[did: string]: PublicKey} = {}; 10 | 11 | constructor(keys?: {[did: string]: PublicKey}) { 12 | if (keys) { 13 | Object.keys(keys).forEach(did => this.keys[did] = keys[did]); 14 | } 15 | } 16 | 17 | /** 18 | * Sets the key for a specific DID. 19 | */ 20 | setKey(did: string, key: PublicKey) { 21 | this.keys[did] = key; 22 | } 23 | 24 | /** 25 | * Resolves the given DID. 26 | */ 27 | async resolve(did: string) { 28 | 29 | const key = this.keys[did]; 30 | 31 | if (!key) { 32 | throw new Error(`MockResolver has no entry for requested DID: ${did}`); 33 | } 34 | 35 | return { 36 | didDocument: new DidDocument({ 37 | "@context": "https://w3id.org/did/v1", 38 | id: did, 39 | publicKey: [{ 40 | id: key.kid, 41 | type: 'RsaVerificationKey2018', 42 | controller: did, 43 | publicKeyJwk: key 44 | }] 45 | }) 46 | }; 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /tests/SignedCommit.spec.ts: -------------------------------------------------------------------------------- 1 | import { ICommitProtectedHeaders, IFlattenedJws } from '@decentralized-identity/hub-common-js'; 2 | import base64url from 'base64url'; 3 | import SignedCommit from '../src/SignedCommit'; 4 | import objectAssign = require('object-assign'); 5 | 6 | const createHeaders: ICommitProtectedHeaders = { 7 | interface: 'Collections', 8 | context: 'schema.org', 9 | type: 'MusicPlaylist', 10 | operation: 'create', 11 | committed_at: '2019-01-01', 12 | commit_strategy: 'basic', 13 | sub: 'did:example:sub.id', 14 | kid: 'did:example:client.id#key-1', 15 | iss: 'did:example:client.id', 16 | }; 17 | 18 | describe('SignedCommit', () => { 19 | 20 | describe('getProtectedHeaders()', () => { 21 | 22 | it('should return the headers', async () => { 23 | const signedCommit = new SignedCommit({ 24 | protected: base64url(JSON.stringify(createHeaders)), 25 | payload: base64url(JSON.stringify({ name: 'test' })), 26 | signature: 'abc', 27 | }); 28 | 29 | expect(signedCommit.getProtectedHeaders()).toEqual(createHeaders); 30 | }); 31 | 32 | it('should throw if protected headers are missing', async () => { 33 | const signedCommit = new SignedCommit({ 34 | payload: base64url(JSON.stringify({ name: 'test' })), 35 | signature: 'abc', 36 | } as IFlattenedJws); 37 | 38 | try { 39 | signedCommit.getProtectedHeaders(); 40 | fail('Should not reach this point.') 41 | } catch (e) { 42 | // Expected 43 | } 44 | }); 45 | 46 | }); 47 | 48 | describe('getPayload()', () => { 49 | 50 | it('should return a json payload', async () => { 51 | const payload = { 52 | name: 'test' 53 | }; 54 | 55 | const signedCommit = new SignedCommit({ 56 | protected: base64url(JSON.stringify(createHeaders)), 57 | payload: base64url(JSON.stringify(payload)), 58 | signature: 'abc', 59 | }); 60 | 61 | expect(signedCommit.getPayload()).toEqual(payload); 62 | }); 63 | 64 | it('should return a non-json payload', async () => { 65 | const payload = 'test'; 66 | 67 | const signedCommit = new SignedCommit({ 68 | protected: base64url(JSON.stringify(createHeaders)), 69 | payload: base64url(payload), 70 | signature: 'abc', 71 | }); 72 | 73 | expect(signedCommit.getPayload()).toEqual(payload); 74 | }); 75 | 76 | it('should throw if a payload is missing', async () => { 77 | const signedCommit = new SignedCommit({ 78 | protected: base64url(JSON.stringify(createHeaders)), 79 | signature: 'abc', 80 | } as IFlattenedJws); 81 | 82 | try { 83 | signedCommit.getPayload(); 84 | fail('Should not reach this point.') 85 | } catch (e) { 86 | // Expected 87 | } 88 | }); 89 | 90 | }); 91 | 92 | describe('getObjectId()', () => { 93 | 94 | it('should return the revision for a create commit', async () => { 95 | 96 | const signedCommit = new SignedCommit({ 97 | protected: base64url(JSON.stringify(createHeaders)), 98 | payload: base64url(JSON.stringify({ name: 'test '})), 99 | signature: 'abc', 100 | }); 101 | 102 | expect(signedCommit.getObjectId()).toEqual(signedCommit.getRevision()); 103 | }); 104 | 105 | it('should return the revision for an update commit', async () => { 106 | 107 | const updateHeaders = objectAssign({}, createHeaders, { 108 | operation: 'update', 109 | object_id: 'abc123' 110 | }); 111 | 112 | const signedCommit = new SignedCommit({ 113 | protected: base64url(JSON.stringify(updateHeaders)), 114 | payload: base64url(JSON.stringify({ name: 'test '})), 115 | signature: 'abc', 116 | }); 117 | 118 | expect(signedCommit.getObjectId()).toEqual('abc123'); 119 | }); 120 | 121 | }); 122 | 123 | }); 124 | -------------------------------------------------------------------------------- /tests/TestUtils.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | 3 | /** 4 | * Returns a clone of the given object with certain fields changed. 5 | * 6 | * @param original The original object to clone and modify. 7 | * @param adjustments An object containing fields to be replaced. Each key of the object is a field 8 | * to change, in dotted notation (e.g. `field` or `field.prop`). The value is the new value to set 9 | * for that key. 10 | */ 11 | export const alter = (original: any, adjustments: {[fieldPath: string]: any}) => { 12 | const clone = _.cloneDeep(original); 13 | Object.keys(adjustments).forEach((fieldPath) => { 14 | const newValue = adjustments[fieldPath]; 15 | (newValue !== undefined) 16 | ? _.set(clone, fieldPath, newValue) 17 | : _.unset(clone, fieldPath); 18 | }); 19 | return clone; 20 | }; 21 | 22 | /** 23 | * Returns a debug description of the given value. 24 | */ 25 | export const explain = (value: any): string => { 26 | if (Array.isArray(value)) { 27 | let joined = (value).map(item => explain(item)).join(','); 28 | return `[${joined}]`; 29 | } else if (typeof value === 'string') { 30 | return `'${value}'`; 31 | } else if (value === null) { 32 | return 'null'; 33 | } else if (value === undefined) { 34 | return 'undefined'; 35 | } else if (typeof value === 'object') { 36 | return JSON.stringify(value); 37 | } 38 | return value.toString(); 39 | }; -------------------------------------------------------------------------------- /tests/crypto/CommitSigner.spec.ts: -------------------------------------------------------------------------------- 1 | import { ICommitProtectedHeaders } from '@decentralized-identity/hub-common-js'; 2 | import CommitSigner from '../../src/crypto/CommitSigner'; 3 | import RsaPrivateKey from '@decentralized-identity/did-auth-jose/dist/lib/crypto/rsa/RsaPrivateKey'; 4 | import { EcPrivateKey, Secp256k1CryptoSuite } from '@decentralized-identity/did-auth-jose'; 5 | import Commit from '../../src/Commit'; 6 | 7 | describe('CommitSigner', () => { 8 | 9 | describe('sign()', () => { 10 | 11 | it('should sign a commit using Rsa', async () => { 12 | const testDid = 'did:example:person.id'; 13 | const testKid = `${testDid}#key-1`; 14 | const testKey = await RsaPrivateKey.generatePrivateKey(testKid); 15 | 16 | const protectedHeaders: Partial = { 17 | interface: 'Collections', 18 | context: 'schema.org', 19 | type: 'MusicPlaylist', 20 | operation: 'create', 21 | committed_at: '2019-01-01', 22 | commit_strategy: 'basic', 23 | sub: 'did:example:sub.id', 24 | // iss and kid left out intentionally 25 | }; 26 | 27 | const payload = { 28 | name: "Test" 29 | }; 30 | 31 | const commit = new Commit({ 32 | protected: protectedHeaders, 33 | payload 34 | }); 35 | 36 | const signer = new CommitSigner({ 37 | did: testDid, 38 | key: testKey 39 | }); 40 | 41 | const signedCommit = await signer.sign(commit); 42 | 43 | expect(signedCommit.getPayload()).toEqual(payload); 44 | 45 | const signedProtectedHeaders = signedCommit.getProtectedHeaders(); 46 | Object.keys(protectedHeaders).forEach((headerKey) => { 47 | expect((signedProtectedHeaders as any)[headerKey]).toEqual((protectedHeaders as any)[headerKey]); 48 | }) 49 | 50 | expect(signedProtectedHeaders.iss).toEqual(testDid); 51 | expect(signedProtectedHeaders.kid).toEqual(testKid); 52 | }); 53 | 54 | it('should sign a commit using EC', async () => { 55 | const testDid = 'did:example:person.id'; 56 | const testKid = `${testDid}#key-1`; 57 | const testKey = await EcPrivateKey.generatePrivateKey(testKid); 58 | 59 | const protectedHeaders: Partial = { 60 | interface: 'Collections', 61 | context: 'schema.org', 62 | type: 'MusicPlaylist', 63 | operation: 'create', 64 | committed_at: '2019-01-01', 65 | commit_strategy: 'basic', 66 | sub: 'did:example:sub.id', 67 | // iss and kid left out intentionally 68 | }; 69 | 70 | const payload = { 71 | name: "Test" 72 | }; 73 | 74 | const commit = new Commit({ 75 | protected: protectedHeaders, 76 | payload 77 | }); 78 | 79 | const signer = new CommitSigner({ 80 | did: testDid, 81 | key: testKey, 82 | cryptoSuite: new Secp256k1CryptoSuite() 83 | }); 84 | 85 | const signedCommit = await signer.sign(commit); 86 | 87 | expect(signedCommit.getPayload()).toEqual(payload); 88 | 89 | const signedProtectedHeaders = signedCommit.getProtectedHeaders(); 90 | Object.keys(protectedHeaders).forEach((headerKey) => { 91 | expect((signedProtectedHeaders as any)[headerKey]).toEqual((protectedHeaders as any)[headerKey]); 92 | }) 93 | 94 | expect(signedProtectedHeaders.iss).toEqual(testDid); 95 | expect(signedProtectedHeaders.kid).toEqual(testKid); 96 | }); 97 | 98 | it('should throw an error if a commit is not valid', async () => { 99 | const testDid = 'did:example:person.id'; 100 | const testKid = `${testDid}#key-1`; 101 | const testKey = await RsaPrivateKey.generatePrivateKey(testKid); 102 | 103 | const commit = new Commit({ 104 | protected: { 105 | interface: 'Collections', 106 | context: 'schema.org', 107 | // type: 'MusicPlaylist', // left out intentionally 108 | operation: 'create', 109 | committed_at: '2019-01-01', 110 | commit_strategy: 'basic', 111 | sub: 'did:example:sub.id', 112 | }, 113 | payload: { 114 | name: "Test" 115 | } 116 | }); 117 | 118 | const signer = new CommitSigner({ 119 | did: testDid, 120 | key: testKey 121 | }); 122 | 123 | try { 124 | await signer.sign(commit); 125 | fail('Not expected to reach this point.'); 126 | } catch (err) { 127 | expect(err.message).toContain("Commit 'protected.type' field must be"); 128 | } 129 | }); 130 | 131 | }); 132 | }); 133 | -------------------------------------------------------------------------------- /tests/helpers/reporter.js: -------------------------------------------------------------------------------- 1 | // this is a Jasmine helper function used to export results as xunit tests results. 2 | var jasmineReporters = require('jasmine-reporters'); 3 | var SpecReporter = require('jasmine-spec-reporter').SpecReporter; 4 | 5 | var junitReporter = new jasmineReporters.NUnitXmlReporter({ 6 | savePath: './', 7 | consolidateAll: false, 8 | }); 9 | 10 | var textReporter = new SpecReporter({ // add jasmine-spec-reporter 11 | spec: { 12 | displayDuration: true, 13 | } 14 | }); 15 | 16 | jasmine.getEnv().clearReporters(); 17 | jasmine.getEnv().addReporter(junitReporter); 18 | jasmine.getEnv().addReporter(textReporter); 19 | -------------------------------------------------------------------------------- /tests/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "./tests", 3 | "spec_files": [ 4 | "**/*[sS]pec.ts" 5 | ], 6 | "helpers": [ 7 | "./helpers/**/*.js" 8 | ], 9 | "stopSpecOnExpectationFailure": false, 10 | "random": false 11 | } 12 | -------------------------------------------------------------------------------- /tests/requests/HubCommitQueryRequest.spec.ts: -------------------------------------------------------------------------------- 1 | import HubCommitQueryRequest from '../../src/requests/HubCommitQueryRequest'; 2 | 3 | describe('HubCommitQueryRequest', () => { 4 | 5 | describe('getRequestJson()', () => { 6 | it('should return a complete request body', async () => { 7 | 8 | const req = new HubCommitQueryRequest({ 9 | object_id: ['1234'], 10 | }); 11 | 12 | const json = await req.getRequestJson(); 13 | 14 | expect(json).toEqual({ 15 | '@context': 'https://schema.identity.foundation/0.1', 16 | '@type': 'CommitQueryRequest', 17 | query: { 18 | object_id: ['1234'], 19 | }, 20 | }); 21 | 22 | }); 23 | 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /tests/requests/HubObjectQueryRequest.spec.ts: -------------------------------------------------------------------------------- 1 | import HubObjectQueryRequest from '../../src/requests/HubObjectQueryRequest'; 2 | 3 | describe('HubObjectQueryRequest', () => { 4 | 5 | describe('getRequestJson()', () => { 6 | it('should return a complete request body', async () => { 7 | 8 | const req = new HubObjectQueryRequest({ 9 | object_id: ['1234'], 10 | }); 11 | 12 | const json = await req.getRequestJson(); 13 | 14 | expect(json).toEqual({ 15 | '@context': 'https://schema.identity.foundation/0.1', 16 | '@type': 'ObjectQueryRequest', 17 | query: { 18 | object_id: ['1234'], 19 | }, 20 | }); 21 | 22 | }); 23 | 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /tests/requests/HubWriteRequest.spec.ts: -------------------------------------------------------------------------------- 1 | import HubWriteRequest from '../../src/requests/HubWriteRequest'; 2 | import SignedCommit from '../../src/SignedCommit'; 3 | 4 | describe('HubWriteRequest', () => { 5 | 6 | describe('getRequestJson()', () => { 7 | it('should return a complete request body', async () => { 8 | 9 | const flattenedCommitJson = { 10 | protected: 'test', 11 | payload: 'test', 12 | signature: 'test', 13 | }; 14 | 15 | const req = new HubWriteRequest(new SignedCommit(flattenedCommitJson)); 16 | 17 | const json = await req.getRequestJson(); 18 | 19 | expect(json).toEqual({ 20 | '@context': 'https://schema.identity.foundation/0.1', 21 | '@type': 'WriteRequest', 22 | commit: flattenedCommitJson, 23 | }); 24 | 25 | }); 26 | 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /tests/responses/HubCommitQueryResponse.spec.ts: -------------------------------------------------------------------------------- 1 | import HubCommitQueryResponse from '../../src/responses/HubCommitQueryResponse'; 2 | import SignedCommit from '../../src/SignedCommit'; 3 | 4 | const flattenedCommitJson = { 5 | protected: 'test', 6 | payload: 'test', 7 | signature: 'test', 8 | }; 9 | 10 | const response = new HubCommitQueryResponse({ 11 | '@context': 'https://schema.identity.foundation/0.1', 12 | '@type': 'CommitQueryResponse', 13 | commits: [flattenedCommitJson], 14 | skip_token: 'abc', 15 | }); 16 | 17 | describe('HubCommitQueryResponse', () => { 18 | 19 | describe('constructor', () => { 20 | it('should throw on an invalid response type', async () => { 21 | try { 22 | const r = new HubCommitQueryResponse({ 23 | '@type': 'WrongType', 24 | } as any); 25 | fail('Constructor was expected to throw'); 26 | } catch (e) { 27 | // Expected 28 | } 29 | }); 30 | }); 31 | 32 | describe('getCommits()', () => { 33 | it('should return the matching commits', async () => { 34 | const returnedCommits = await response.getCommits(); 35 | 36 | expect(returnedCommits.length).toEqual(1); 37 | expect(returnedCommits[0].toFlattenedJson()).toEqual(flattenedCommitJson); 38 | }); 39 | }); 40 | 41 | describe('hasSkipToken()', () => { 42 | it('should indicate whether a skip token was returned', async () => { 43 | expect(response.hasSkipToken()).toEqual(true); 44 | }); 45 | }); 46 | 47 | describe('getSkipToken()', () => { 48 | it('should return the skip token', async () => { 49 | expect(response.getSkipToken()).toEqual('abc'); 50 | }); 51 | }); 52 | 53 | }); 54 | -------------------------------------------------------------------------------- /tests/responses/HubObjectQueryResponse.spec.ts: -------------------------------------------------------------------------------- 1 | import { IObjectMetadata } from '@decentralized-identity/hub-common-js'; 2 | import HubObjectQueryResponse from '../../src/responses/HubObjectQueryResponse'; 3 | 4 | const objects: IObjectMetadata[] = [{ 5 | interface: 'Collections', 6 | context: 'schema.org', 7 | type: 'MusicPlaylist', 8 | id: 'abc', 9 | created_by: 'did:test:example.id', 10 | created_at: '2019-01-01', 11 | sub: 'did:test:example.id', 12 | commit_strategy: 'basic', 13 | }]; 14 | 15 | const response = new HubObjectQueryResponse({ 16 | '@context': 'https://schema.identity.foundation/0.1', 17 | '@type': 'ObjectQueryResponse', 18 | objects, 19 | skip_token: 'abc', 20 | }); 21 | 22 | describe('HubObjectQueryResponse', () => { 23 | 24 | describe('constructor', () => { 25 | it('should throw on an invalid response type', async () => { 26 | try { 27 | const r = new HubObjectQueryResponse({ 28 | '@type': 'WrongType', 29 | } as any); 30 | fail('Constructor was expected to throw'); 31 | } catch (e) { 32 | // Expected 33 | } 34 | }); 35 | }); 36 | 37 | describe('getObjects()', () => { 38 | it('should return the matching objects', async () => { 39 | const returnedObjects = await response.getObjects(); 40 | expect(returnedObjects).toEqual(objects); 41 | }); 42 | 43 | it('should return an array even if none was in the response', async () => { 44 | const response = new HubObjectQueryResponse({ 45 | '@context': 'https://schema.identity.foundation/0.1', 46 | '@type': 'ObjectQueryResponse', 47 | objects: null, 48 | } as any); 49 | expect(Array.isArray(response.getObjects())).toEqual(true); 50 | }); 51 | }); 52 | 53 | describe('hasSkipToken()', () => { 54 | it('should indicate whether a skip token was returned', async () => { 55 | expect(response.hasSkipToken()).toEqual(true); 56 | }); 57 | }); 58 | 59 | describe('getSkipToken()', () => { 60 | it('should return the skip token', async () => { 61 | expect(response.getSkipToken()).toEqual('abc'); 62 | }); 63 | }); 64 | 65 | }); 66 | -------------------------------------------------------------------------------- /tests/responses/HubWriteResponse.spec.ts: -------------------------------------------------------------------------------- 1 | import HubWriteResponse from '../../src/responses/HubWriteResponse'; 2 | 3 | describe('HubWriteResponse', () => { 4 | 5 | describe('getRevisions()', () => { 6 | it('should return the object revisions', async () => { 7 | 8 | const resp = new HubWriteResponse({ 9 | '@context': 'https://schema.identity.foundation/0.1', 10 | '@type': 'WriteResponse', 11 | revisions: ['abc', 'def'], 12 | }); 13 | 14 | expect(await resp.getRevisions()).toEqual(['abc', 'def']); 15 | 16 | }); 17 | 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "es2018", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ 5 | "module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 6 | "lib": [ /* Specify library files to be included in the compilation: */ 7 | "dom", 8 | "es2018" 9 | ], 10 | 11 | // "allowJs": true, /* Allow javascript files to be compiled. */ 12 | // "checkJs": true, /* Report errors in .js files. */ 13 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 14 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 15 | "sourceMap": true, /* Generates corresponding '.map' file. */ 16 | // "outFile": "./", /* Concatenate and emit output to single file. */ 17 | "outDir": "./out", /* Redirect output structure to the directory. */ 18 | // "rootDir": "./lib", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | // "removeComments": true, /* Do not emit comments to output. */ 20 | // "noEmit": true, /* Do not emit outputs. */ 21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 22 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 23 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 24 | 25 | /* Strict Type-Checking Options */ 26 | "strict": true, /* Enable all strict type-checking options. */ 27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | // "strictNullChecks": true, /* Enable strict null checks. */ 29 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 30 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 31 | 32 | /* Additional Checks */ 33 | //"noUnusedLocals": true, /* Report errors on unused locals. */ 34 | "noUnusedParameters": true, /* Report errors on unused parameters. */ 35 | "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 36 | "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 37 | 38 | /* Module Resolution Options */ 39 | // "typeRoots": [] /* List of folders to include type definitions from. */ 40 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 41 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 42 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 43 | "rootDirs": [ /* List of root folders whose combined content represents the structure of the project at runtime. */ 44 | "./src", 45 | "./tests" 46 | ] 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | 50 | /* Source Map Options */ 51 | // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 52 | // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ 53 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 54 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 55 | 56 | /* Experimental Options */ 57 | // "experimentalAsyncFunctions": true 58 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 59 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 60 | }, 61 | "exclude": [ 62 | "node_modules", 63 | "dist", 64 | "out" 65 | ] 66 | } 67 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint-config-airbnb", 3 | "linterOptions": { 4 | "exclude": [ 5 | "node_modules/**", 6 | "tests/**" 7 | ] 8 | }, 9 | "rules": { 10 | "max-line-length": [ 11 | true, 12 | 160 13 | ], 14 | "completed-docs": [ 15 | true, 16 | { 17 | "classes": {"visibilities": ["exported"]}, 18 | "enums": {"visibilities": ["exported"]}, 19 | "functions": {"visibilities": ["exported"]}, 20 | "interfaces": {"visibilities": ["exported"]}, 21 | "methods": {"privacies": ["public", "protected"], 22 | "locations": ["all"]}, 23 | "properties": {"privacies": ["public", "protected"], 24 | "locations": ["all"]}, 25 | "types": {"visibilities": ["all"]} 26 | } 27 | ], 28 | "jsdoc-format": [ 29 | true, 30 | "check-multiline-start" 31 | ], 32 | "variable-name": [ 33 | true, 34 | "check-format", 35 | "allow-leading-underscore" 36 | ] 37 | } 38 | } -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "out": "./docs", 3 | "exclude": ["**/node_modules/**", ".\\src\\example.ts"], 4 | "mode": "file", 5 | "gitRevision": "master" 6 | } --------------------------------------------------------------------------------