Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update did key v7 #3

Open
wants to merge 55 commits into
base: initial
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
74f3911
Start work on did & did:key errors & validation.
aljones15 Jul 29, 2022
e2b3a32
Add DidResolutionResult class and use it.
aljones15 Jul 29, 2022
c28c251
Use more except didKeyComponents over didComponents.
aljones15 Jul 29, 2022
38fde21
Correct mistake Number.parseInt not Number.parse.
aljones15 Jul 29, 2022
1299da9
Update lint stuff.
aljones15 Jul 29, 2022
34fc90d
Up engine to 16 & improve error reporting code.
aljones15 Jul 29, 2022
a17e0ea
Remove use strict & correct invalid jsdoc param.
aljones15 Jul 30, 2022
e26070b
Use bnid for public key length errors.
aljones15 Aug 1, 2022
d0b1f22
Use @bedrock/did-io latest.
aljones15 Aug 1, 2022
29b57ec
Convert to es6 module.
aljones15 Aug 1, 2022
992b590
Add MULTICODEC ed25519 header and get bytes.
aljones15 Aug 1, 2022
6dc9f64
Add check for invalidDidUrl.
aljones15 Aug 2, 2022
510f909
Add better tests for isDiDUrl & component split.
aljones15 Aug 2, 2022
963ab56
Get identifier & isDidUrl.
aljones15 Aug 2, 2022
6248cb6
Better separation of did url and identifier logic.
aljones15 Aug 2, 2022
bea2e99
Separate validators better.
aljones15 Aug 3, 2022
0a2e89f
Implement 100% success with latest did:key spec.
aljones15 Aug 3, 2022
38bc2cf
Update github actions node versions to 14 and 16 + .npmrc for legacy …
aljones15 Aug 3, 2022
98b2212
Add .npmrc to test project.
aljones15 Aug 3, 2022
cb0c52e
Upgrade github workflows to mongodb 4.4 add more install steps.
aljones15 Aug 3, 2022
ed6989f
Add statusCodes to errors.
aljones15 Aug 3, 2022
e2299ea
Add basic tests to test project.
aljones15 Aug 3, 2022
ad39610
Use c8 for coverage.
aljones15 Aug 3, 2022
7e69c10
Fully capitalize JSON-LD.
aljones15 Aug 3, 2022
c40562c
Capitalize DID in error message.
aljones15 Aug 3, 2022
629b79a
Separate publicKey into public key.
aljones15 Aug 3, 2022
ef6af7a
Expect whole thing and not part of a string.
aljones15 Aug 3, 2022
177f622
Remove validators moved to did-io.
aljones15 Aug 5, 2022
bc44d0b
Remove did key related validators.
aljones15 Aug 5, 2022
02c977c
Remove lib/didComponents, didErrors, & bnid.
aljones15 Aug 8, 2022
ae77629
Add @db/did-io to peerDeps & isDidUrl & parseDid.
aljones15 Aug 8, 2022
a47725f
Remove didKey and didResolution error files in favor of DidResolution…
aljones15 Aug 8, 2022
6aea527
Add function to turn error codes into statusCodes.
aljones15 Aug 8, 2022
23e0f63
Remove validators dir & make it a file.
aljones15 Aug 8, 2022
3364e4a
Switch route to /1.0/identifiers/resolve/:did.
aljones15 Aug 8, 2022
2c1c0d5
Improve naming of errorCodeToStatusCode & hasDereferencingMetadata.
aljones15 Aug 9, 2022
99ca721
Move metadata detection into DidResolutionResult.
aljones15 Aug 9, 2022
a05ff0e
Remove request options & move to did-method-key.
aljones15 Aug 9, 2022
d9d4794
Add formatResponse to handle requests that don't want metadata.
aljones15 Aug 9, 2022
db32c3a
Correct test makeRequest basePath.
aljones15 Aug 9, 2022
40eed63
Sort status codes ascending & add 501 for methodNotSupported.
aljones15 Aug 10, 2022
16d594f
Remove experimental from supportedMethods.
aljones15 Aug 10, 2022
8b06c20
Make supported methods a Set.
aljones15 Aug 10, 2022
d88e2de
Remove logic for supportedMethods.
aljones15 Aug 11, 2022
81a8d70
Use node 16-18 in ci actions.
aljones15 Aug 12, 2022
337f2ff
Remove unneeded line from https.js.
aljones15 Aug 15, 2022
4f16b24
Remove 'use strict' from logger.js.
aljones15 Aug 15, 2022
b25d609
Remove extra line in helpers.js.
aljones15 Aug 15, 2022
297950b
Remove 'use strict' from test.config.js.
aljones15 Aug 15, 2022
06560a3
Remove extra line from test.js.
aljones15 Aug 15, 2022
e83a8ce
Better document did and resolver options.
aljones15 Aug 15, 2022
5e918ab
Add @db/http-client to test project.
aljones15 Aug 15, 2022
c4ff86b
Change nyc opts to c8 opts.
aljones15 Aug 23, 2022
cae41cc
Remove assertNoError from lint test globals.
aljones15 Aug 23, 2022
809aaa3
Correct typo in jsdoc string for didOptions.
aljones15 Dec 29, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
env: {
node: true
},
extends: [
'eslint-config-digitalbazaar',
'eslint-config-digitalbazaar/jsdoc',
'eslint-config-digitalbazaar/module',
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module'
}
};
7 changes: 0 additions & 7 deletions .eslintrc.js

