@interop/did-web-resolver 1.0.1 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/build-dist.sh +14 -0
- package/dist/DidWebResolver.js +336 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/package.json +3 -0
- package/dist/index.js +14 -0
- package/package.json +43 -20
- package/rollup.config.js +15 -0
- package/src/DidWebResolver.js +78 -17
- package/.github/workflows/main.yml +0 -35
- package/.idea/codeStyles/Project.xml +0 -38
- package/.idea/codeStyles/codeStyleConfig.xml +0 -5
- package/.idea/did-web-resolver.iml +0 -12
- package/.idea/inspectionProfiles/Project_Default.xml +0 -6
- package/.idea/jsLibraryMappings.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/vcs.xml +0 -6
- package/.travis.yml +0 -10
- package/CHANGELOG.md +0 -24
- package/src/main.js +0 -3
- package/test/karma.conf.js +0 -40
- package/test/unit/DidWebResolver.spec.js +0 -175
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ TBD
|
|
|
21
21
|
## Background
|
|
22
22
|
|
|
23
23
|
A `did:web` method driver for use with in-browser and server-side on Node.js
|
|
24
|
-
with the [`did-io`](https://github.com/
|
|
24
|
+
with the [`did-io`](https://github.com/digitalcredentials/did-io) resolver library.
|
|
25
25
|
|
|
26
26
|
Draft spec (W3C CCG Work Item):
|
|
27
27
|
|
|
@@ -37,9 +37,9 @@ Other implementations:
|
|
|
37
37
|
|
|
38
38
|
```js
|
|
39
39
|
import { Ed25519VerificationKey2020 }
|
|
40
|
-
from '@
|
|
40
|
+
from '@digitalcredentials/ed25519-verification-key-2020'
|
|
41
41
|
import { X25519KeyAgreementKey2020 }
|
|
42
|
-
from '@
|
|
42
|
+
from '@digitalcredentials/x25519-key-agreement-key-2020'
|
|
43
43
|
import { CryptoLD } from 'crypto-ld'
|
|
44
44
|
|
|
45
45
|
import * as didWeb from '@interop/did-web-resolver'
|
|
@@ -51,7 +51,7 @@ cryptoLd.use(X25519KeyAgreementKey2020)
|
|
|
51
51
|
const didWebDriver = didWeb.driver({ cryptoLd })
|
|
52
52
|
|
|
53
53
|
// Optionally use it with the CachedResolver from did-io
|
|
54
|
-
import {CachedResolver} from '@
|
|
54
|
+
import {CachedResolver} from '@digitalcredentials/did-io';
|
|
55
55
|
const resolver = new CachedResolver()
|
|
56
56
|
resolver.use(didWebDriver)
|
|
57
57
|
```
|
package/build-dist.sh
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
mkdir ./dist/esm
|
|
2
|
+
cat >dist/esm/index.js <<!EOF
|
|
3
|
+
import cjsModule from '../index.js';
|
|
4
|
+
export const driver = cjsModule.driver;
|
|
5
|
+
export const DidWebResolver = cjsModule.DidWebResolver;
|
|
6
|
+
export const didFromUrl = cjsModule.didFromUrl;
|
|
7
|
+
export const urlFromDid = cjsModule.urlFromDid;
|
|
8
|
+
!EOF
|
|
9
|
+
|
|
10
|
+
cat >dist/esm/package.json <<!EOF
|
|
11
|
+
{
|
|
12
|
+
"type": "module"
|
|
13
|
+
}
|
|
14
|
+
!EOF
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var httpClient = require('@digitalcredentials/http-client');
|
|
6
|
+
var didIo = require('@digitalcredentials/did-io');
|
|
7
|
+
var ed25519Context = require('ed25519-signature-2020-context');
|
|
8
|
+
var x25519Context = require('x25519-key-agreement-2020-context');
|
|
9
|
+
var didContext = require('did-context');
|
|
10
|
+
|
|
11
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
12
|
+
|
|
13
|
+
function _interopNamespace(e) {
|
|
14
|
+
if (e && e.__esModule) return e;
|
|
15
|
+
var n = Object.create(null);
|
|
16
|
+
if (e) {
|
|
17
|
+
Object.keys(e).forEach(function (k) {
|
|
18
|
+
if (k !== 'default') {
|
|
19
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
20
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
get: function () { return e[k]; }
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
n["default"] = e;
|
|
28
|
+
return Object.freeze(n);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
var didIo__namespace = /*#__PURE__*/_interopNamespace(didIo);
|
|
32
|
+
var ed25519Context__default = /*#__PURE__*/_interopDefaultLegacy(ed25519Context);
|
|
33
|
+
var x25519Context__default = /*#__PURE__*/_interopDefaultLegacy(x25519Context);
|
|
34
|
+
var didContext__default = /*#__PURE__*/_interopDefaultLegacy(didContext);
|
|
35
|
+
|
|
36
|
+
const { VERIFICATION_RELATIONSHIPS } = didIo__namespace;
|
|
37
|
+
|
|
38
|
+
const DEFAULT_KEY_MAP = {
|
|
39
|
+
capabilityInvocation: 'Ed25519VerificationKey2020',
|
|
40
|
+
authentication: 'Ed25519VerificationKey2020',
|
|
41
|
+
assertionMethod: 'Ed25519VerificationKey2020',
|
|
42
|
+
capabilityDelegation: 'Ed25519VerificationKey2020',
|
|
43
|
+
keyAgreement: 'X25519KeyAgreementKey2020'
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function didFromUrl ({ url } = {}) {
|
|
47
|
+
if (!url) {
|
|
48
|
+
throw new TypeError('Cannot convert url to did, missing url.')
|
|
49
|
+
}
|
|
50
|
+
if (url.startsWith('http:')) {
|
|
51
|
+
throw new TypeError('did:web does not support non-HTTPS URLs.')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let parsedUrl;
|
|
55
|
+
try {
|
|
56
|
+
parsedUrl = new URL(url);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
throw new TypeError(`Invalid url: "${url}".`)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const { host, pathname } = parsedUrl;
|
|
62
|
+
|
|
63
|
+
let pathComponent = '';
|
|
64
|
+
if (pathname && pathname !== '/' && pathname !== '/.well-known/did.json') {
|
|
65
|
+
pathComponent = pathname.split('/').map(encodeURIComponent).join(':');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return 'did:web:' + encodeURIComponent(host) + pathComponent
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function urlFromDid ({ did } = {}) {
|
|
72
|
+
if (!did) {
|
|
73
|
+
throw new TypeError('Cannot convert did to url, missing did.')
|
|
74
|
+
}
|
|
75
|
+
if (!did.startsWith('did:web:')) {
|
|
76
|
+
throw new TypeError(`DID Method not supported: "${did}".`)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const [didUrl, hashFragment] = did.split('#');
|
|
80
|
+
// eslint-disable-next-line no-unused-vars
|
|
81
|
+
// const [didResource, query] = didUrl.split('?')
|
|
82
|
+
|
|
83
|
+
// eslint-disable-next-line no-unused-vars
|
|
84
|
+
const [_did, _web, urlNoProtocol] = didUrl.split(':');
|
|
85
|
+
|
|
86
|
+
let parsedUrl;
|
|
87
|
+
try {
|
|
88
|
+
// URI-decode the url (in case it contained a port number,
|
|
89
|
+
// for example, `did:web:localhost%3A8080`
|
|
90
|
+
parsedUrl = new URL('https://' + decodeURIComponent(urlNoProtocol));
|
|
91
|
+
} catch (error) {
|
|
92
|
+
throw new TypeError(`Cannot construct url from did: "${did}".`)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!parsedUrl.pathname || parsedUrl.pathname === '/') {
|
|
96
|
+
parsedUrl.pathname = '/.well-known/did.json';
|
|
97
|
+
} else {
|
|
98
|
+
const pathFragments = parsedUrl.pathname.split('/');
|
|
99
|
+
parsedUrl.pathname = pathFragments.map(decodeURIComponent).join('/');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (hashFragment) {
|
|
103
|
+
parsedUrl.hash = hashFragment;
|
|
104
|
+
}
|
|
105
|
+
return parsedUrl.toString()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Initializes the DID Document's keys/proof methods.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* didDocument.id = 'did:ex:123';
|
|
113
|
+
* const {didDocument, keyPairs} = await initKeys({
|
|
114
|
+
* didDocument,
|
|
115
|
+
* cryptoLd,
|
|
116
|
+
* keyMap: {
|
|
117
|
+
* capabilityInvocation: someExistingKey,
|
|
118
|
+
* authentication: 'Ed25519VerificationKey2020',
|
|
119
|
+
* assertionMethod: 'Ed25519VerificationKey2020',
|
|
120
|
+
* keyAgreement: 'X25519KeyAgreementKey2019'
|
|
121
|
+
* }
|
|
122
|
+
* });.
|
|
123
|
+
*
|
|
124
|
+
* @param {object} options - Options hashmap.
|
|
125
|
+
* @param {object} options.didDocument - DID Document.
|
|
126
|
+
* @typedef {object} CryptoLD
|
|
127
|
+
* @param {CryptoLD} [options.cryptoLd] - CryptoLD driver instance,
|
|
128
|
+
* initialized with the key types this DID Document intends to support.
|
|
129
|
+
* @param {object} [options.keyMap] - Map of keys (or key types) by purpose.
|
|
130
|
+
*
|
|
131
|
+
* @returns {Promise<{didDocument: object, keyPairs: Map}>} Resolves with the
|
|
132
|
+
* DID Document initialized with keys, as well as the map of the corresponding
|
|
133
|
+
* key pairs (by key id).
|
|
134
|
+
*/
|
|
135
|
+
async function initKeys ({ didDocument, cryptoLd, keyMap = {} } = {}) {
|
|
136
|
+
const doc = { ...didDocument };
|
|
137
|
+
if (!doc.id) {
|
|
138
|
+
throw new TypeError(
|
|
139
|
+
'DID Document "id" property is required to initialize keys.')
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const keyPairs = new Map();
|
|
143
|
+
|
|
144
|
+
// Set the defaults for the created keys (if needed)
|
|
145
|
+
const options = { controller: doc.id };
|
|
146
|
+
|
|
147
|
+
for (const purpose in keyMap) {
|
|
148
|
+
if (!VERIFICATION_RELATIONSHIPS.has(purpose)) {
|
|
149
|
+
throw new Error(`Unsupported key purpose: "${purpose}".`)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
let key;
|
|
153
|
+
if (typeof keyMap[purpose] === 'string') {
|
|
154
|
+
if (!cryptoLd) {
|
|
155
|
+
throw new Error('Please provide an initialized CryptoLD instance.')
|
|
156
|
+
}
|
|
157
|
+
key = await cryptoLd.generate({ type: keyMap[purpose], ...options });
|
|
158
|
+
} else {
|
|
159
|
+
// An existing key has been provided
|
|
160
|
+
key = keyMap[purpose];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
doc[purpose] = [key.export({ publicKey: true })];
|
|
164
|
+
keyPairs.set(key.id, key);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return { didDocument: doc, keyPairs }
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
class DidWebResolver {
|
|
171
|
+
/**
|
|
172
|
+
* @param cryptoLd {CryptoLD}
|
|
173
|
+
* @param keyMap {object}
|
|
174
|
+
* @param [logger] {object} Logger object (with .log, .error, .warn,
|
|
175
|
+
* etc methods).
|
|
176
|
+
*/
|
|
177
|
+
constructor ({ cryptoLd, keyMap = DEFAULT_KEY_MAP, logger = console } = {}) {
|
|
178
|
+
this.method = 'web'; // did:web:... (used for didIo resolver harness)
|
|
179
|
+
this.cryptoLd = cryptoLd;
|
|
180
|
+
this.keyMap = keyMap;
|
|
181
|
+
this.logger = logger;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Generates a new DID Document and initializes various authentication
|
|
186
|
+
* and authorization proof purpose keys.
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* const url = 'https://example.com'
|
|
190
|
+
* const { didDocument, didKeys } = await didWeb.generate({url})
|
|
191
|
+
* didDocument.id
|
|
192
|
+
* // -> 'did:web:example.com'
|
|
193
|
+
*
|
|
194
|
+
*
|
|
195
|
+
* Either an `id` or a `url` is required:
|
|
196
|
+
* @param [id] {string} - A did:web DID. If absent, will be converted from url
|
|
197
|
+
* @param [url] {string}
|
|
198
|
+
*
|
|
199
|
+
* @param [keyMap=DEFAULT_KEY_MAP] {object} A hashmap of key types by purpose.
|
|
200
|
+
*
|
|
201
|
+
* @parma [cryptoLd] {object} CryptoLD instance with support for supported
|
|
202
|
+
* crypto suites installed.
|
|
203
|
+
*
|
|
204
|
+
* @returns {Promise<{didDocument: object, keyPairs: Map,
|
|
205
|
+
* methodFor: Function}>} Resolves with the generated DID Document, along
|
|
206
|
+
* with the corresponding key pairs used to generate it (for storage in a
|
|
207
|
+
* KMS).
|
|
208
|
+
*/
|
|
209
|
+
async generate ({ id, url, keyMap = this.keyMap, cryptoLd = this.cryptoLd } = {}) {
|
|
210
|
+
const did = id || didFromUrl({ url });
|
|
211
|
+
|
|
212
|
+
// Compose the DID Document
|
|
213
|
+
let didDocument = {
|
|
214
|
+
'@context': [
|
|
215
|
+
didContext__default["default"].constants.DID_CONTEXT_URL,
|
|
216
|
+
ed25519Context__default["default"].constants.CONTEXT_URL,
|
|
217
|
+
x25519Context__default["default"].constants.CONTEXT_URL
|
|
218
|
+
],
|
|
219
|
+
id: did
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const result = await initKeys({ didDocument, cryptoLd, keyMap });
|
|
223
|
+
const keyPairs = result.keyPairs;
|
|
224
|
+
didDocument = result.didDocument;
|
|
225
|
+
|
|
226
|
+
// Convenience function that returns the public/private key pair instance
|
|
227
|
+
// for a given purpose (authentication, assertionMethod, keyAgreement, etc).
|
|
228
|
+
const methodFor = ({ purpose }) => {
|
|
229
|
+
const { id: methodId } = didIo__namespace.findVerificationMethod({
|
|
230
|
+
doc: didDocument, purpose
|
|
231
|
+
});
|
|
232
|
+
return keyPairs.get(methodId)
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
return { didDocument, keyPairs, methodFor }
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Fetches a DID Document for a given DID.
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* // In Node.js tests, use an agent to avoid self-signed certificate errors
|
|
243
|
+
* const agent = new https.agent({rejectUnauthorized: false});
|
|
244
|
+
*
|
|
245
|
+
* @param {string} [did] For example, 'did:web:example.com'
|
|
246
|
+
* @param {string} [url]
|
|
247
|
+
* @param {https.Agent} [agent] Optional agent used to customize network
|
|
248
|
+
* behavior in Node.js (such as `rejectUnauthorized: false`).
|
|
249
|
+
* @param {object} [logger] Logger object (with .log, .error, .warn,
|
|
250
|
+
* etc methods).
|
|
251
|
+
*
|
|
252
|
+
* @throws {Error}
|
|
253
|
+
*
|
|
254
|
+
* @returns {Promise<object>} Plain parsed JSON object of the DID Document.
|
|
255
|
+
*/
|
|
256
|
+
async get ({ did, url, agent, logger = this.logger }) {
|
|
257
|
+
const didUrl = url || urlFromDid({ did });
|
|
258
|
+
if (!didUrl) {
|
|
259
|
+
throw new TypeError('A DID or a URL is required.')
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const [urlAuthority, keyIdFragment] = didUrl.split('#');
|
|
263
|
+
|
|
264
|
+
let didDocument;
|
|
265
|
+
try {
|
|
266
|
+
logger.info(`Fetching "${urlAuthority}" via http client.`);
|
|
267
|
+
const result = await httpClient.httpClient.get(urlAuthority, { agent });
|
|
268
|
+
didDocument = result.data;
|
|
269
|
+
} catch (e) {
|
|
270
|
+
// status is HTTP status code
|
|
271
|
+
// data is JSON error from the server if available
|
|
272
|
+
const { data, status } = e;
|
|
273
|
+
logger.error(`Http ${status} error:`, data);
|
|
274
|
+
throw e
|
|
275
|
+
}
|
|
276
|
+
if (didDocument && keyIdFragment) {
|
|
277
|
+
// resolve an individual key
|
|
278
|
+
// Keys are expected to have format: <did:web:...>#<keyIdFragment>
|
|
279
|
+
const didAuthority = didFromUrl({ url: urlAuthority });
|
|
280
|
+
const methodId = `${didAuthority}#${keyIdFragment}`;
|
|
281
|
+
|
|
282
|
+
const key = didIo__namespace.findVerificationMethod({ doc: didDocument, methodId });
|
|
283
|
+
if (!key) {
|
|
284
|
+
throw new Error(`Key id ${methodId} not found.`)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const keyPair = await this.cryptoLd.from(key);
|
|
288
|
+
|
|
289
|
+
return keyPair.export({ publicKey: true, includeContext: true })
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return didDocument
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Returns the public key (verification method) object for a given DID
|
|
297
|
+
* Document and purpose. Useful in conjunction with a `.get()` call.
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* const didDocument = await didKeyDriver.get({did});
|
|
301
|
+
* const authKeyData = didDriver.publicMethodFor({
|
|
302
|
+
* didDocument, purpose: 'authentication'
|
|
303
|
+
* });
|
|
304
|
+
* // You can then create a suite instance object to verify signatures etc.
|
|
305
|
+
* const authPublicKey = await cryptoLd.from(authKeyData);
|
|
306
|
+
* const {verify} = authPublicKey.verifier();
|
|
307
|
+
*
|
|
308
|
+
* @param {object} options - Options hashmap.
|
|
309
|
+
* @param {object} options.didDocument - DID Document (retrieved via a
|
|
310
|
+
* `.get()` or from some other source).
|
|
311
|
+
* @param {string} options.purpose - Verification method purpose, such as
|
|
312
|
+
* 'authentication', 'assertionMethod', 'keyAgreement' and so on.
|
|
313
|
+
*
|
|
314
|
+
* @returns {object} Returns the public key object (obtained from the DID
|
|
315
|
+
* Document), without a `@context`.
|
|
316
|
+
*/
|
|
317
|
+
publicMethodFor ({ didDocument, purpose } = {}) {
|
|
318
|
+
if (!didDocument) {
|
|
319
|
+
throw new TypeError('The "didDocument" parameter is required.')
|
|
320
|
+
}
|
|
321
|
+
if (!purpose) {
|
|
322
|
+
throw new TypeError('The "purpose" parameter is required.')
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const method = didIo__namespace.findVerificationMethod({ doc: didDocument, purpose });
|
|
326
|
+
if (!method) {
|
|
327
|
+
throw new Error(`No verification method found for purpose "${purpose}"`)
|
|
328
|
+
}
|
|
329
|
+
return method
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
exports.DidWebResolver = DidWebResolver;
|
|
334
|
+
exports.didFromUrl = didFromUrl;
|
|
335
|
+
exports.initKeys = initKeys;
|
|
336
|
+
exports.urlFromDid = urlFromDid;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var DidWebResolver = require('./DidWebResolver.js');
|
|
6
|
+
|
|
7
|
+
const driver = options => {
|
|
8
|
+
return new DidWebResolver.DidWebResolver(options)
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
exports.DidWebResolver = DidWebResolver.DidWebResolver;
|
|
12
|
+
exports.didFromUrl = DidWebResolver.didFromUrl;
|
|
13
|
+
exports.urlFromDid = DidWebResolver.urlFromDid;
|
|
14
|
+
exports.driver = driver;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@interop/did-web-resolver",
|
|
3
3
|
"description": "A did:web method Decentralized Identifier (DID) resolver for the did-io library.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "2.1.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Dmitri Zagidulin",
|
|
7
7
|
"url": "https://github.com/dmitrizagidulin/"
|
|
@@ -14,34 +14,56 @@
|
|
|
14
14
|
"homepage": "https://github.com/interop-alliance/did-web-driver",
|
|
15
15
|
"bugs": "https://github.com/interop-alliance/did-web-driver/issues",
|
|
16
16
|
"scripts": {
|
|
17
|
+
"rollup": "rollup -c rollup.config.js",
|
|
18
|
+
"build": "npm run clear && npm run rollup && ./build-dist.sh",
|
|
19
|
+
"clear": "rimraf dist/ && mkdir dist",
|
|
20
|
+
"prepare": "npm run build",
|
|
21
|
+
"rebuild": "npm run clear && npm run build",
|
|
17
22
|
"test": "npm run standard && npm run test-node",
|
|
18
23
|
"test-node": "cross-env NODE_ENV=test mocha -r esm --preserve-symlinks -t 10000 test/**/*.spec.js",
|
|
19
24
|
"test-karma": "karma start test/karma.conf.js",
|
|
20
25
|
"nyc": "cross-env NODE_ENV=test nyc npm run test-node",
|
|
21
26
|
"standard": "standard --fix"
|
|
22
27
|
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"src",
|
|
31
|
+
"rollup.config.js",
|
|
32
|
+
"build-dist.sh",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"main": "dist/index.js",
|
|
37
|
+
"module": "dist/esm/index.js",
|
|
38
|
+
"exports": {
|
|
39
|
+
".": {
|
|
40
|
+
"require": "./dist/index.js",
|
|
41
|
+
"import": "./dist/esm/index.js"
|
|
42
|
+
},
|
|
43
|
+
"./package.json": "./package.json"
|
|
44
|
+
},
|
|
23
45
|
"dependencies": {
|
|
24
|
-
"@
|
|
25
|
-
"@
|
|
26
|
-
"did-context": "^3.
|
|
46
|
+
"@digitalcredentials/did-io": "^1.0.2",
|
|
47
|
+
"@digitalcredentials/http-client": "^1.2.2",
|
|
48
|
+
"did-context": "^3.1.1",
|
|
27
49
|
"ed25519-signature-2020-context": "^1.1.0",
|
|
28
|
-
"esm": "^3.2.25",
|
|
29
50
|
"x25519-key-agreement-2020-context": "^1.0.0"
|
|
30
51
|
},
|
|
31
52
|
"devDependencies": {
|
|
32
|
-
"@babel/core": "^7.
|
|
33
|
-
"@babel/plugin-transform-modules-commonjs": "^7.
|
|
34
|
-
"@babel/plugin-transform-runtime": "^7.
|
|
35
|
-
"@babel/preset-env": "^7.
|
|
36
|
-
"@babel/runtime": "^7.
|
|
37
|
-
"@
|
|
38
|
-
"@
|
|
39
|
-
"babel-loader": "^8.2.
|
|
53
|
+
"@babel/core": "^7.16.7",
|
|
54
|
+
"@babel/plugin-transform-modules-commonjs": "^7.16.7",
|
|
55
|
+
"@babel/plugin-transform-runtime": "^7.16.7",
|
|
56
|
+
"@babel/preset-env": "^7.16.7",
|
|
57
|
+
"@babel/runtime": "^7.16.7",
|
|
58
|
+
"@digitalcredentials/ed25519-verification-key-2020": "^3.2.2",
|
|
59
|
+
"@digitalcredentials/x25519-key-agreement-key-2020": "^2.0.2",
|
|
60
|
+
"babel-loader": "^8.2.3",
|
|
40
61
|
"chai": "^4.3.4",
|
|
41
62
|
"cross-env": "^7.0.3",
|
|
42
|
-
"crypto-ld": "^
|
|
63
|
+
"crypto-ld": "^6.0.0",
|
|
64
|
+
"esm": "^3.2.25",
|
|
43
65
|
"dirty-chai": "^2.0.1",
|
|
44
|
-
"karma": "^6.3.
|
|
66
|
+
"karma": "^6.3.9",
|
|
45
67
|
"karma-babel-preprocessor": "^8.0.1",
|
|
46
68
|
"karma-chai": "^0.1.0",
|
|
47
69
|
"karma-chrome-launcher": "^3.1.0",
|
|
@@ -49,13 +71,14 @@
|
|
|
49
71
|
"karma-mocha-reporter": "^2.2.5",
|
|
50
72
|
"karma-sourcemap-loader": "^0.3.8",
|
|
51
73
|
"karma-webpack": "^5.0.0",
|
|
52
|
-
"mocha": "^8.
|
|
74
|
+
"mocha": "^8.4.0",
|
|
53
75
|
"nyc": "^15.1.0",
|
|
54
|
-
"sinon": "^
|
|
55
|
-
"standard": "^16.0.
|
|
56
|
-
"
|
|
76
|
+
"sinon": "^12.0.1",
|
|
77
|
+
"standard": "^16.0.4",
|
|
78
|
+
"rimraf": "^3.0.2",
|
|
79
|
+
"rollup": "^2.62.0",
|
|
80
|
+
"webpack": "^5.65.0"
|
|
57
81
|
},
|
|
58
|
-
"main": "src/main.js",
|
|
59
82
|
"nyc": {
|
|
60
83
|
"reporter": [
|
|
61
84
|
"html",
|
package/rollup.config.js
ADDED
package/src/DidWebResolver.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { httpClient } from '@
|
|
2
|
-
import * as didIo from '@
|
|
1
|
+
import { httpClient } from '@digitalcredentials/http-client'
|
|
2
|
+
import * as didIo from '@digitalcredentials/did-io'
|
|
3
3
|
import ed25519Context from 'ed25519-signature-2020-context'
|
|
4
4
|
import x25519Context from 'x25519-key-agreement-2020-context'
|
|
5
5
|
import didContext from 'did-context'
|
|
@@ -49,25 +49,31 @@ export function urlFromDid ({ did } = {}) {
|
|
|
49
49
|
|
|
50
50
|
const [didUrl, hashFragment] = did.split('#')
|
|
51
51
|
// eslint-disable-next-line no-unused-vars
|
|
52
|
-
const [didResource, query] = didUrl.split('?')
|
|
52
|
+
// const [didResource, query] = didUrl.split('?')
|
|
53
53
|
|
|
54
54
|
// eslint-disable-next-line no-unused-vars
|
|
55
|
-
const [_did, _web,
|
|
55
|
+
const [_did, _web, urlNoProtocol] = didUrl.split(':')
|
|
56
56
|
|
|
57
|
-
let
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
let parsedUrl
|
|
58
|
+
try {
|
|
59
|
+
// URI-decode the url (in case it contained a port number,
|
|
60
|
+
// for example, `did:web:localhost%3A8080`
|
|
61
|
+
parsedUrl = new URL('https://' + decodeURIComponent(urlNoProtocol))
|
|
62
|
+
} catch (error) {
|
|
63
|
+
throw new TypeError(`Cannot construct url from did: "${did}".`)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!parsedUrl.pathname || parsedUrl.pathname === '/') {
|
|
67
|
+
parsedUrl.pathname = '/.well-known/did.json'
|
|
60
68
|
} else {
|
|
61
|
-
|
|
69
|
+
const pathFragments = parsedUrl.pathname.split('/')
|
|
70
|
+
parsedUrl.pathname = pathFragments.map(decodeURIComponent).join('/')
|
|
62
71
|
}
|
|
63
72
|
|
|
64
|
-
const url = new URL(pathname, 'https://' + decodeURIComponent(host))
|
|
65
73
|
if (hashFragment) {
|
|
66
|
-
|
|
74
|
+
parsedUrl.hash = hashFragment
|
|
67
75
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return url.toString()
|
|
76
|
+
return parsedUrl.toString()
|
|
71
77
|
}
|
|
72
78
|
|
|
73
79
|
/**
|
|
@@ -224,10 +230,13 @@ export class DidWebResolver {
|
|
|
224
230
|
throw new TypeError('A DID or a URL is required.')
|
|
225
231
|
}
|
|
226
232
|
|
|
227
|
-
|
|
233
|
+
const [urlAuthority, keyIdFragment] = didUrl.split('#')
|
|
234
|
+
|
|
235
|
+
let didDocument
|
|
228
236
|
try {
|
|
229
|
-
logger.info(`Fetching "${
|
|
230
|
-
result = await httpClient.get(
|
|
237
|
+
logger.info(`Fetching "${urlAuthority}" via http client.`)
|
|
238
|
+
const result = await httpClient.get(urlAuthority, { agent })
|
|
239
|
+
didDocument = result.data
|
|
231
240
|
} catch (e) {
|
|
232
241
|
// status is HTTP status code
|
|
233
242
|
// data is JSON error from the server if available
|
|
@@ -235,7 +244,59 @@ export class DidWebResolver {
|
|
|
235
244
|
logger.error(`Http ${status} error:`, data)
|
|
236
245
|
throw e
|
|
237
246
|
}
|
|
247
|
+
if (didDocument && keyIdFragment) {
|
|
248
|
+
// resolve an individual key
|
|
249
|
+
// Keys are expected to have format: <did:web:...>#<keyIdFragment>
|
|
250
|
+
const didAuthority = didFromUrl({ url: urlAuthority })
|
|
251
|
+
const methodId = `${didAuthority}#${keyIdFragment}`
|
|
252
|
+
|
|
253
|
+
const key = didIo.findVerificationMethod({ doc: didDocument, methodId })
|
|
254
|
+
if (!key) {
|
|
255
|
+
throw new Error(`Key id ${methodId} not found.`)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const keyPair = await this.cryptoLd.from(key)
|
|
259
|
+
|
|
260
|
+
return keyPair.export({ publicKey: true, includeContext: true })
|
|
261
|
+
}
|
|
238
262
|
|
|
239
|
-
return
|
|
263
|
+
return didDocument
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Returns the public key (verification method) object for a given DID
|
|
268
|
+
* Document and purpose. Useful in conjunction with a `.get()` call.
|
|
269
|
+
*
|
|
270
|
+
* @example
|
|
271
|
+
* const didDocument = await didKeyDriver.get({did});
|
|
272
|
+
* const authKeyData = didDriver.publicMethodFor({
|
|
273
|
+
* didDocument, purpose: 'authentication'
|
|
274
|
+
* });
|
|
275
|
+
* // You can then create a suite instance object to verify signatures etc.
|
|
276
|
+
* const authPublicKey = await cryptoLd.from(authKeyData);
|
|
277
|
+
* const {verify} = authPublicKey.verifier();
|
|
278
|
+
*
|
|
279
|
+
* @param {object} options - Options hashmap.
|
|
280
|
+
* @param {object} options.didDocument - DID Document (retrieved via a
|
|
281
|
+
* `.get()` or from some other source).
|
|
282
|
+
* @param {string} options.purpose - Verification method purpose, such as
|
|
283
|
+
* 'authentication', 'assertionMethod', 'keyAgreement' and so on.
|
|
284
|
+
*
|
|
285
|
+
* @returns {object} Returns the public key object (obtained from the DID
|
|
286
|
+
* Document), without a `@context`.
|
|
287
|
+
*/
|
|
288
|
+
publicMethodFor ({ didDocument, purpose } = {}) {
|
|
289
|
+
if (!didDocument) {
|
|
290
|
+
throw new TypeError('The "didDocument" parameter is required.')
|
|
291
|
+
}
|
|
292
|
+
if (!purpose) {
|
|
293
|
+
throw new TypeError('The "purpose" parameter is required.')
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const method = didIo.findVerificationMethod({ doc: didDocument, purpose })
|
|
297
|
+
if (!method) {
|
|
298
|
+
throw new Error(`No verification method found for purpose "${purpose}"`)
|
|
299
|
+
}
|
|
300
|
+
return method
|
|
240
301
|
}
|
|
241
302
|
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
name: Node.js CI
|
|
2
|
-
|
|
3
|
-
on: [push]
|
|
4
|
-
|
|
5
|
-
jobs:
|
|
6
|
-
test-node:
|
|
7
|
-
runs-on: ubuntu-latest
|
|
8
|
-
strategy:
|
|
9
|
-
matrix:
|
|
10
|
-
node-version: [14.x]
|
|
11
|
-
steps:
|
|
12
|
-
- uses: actions/checkout@v2
|
|
13
|
-
- name: Use Node.js ${{ matrix.node-version }}
|
|
14
|
-
uses: actions/setup-node@v1
|
|
15
|
-
with:
|
|
16
|
-
node-version: ${{ matrix.node-version }}
|
|
17
|
-
- run: npm install
|
|
18
|
-
- name: Run test with Node.js ${{ matrix.node-version }}
|
|
19
|
-
run: npm run test-node
|
|
20
|
-
env:
|
|
21
|
-
CI: true
|
|
22
|
-
lint:
|
|
23
|
-
runs-on: ubuntu-latest
|
|
24
|
-
strategy:
|
|
25
|
-
matrix:
|
|
26
|
-
node-version: [14.x]
|
|
27
|
-
steps:
|
|
28
|
-
- uses: actions/checkout@v2
|
|
29
|
-
- name: Use Node.js ${{ matrix.node-version }}
|
|
30
|
-
uses: actions/setup-node@v1
|
|
31
|
-
with:
|
|
32
|
-
node-version: ${{ matrix.node-version }}
|
|
33
|
-
- run: npm install
|
|
34
|
-
- name: Run Standard.js linter
|
|
35
|
-
run: npm run standard
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
<component name="ProjectCodeStyleConfiguration">
|
|
2
|
-
<code_scheme name="Project" version="173">
|
|
3
|
-
<option name="RIGHT_MARGIN" value="80" />
|
|
4
|
-
<JSCodeStyleSettings version="0">
|
|
5
|
-
<option name="USE_SEMICOLON_AFTER_STATEMENT" value="false" />
|
|
6
|
-
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
|
7
|
-
<option name="SPACE_BEFORE_GENERATOR_MULT" value="true" />
|
|
8
|
-
<option name="USE_DOUBLE_QUOTES" value="false" />
|
|
9
|
-
<option name="FORCE_QUOTE_STYlE" value="true" />
|
|
10
|
-
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
|
11
|
-
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
|
12
|
-
</JSCodeStyleSettings>
|
|
13
|
-
<codeStyleSettings language="HTML">
|
|
14
|
-
<indentOptions>
|
|
15
|
-
<option name="INDENT_SIZE" value="2" />
|
|
16
|
-
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
|
17
|
-
<option name="TAB_SIZE" value="2" />
|
|
18
|
-
</indentOptions>
|
|
19
|
-
</codeStyleSettings>
|
|
20
|
-
<codeStyleSettings language="JavaScript">
|
|
21
|
-
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
|
22
|
-
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
|
23
|
-
<option name="ALIGN_MULTILINE_FOR" value="false" />
|
|
24
|
-
<option name="SPACE_BEFORE_METHOD_PARENTHESES" value="true" />
|
|
25
|
-
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
|
|
26
|
-
<option name="KEEP_SIMPLE_BLOCKS_IN_ONE_LINE" value="true" />
|
|
27
|
-
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
|
|
28
|
-
<indentOptions>
|
|
29
|
-
<option name="INDENT_SIZE" value="2" />
|
|
30
|
-
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
|
31
|
-
<option name="TAB_SIZE" value="2" />
|
|
32
|
-
</indentOptions>
|
|
33
|
-
</codeStyleSettings>
|
|
34
|
-
<codeStyleSettings language="Markdown">
|
|
35
|
-
<option name="SOFT_MARGINS" value="80" />
|
|
36
|
-
</codeStyleSettings>
|
|
37
|
-
</code_scheme>
|
|
38
|
-
</component>
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<module type="WEB_MODULE" version="4">
|
|
3
|
-
<component name="NewModuleRootManager">
|
|
4
|
-
<content url="file://$MODULE_DIR$">
|
|
5
|
-
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
|
6
|
-
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
|
7
|
-
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
|
8
|
-
</content>
|
|
9
|
-
<orderEntry type="inheritedJdk" />
|
|
10
|
-
<orderEntry type="sourceFolder" forTests="false" />
|
|
11
|
-
</component>
|
|
12
|
-
</module>
|
package/.idea/modules.xml
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<project version="4">
|
|
3
|
-
<component name="ProjectModuleManager">
|
|
4
|
-
<modules>
|
|
5
|
-
<module fileurl="file://$PROJECT_DIR$/.idea/did-web-resolver.iml" filepath="$PROJECT_DIR$/.idea/did-web-resolver.iml" />
|
|
6
|
-
</modules>
|
|
7
|
-
</component>
|
|
8
|
-
</project>
|
package/.idea/vcs.xml
DELETED
package/.travis.yml
DELETED
package/CHANGELOG.md
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# did-web-driver ChangeLog
|
|
2
|
-
|
|
3
|
-
## 1.0.1 - 2021-04-25
|
|
4
|
-
|
|
5
|
-
### Fixed
|
|
6
|
-
- Fix handling of hash fragments by `urlFromDid()`.
|
|
7
|
-
- Add logger to constructor.
|
|
8
|
-
|
|
9
|
-
## 1.0.0 - 2021-04-24
|
|
10
|
-
|
|
11
|
-
### Changed
|
|
12
|
-
- **BREAKING** Update to latest DID Core context
|
|
13
|
-
- **BREAKING** Update to use crypto-ld v5 API, latest crypto suites
|
|
14
|
-
- Add support for X25519KeyAgreementKey suite
|
|
15
|
-
|
|
16
|
-
## 0.2.0 - 2020-08-01
|
|
17
|
-
|
|
18
|
-
### Changed
|
|
19
|
-
- **BREAKING**: Update to use crypto-ld v4 API
|
|
20
|
-
|
|
21
|
-
## 0.0.1
|
|
22
|
-
|
|
23
|
-
### Added
|
|
24
|
-
- Initial implementation.
|
package/src/main.js
DELETED
package/test/karma.conf.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
module.exports = (config) => {
|
|
2
|
-
const bundler = process.env.BUNDLER || 'webpack'
|
|
3
|
-
const frameworks = ['mocha']
|
|
4
|
-
const files = ['**/*.spec.js']
|
|
5
|
-
const reporters = ['mocha']
|
|
6
|
-
const browsers = ['ChromeHeadless']
|
|
7
|
-
const client = {
|
|
8
|
-
mocha: {
|
|
9
|
-
timeout: 2000
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
// main bundle preprocessors
|
|
13
|
-
const preprocessors = []
|
|
14
|
-
preprocessors.push(bundler)
|
|
15
|
-
preprocessors.push('sourcemap')
|
|
16
|
-
|
|
17
|
-
return config.set({
|
|
18
|
-
frameworks,
|
|
19
|
-
files,
|
|
20
|
-
reporters,
|
|
21
|
-
basePath: '',
|
|
22
|
-
port: 9876,
|
|
23
|
-
colors: true,
|
|
24
|
-
browsers,
|
|
25
|
-
client,
|
|
26
|
-
singleRun: true,
|
|
27
|
-
preprocessors: {
|
|
28
|
-
'unit/*.js': preprocessors
|
|
29
|
-
},
|
|
30
|
-
webpack: {
|
|
31
|
-
devtool: 'inline-source-map',
|
|
32
|
-
mode: 'development',
|
|
33
|
-
node: {
|
|
34
|
-
Buffer: false,
|
|
35
|
-
crypto: false,
|
|
36
|
-
setImmediate: false
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
})
|
|
40
|
-
}
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import chai from 'chai'
|
|
2
|
-
import dirtyChai from 'dirty-chai'
|
|
3
|
-
|
|
4
|
-
import { DidWebResolver, urlFromDid, didFromUrl } from '../../src'
|
|
5
|
-
|
|
6
|
-
import { Ed25519VerificationKey2020 }
|
|
7
|
-
from '@digitalbazaar/ed25519-verification-key-2020'
|
|
8
|
-
import { X25519KeyAgreementKey2020 }
|
|
9
|
-
from '@digitalbazaar/x25519-key-agreement-key-2020'
|
|
10
|
-
import { CryptoLD } from 'crypto-ld'
|
|
11
|
-
chai.use(dirtyChai)
|
|
12
|
-
chai.should()
|
|
13
|
-
const { expect } = chai
|
|
14
|
-
|
|
15
|
-
const cryptoLd = new CryptoLD()
|
|
16
|
-
cryptoLd.use(Ed25519VerificationKey2020)
|
|
17
|
-
cryptoLd.use(X25519KeyAgreementKey2020)
|
|
18
|
-
|
|
19
|
-
describe('DidWebDriver', () => {
|
|
20
|
-
describe('constructor', () => {
|
|
21
|
-
it('should exist', () => {
|
|
22
|
-
expect(new DidWebResolver()).to.exist()
|
|
23
|
-
})
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
describe('generate()', () => {
|
|
27
|
-
let didWeb
|
|
28
|
-
|
|
29
|
-
beforeEach(async () => {
|
|
30
|
-
didWeb = new DidWebResolver({ cryptoLd })
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('should generate using default key map', async () => {
|
|
34
|
-
const url = 'https://example.com'
|
|
35
|
-
const { didDocument, keyPairs } = await didWeb.generate({ url })
|
|
36
|
-
|
|
37
|
-
expect(didDocument).to.have.property('@context')
|
|
38
|
-
expect(didDocument.id).to.equal('did:web:example.com')
|
|
39
|
-
expect(didDocument.capabilityInvocation[0].type)
|
|
40
|
-
.to.equal('Ed25519VerificationKey2020')
|
|
41
|
-
expect(didDocument.authentication[0].type)
|
|
42
|
-
.to.equal('Ed25519VerificationKey2020')
|
|
43
|
-
expect(didDocument.assertionMethod[0].type)
|
|
44
|
-
.to.equal('Ed25519VerificationKey2020')
|
|
45
|
-
expect(didDocument.capabilityDelegation[0].type)
|
|
46
|
-
.to.equal('Ed25519VerificationKey2020')
|
|
47
|
-
|
|
48
|
-
expect(keyPairs).to.exist()
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it('should return methodFor convenience function', async () => {
|
|
52
|
-
const url = 'https://example.com'
|
|
53
|
-
const { methodFor } = await didWeb.generate({ url })
|
|
54
|
-
|
|
55
|
-
const keyAgreementKey = methodFor({ purpose: 'keyAgreement' })
|
|
56
|
-
|
|
57
|
-
expect(keyAgreementKey).to.have.property('type', 'X25519KeyAgreementKey2020')
|
|
58
|
-
expect(keyAgreementKey).to.have.property('controller', 'did:web:example.com')
|
|
59
|
-
expect(keyAgreementKey).to.have.property('publicKeyMultibase')
|
|
60
|
-
expect(keyAgreementKey).to.have.property('privateKeyMultibase')
|
|
61
|
-
})
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
describe('urlFromDid()', () => {
|
|
65
|
-
it('should error on missing did', () => {
|
|
66
|
-
let error
|
|
67
|
-
try {
|
|
68
|
-
urlFromDid()
|
|
69
|
-
} catch (e) {
|
|
70
|
-
error = e
|
|
71
|
-
}
|
|
72
|
-
expect(error.message).to.equal('Cannot convert did to url, missing did.')
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it('should error on non-did:web dids', () => {
|
|
76
|
-
let error
|
|
77
|
-
try {
|
|
78
|
-
urlFromDid({ did: 'did:example:1234' })
|
|
79
|
-
} catch (e) {
|
|
80
|
-
error = e
|
|
81
|
-
}
|
|
82
|
-
expect(error.message)
|
|
83
|
-
.to.equal('DID Method not supported: "did:example:1234".')
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
it('should convert first id fragment to pathname plus default path', () => {
|
|
87
|
-
expect(urlFromDid({ did: 'did:web:example.com' }))
|
|
88
|
-
.to.equal('https://example.com/.well-known/did.json')
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
it('should url-decode host', () => {
|
|
92
|
-
expect(urlFromDid({ did: 'did:web:localhost%3A8080' }))
|
|
93
|
-
.to.equal('https://localhost:8080/.well-known/did.json')
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
it('should url-decode path fragments', () => {
|
|
97
|
-
expect(urlFromDid({ did: 'did:web:example.com:path:some%2Bsubpath' }))
|
|
98
|
-
.to.equal('https://example.com/path/some+subpath')
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
it('should preserve hash fragments for dids without paths', () => {
|
|
102
|
-
const url = urlFromDid({ did: 'did:web:localhost%3A8080#keyId' })
|
|
103
|
-
expect(url).to.equal('https://localhost:8080/.well-known/did.json#keyId')
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
it('should preserve hash fragments for dids with paths', () => {
|
|
107
|
-
const url = urlFromDid({ did: 'did:web:example.com:path:some%2Bsubpath#keyId' })
|
|
108
|
-
expect(url).to.equal('https://example.com/path/some+subpath#keyId')
|
|
109
|
-
})
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
describe('didFromUrl', () => {
|
|
113
|
-
it('should error on missing url', () => {
|
|
114
|
-
let error
|
|
115
|
-
try {
|
|
116
|
-
didFromUrl()
|
|
117
|
-
} catch (e) {
|
|
118
|
-
error = e
|
|
119
|
-
}
|
|
120
|
-
expect(error.message).to.equal('Cannot convert url to did, missing url.')
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
it('should error on http URLs', () => {
|
|
124
|
-
let error
|
|
125
|
-
try {
|
|
126
|
-
didFromUrl({ url: 'http://example.com' })
|
|
127
|
-
} catch (e) {
|
|
128
|
-
error = e
|
|
129
|
-
}
|
|
130
|
-
expect(error.message).to.equal('did:web does not support non-HTTPS URLs.')
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
it('should error on invalid URLs', () => {
|
|
134
|
-
let error
|
|
135
|
-
try {
|
|
136
|
-
didFromUrl({ url: 'non-url' })
|
|
137
|
-
} catch (e) {
|
|
138
|
-
error = e
|
|
139
|
-
}
|
|
140
|
-
expect(error.message).to.equal('Invalid url: "non-url".')
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
it('should convert host to did identifier', () => {
|
|
144
|
-
expect(didFromUrl({ url: 'https://localhost' }))
|
|
145
|
-
.to.equal('did:web:localhost')
|
|
146
|
-
expect(didFromUrl({ url: 'https://example.com' }))
|
|
147
|
-
.to.equal('did:web:example.com')
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
it('should url-encode host', () => {
|
|
151
|
-
expect(didFromUrl({ url: 'https://localhost:8080' }))
|
|
152
|
-
.to.equal('did:web:localhost%3A8080')
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
it('should leave off the default / path', () => {
|
|
156
|
-
expect(didFromUrl({ url: 'https://example.com/' }))
|
|
157
|
-
.to.equal('did:web:example.com')
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
it('should encode path / separators as :', () => {
|
|
161
|
-
expect(didFromUrl({ url: 'https://example.com/path/subpath/did.json' }))
|
|
162
|
-
.to.equal('did:web:example.com:path:subpath:did.json')
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
it('should drop the default /.well-known/did.json pathname', () => {
|
|
166
|
-
expect(didFromUrl({ url: 'https://example.com/.well-known/did.json' }))
|
|
167
|
-
.to.equal('did:web:example.com')
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
it('should url-encode path fragments', () => {
|
|
171
|
-
expect(didFromUrl({ url: 'https://example.com/path/some+subpath' }))
|
|
172
|
-
.to.equal('did:web:example.com:path:some%2Bsubpath')
|
|
173
|
-
})
|
|
174
|
-
})
|
|
175
|
-
})
|