This file was deleted.

8 changes: 5 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,20 @@ jobs:
timeout-minutes: 10
services:
mongodb:
image: mongo:4.2
image: mongo:4.4
ports:
- 27017:27017
strategy:
matrix:
node-version: [12.x, 14.x]
node-version: [14.x, 16.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: |
npm install
cd test
npm install
- name: Run test with Node.js ${{ matrix.node-version }}
Expand All @@ -49,7 +50,7 @@ jobs:
timeout-minutes: 10
services:
mongodb:
image: mongo:4.2
image: mongo:4.4
ports:
- 27017:27017
strategy:
Expand All @@ -62,6 +63,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- run: |
npm install
cd test
npm install
- name: Generate coverage report
Expand Down
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
legacy-peer-deps=true
35 changes: 35 additions & 0 deletions lib/DidResolutionResult.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*!
* Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved.
*/

/**
* Creates the json-ld for the response.
*
* @param {object} options - Options to use.
* @param {boolean} [options.isDidUrl=false] - Does the response need
* didDereferencingMetadata?
* @param {object} [options.didDocument={}] - The didDocument for the
* response.
* @param {object|null} [options.didDocumentMetadata=null] - Meta data
* for the didDocument.
* @param {Error} options.error - An error if any was raised.
*/
export class DidResolutionResult {
constructor({
isDidUrl = false,
didDocument = {},
didDocumentMetadata = null,
error
}) {
this['@context'] = 'https://w3id.org/did-resolution/v1';
this.didDocument = didDocument;
this.didDocumentMetadata = didDocumentMetadata;
const metadataProperty = isDidUrl ?
'didDereferencingMetadata' : 'didResolutionMetadata';
this[metadataProperty] = {};
// only define error if it was passed in
if(error) {
this[metadataProperty].error = error.code || 'internalError';
}
}
}
15 changes: 11 additions & 4 deletions lib/config.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
/*!
* Copyright (c) 2021 Digital Bazaar, Inc. All rights reserved.
*/
import bedrock from 'bedrock';
const {config} = bedrock;
import {config} from '@bedrock/core';

const namespace = 'bedrock-did-resolver-http';
const cfg = config[namespace] = {};

// support did:key and veres one keys by default
cfg.supportedMethods = ['key', 'v1'];
// a list of which did:methods we supported
// experimental is for tests only
cfg.supportedMethods = ['key', 'v1', 'experimental'];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cfg.supportedMethods = ['key', 'v1', 'experimental'];
cfg.supportedMethods = ['key', 'v1', 'experimental'];

We definitely don't want the experimental DID Method in any of our resolution code -- this is going to go into production and could become an attack vector. Why do we need this here?

Copy link
Contributor Author

@aljones15 aljones15 Aug 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a method that is not key for testing the did:key statement about the method always being key. We could use v1, but this actually shows a larger issue, right now passing in a did:v1 will result in this implementation throwing as it only allows did:key.

// FIXME this can be expanded to support Multikey or JsonWebKey2020
cfg.publicKeyFormats = [
'Ed25519VerificationKey2020',
'X25519KeyAgreementKey2020',
// this is strictly for testing
'ExperimentalVerificationKey2022'
];

const basePath = '/1.0/resolve/identifiers/:did';
cfg.routes = {basePath};
79 changes: 79 additions & 0 deletions lib/didComponents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*!
* Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved.
*/

/**
* Using a colon (:) as the delimiter, split the identifier
* into its components: a scheme, a method, a version, and a
* multibaseValue. If there are only three components set the
* version to the string value 1 and use the last value as
* the multibaseValue.
*
* @param {object} options - Options to use.
* @param {string} options.did - A did as a string.
*
* @returns {object} The components of the did.
*/
export const getDidKeyComponents = ({did}) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Abstraction here feels wrong... getting method-specific things should probably be placed in method-specific libraries.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally agree here, this should probably be in did-method-key-js. I made an issue to start discussion on this: digitalbazaar/did-method-key#46

There are a lot of validation stuff included here that should be in did-method-key-js ed25519-verification-key-2020 and possibly x25519.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Marking this as resolved as getDidKeyComponents is no longer in this library and the way we parse did keys has changed. See digitalbazaar/did-method-key#47 for how did keys are parsed now.

const {
isDidUrl,
didEndIndex,
fragment,
search,
searchParams
} = _getUrlComponents({did});
// the identifier should be the did before the url syntax starts
const identifier = isDidUrl ? did.substring(0, didEndIndex) : did;
// split on : unless : is the last character in a did url /(?!:$):/
const parts = identifier.split(/(?!:$):/);
const [scheme, method, version, multibase] = parts;
return {
scheme,
method,
// if multibase exists use the version
version: multibase ? version : '1',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like it's wrong, you can have version AND multibase exist at the same time. If the code block is right, it feels confusing. Didn't pass the code smell test... something seems off here.

Copy link
Contributor Author

@aljones15 aljones15 Aug 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is exactly what this accounts for did:key:3:zf00 will result in multibase and version being defined at the same time. If multibase is not defined then the string 1 is used as the version. p.s. this results from using the algorithm from the spec that states to separate the components of the identifier using :.

Copy link
Member

@dlongley dlongley Aug 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another problem that needs accounting for here is if someone uses did:key:1:... (version 1 the wrong way) ... that needs to be an error, I'd think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that actually should not error as the version number is a positive integer unless I'm not understanding this correctly.

// multibase maybe undefined
// if multibase exists use multibase
multibase: multibase || version,
parts,
did,
identifier,
// contains the fragment to dereference
fragment,
// contains the search string
search,
// contains a map with key value pairs
searchParams,
isDidUrl
};
};

/**
* If the did is a did url it gets the fragment and searchParams.
*
* @private
*
* @param {object} options - Options to use.
* @param {string} options.did - A potential did url.
*
* @returns {object} The resulting url components.
*/
function _getUrlComponents({did}) {
// if there is a fragment `#` or a service `?service` or it contains /
// then we are dereferencing a did url
const urlParts = /[\/#?]/;
const urlMatch = urlParts.exec(did);
if(urlMatch) {
const urlParts = new URL(did);
return {
isDidUrl: true,
fragment: urlParts.hash,
search: urlParts.search,
searchParams: urlParts.searchParams,
didEndIndex: urlMatch.index
};
}
return {
isDidUrl: false
};
}
61 changes: 61 additions & 0 deletions lib/didResolver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*!
* Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved.
*/
import {
validateDidRequest,
validateRequestOptions
} from './validators/index.js';
import {didIo} from '@bedrock/did-io';
import {DidResolutionResult} from './DidResolutionResult.js';
import {getDidKeyComponents} from './didComponents.js';
import logger from './logger.js';

/**
* Resolves a did after validating it.
*
* @param {object} options - Options to use.
* @param {string} options.did - The did or didUrl being resolved.
* @param {Array<string>} options.supportedMethods - A list of
* did methods this resolver supports.
* @param {Array<string>} options.publicKeyFormats - A list of
* public key formats this resolver supports.
* @param {object} options.didOptions - Options passed to the resolver.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the description be more clear? To me the description for didOptions sounds the same as description for resolverOptions. How are the two different?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an excellent point and I made changes here: e83a8ce

*
* @returns {object} - Returns a status code and the resolution data.
*/
export const resolveDid = async ({
did,
supportedMethods,
publicKeyFormats,
didOptions
}) => {
let isDidUrl = false;
let error;
try {
const didKeyComponents = getDidKeyComponents({did});
isDidUrl = didKeyComponents.isDidUrl;
await validateDidRequest({didKeyComponents, supportedMethods, didOptions});
const didDocument = await didIo.get({did});
// FIXME didOptions should probably be in didIo
// in the future
validateRequestOptions({
did,
didDocument,
didOptions,
publicKeyFormats
});
return {
statusCode: 200,
resolutionResult: new DidResolutionResult({didDocument, isDidUrl})
};
} catch(e) {
logger.error(`did resolution error: ${did}`, {error: e});
error = e;
}
if(error) {
return {
statusCode: error.statusCode || 500,
resolutionResult: new DidResolutionResult({error, isDidUrl})
};
}
};
70 changes: 23 additions & 47 deletions lib/http.js
Original file line number Diff line number Diff line change
@@ -1,61 +1,37 @@
/*!
* Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved.
*/
import {asyncHandler} from 'bedrock-express';
import bedrock from 'bedrock';
import {didIo} from 'bedrock-did-io';
import logger from './logger';

import * as bedrock from '@bedrock/core';
import {asyncHandler} from '@bedrock/express';
import logger from './logger.js';
import {resolveDid} from './didResolver.js';
const {config} = bedrock;

bedrock.events.on('bedrock-express.configure.routes', app => {
const {routes, supportedMethods} = config['bedrock-did-resolver-http'];
const {
routes,
supportedMethods,
publicKeyFormats
} = config['bedrock-did-resolver-http'];
app.get(
routes.basePath,
asyncHandler(async (req, res) => {
// this can be resolution or dereferencing meta data
const metaData = {};
const resolutionResult = {
'@context': 'https://w3id.org/did-resolution/v1',
didDocument: {},
didDocumentMetadata: null,
didResolutionMetadata: metaData
};
const {did} = req.params;
const parsedDid = new URL(did);
// if there is a fragment `#` or a service `?service`
// then we are dereferencing a did url
const didUrl = (parsedDid.hash || parsedDid.search);
if(didUrl) {
// add a did dereferencing meta
resolutionResult.didDereferencingMetadata = metaData;
// delete the resolution metadata
delete resolutionResult.didResolutionMetadata;
}
// the second value should always be the method
const [prefix, method, id] = did.split(':');
// a did must have a did prefix, method, and id
if(!((prefix === 'did') && method && id)) {
metaData.error = didUrl ? 'invalidDidUrl' : 'invalidDid';
return res.status(400).json(resolutionResult);
}
if(!supportedMethods.includes(method)) {
//FIXME this might not be the right error code
metaData.error = 'representationNotSupported';
return res.status(406).json(resolutionResult);
}
// did options are passed to the resolver via url queries
// except some are headers on the request
const didOptions = {...req.headers, ...req.query};
try {
resolutionResult.didDocument = await didIo.get({did});
} catch(e) {
//FIXME the did resolver error could contain invalidDid, notFound,
//or representationNotSupported we need to check for those errors
//and newer errors and add that information in the future
logger.error('DID Resolution error', {error: e});
// the spec doesn't seem to handle what occurs if the
// did resolver fails for reasons unrelated to the did such
// as database timeouts.
metaData.error = 'InternalError';
return res.status(500).json(resolutionResult);
const {statusCode, resolutionResult} = await resolveDid({
did,
supportedMethods,
publicKeyFormats,
didOptions
});
res.status(statusCode).json(resolutionResult);
} catch(error) {
logger.error(`Did resolution error ${did}`, {error});
throw error;
}
res.json(resolutionResult);
}));
});
9 changes: 4 additions & 5 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/*!
* Copyright (c) 2021 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2021-2022 Digital Bazaar, Inc. All rights reserved.
*/
'use strict';
import '@bedrock/express';

// translate `main.js` to CommonJS
require = require('esm')(module);
module.exports = require('./main.js');
import './config.js';
import './http.js';
3 changes: 1 addition & 2 deletions lib/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
/*!
* Copyright (c) 2022 Digital Bazaar, Inc. All rights reserved.
*/
'use strict';

import bedrock from 'bedrock';
import * as bedrock from '@bedrock/core';

const logger = bedrock.loggers.get('app').child('bedrock-did-resolver-http');

Expand Down
7 changes: 0 additions & 7 deletions lib/main.js

This file was deleted.

Loading