Skip to content

Commit

Permalink
Add DID resolution types and rename UniversalResolver (#454)
Browse files Browse the repository at this point in the history
Signed-off-by: Frank Hinek <[email protected]>
  • Loading branch information
frankhinek authored Mar 21, 2024
1 parent 5f69982 commit f96e9c1
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 46 deletions.
3 changes: 2 additions & 1 deletion packages/dids/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './types/did-core.js';
export * from './types/did-resolution.js';
export type * from './types/multibase.js';
export type * from './types/portable-did.js';

Expand All @@ -13,8 +14,8 @@ export * from './methods/did-key.js';
export * from './methods/did-method.js';
export * from './methods/did-web.js';

export * from './resolver/did-resolver.js';
export * from './resolver/resolver-cache-level.js';
export * from './resolver/resolver-cache-noop.js';
export * from './resolver/universal-resolver.js';

export * as utils from './utils.js';
2 changes: 1 addition & 1 deletion packages/dids/src/methods/did-dht.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { BearerDid } from '../bearer-did.js';
import { extractDidFragment } from '../utils.js';
import { DidError, DidErrorCode } from '../did-error.js';
import { DidVerificationRelationship } from '../types/did-core.js';
import { EMPTY_DID_RESOLUTION_RESULT } from '../resolver/did-resolver.js';
import { EMPTY_DID_RESOLUTION_RESULT } from '../types/did-resolution.js';

/**
* Represents a BEP44 message, which is used for storing and retrieving data in the Mainline DHT
Expand Down
2 changes: 1 addition & 1 deletion packages/dids/src/methods/did-ion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { BearerDid } from '../bearer-did.js';
import { DidMethod } from '../methods/did-method.js';
import { DidError, DidErrorCode } from '../did-error.js';
import { getVerificationRelationshipsById } from '../utils.js';
import { EMPTY_DID_RESOLUTION_RESULT } from '../resolver/did-resolver.js';
import { EMPTY_DID_RESOLUTION_RESULT } from '../types/did-resolution.js';

/**
* Options for creating a Decentralized Identifier (DID) using the DID ION method.
Expand Down
2 changes: 1 addition & 1 deletion packages/dids/src/methods/did-jwk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { Did } from '../did.js';
import { DidMethod } from './did-method.js';
import { BearerDid } from '../bearer-did.js';
import { DidError, DidErrorCode } from '../did-error.js';
import { EMPTY_DID_RESOLUTION_RESULT } from '../resolver/did-resolver.js';
import { EMPTY_DID_RESOLUTION_RESULT } from '../types/did-resolution.js';

/**
* Defines the set of options available when creating a new Decentralized Identifier (DID) with the
Expand Down
2 changes: 1 addition & 1 deletion packages/dids/src/methods/did-key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { DidMethod } from './did-method.js';
import { BearerDid } from '../bearer-did.js';
import { DidError, DidErrorCode } from '../did-error.js';
import { KeyWithMulticodec } from '../types/multibase.js';
import { EMPTY_DID_RESOLUTION_RESULT } from '../resolver/did-resolver.js';
import { EMPTY_DID_RESOLUTION_RESULT } from '../types/did-resolution.js';
import { getVerificationMethodTypes, keyBytesToMultibaseId, multibaseIdToKeyBytes } from '../utils.js';

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/dids/src/methods/did-web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { DidDocument, DidResolutionOptions, DidResolutionResult } from '../

import { Did } from '../did.js';
import { DidMethod } from './did-method.js';
import { EMPTY_DID_RESOLUTION_RESULT } from '../resolver/did-resolver.js';
import { EMPTY_DID_RESOLUTION_RESULT } from '../types/did-resolution.js';

/**
* The `DidWeb` class provides an implementation of the `did:web` DID method.
Expand Down
2 changes: 1 addition & 1 deletion packages/dids/src/resolver/resolver-cache-level.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import type { AbstractLevel } from 'abstract-level';
import ms from 'ms';
import { Level } from 'level';

import type { DidResolverCache } from './did-resolver.js';
import type { DidResolutionResult } from '../types/did-core.js';
import type { DidResolverCache } from '../types/did-resolution.js';

/**
* Configuration parameters for creating a LevelDB-based cache for DID resolution results.
Expand Down
2 changes: 1 addition & 1 deletion packages/dids/src/resolver/resolver-cache-noop.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { DidResolverCache } from './did-resolver.js';
import type { DidResolutionResult } from '../types/did-core.js';
import type { DidResolverCache } from '../types/did-resolution.js';

/**
* No-op cache that is used as the default cache for did-resolver.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,22 @@
import type { KeyValueStore } from '@web5/common';

import type { DidMethodResolver } from '../methods/did-method.js';
import type { DidResolver, DidResolverCache, DidUrlDereferencer } from '../types/did-resolution.js';
import type { DidDereferencingOptions, DidDereferencingResult, DidResolutionOptions, DidResolutionResult, DidResource } from '../types/did-core.js';

import { Did } from '../did.js';
import { DidErrorCode } from '../did-error.js';
import { DidResolverCacheNoop } from './resolver-cache-noop.js';
import { EMPTY_DID_RESOLUTION_RESULT } from '../types/did-resolution.js';

/**
* Interface for cache implementations used by {@link DidResolver} to store resolved DID documents.
*/
export interface DidResolverCache extends KeyValueStore<string, DidResolutionResult | void> {}

/**
* Parameters for configuring the `DidResolver` class, which is responsible for resolving
* Parameters for configuring the `UniversalResolver` class, which is responsible for resolving
* decentralized identifiers (DIDs) to their corresponding DID documents.
*
* This type specifies the essential components required by the `DidResolver` to perform
* This type specifies the essential components required by the `UniversalResolver` to perform
* DID resolution and dereferencing. It includes an array of `DidMethodResolver` instances,
* each capable of resolving DIDs for a specific method, and optionally, a cache for storing
* resolved DID documents to improve resolution efficiency.
*/
export type DidResolverParams = {
export type UniversalResolverParams = {
/**
* An array of `DidMethodResolver` instances.
*
Expand All @@ -40,18 +35,6 @@ export type DidResolverParams = {
cache?: DidResolverCache;
}

/**
* A constant representing an empty DID Resolution Result. This object is used as the basis for a
* result of DID resolution and is typically augmented with additional properties by the
* DID method resolver.
*/
export const EMPTY_DID_RESOLUTION_RESULT: DidResolutionResult = {
'@context' : 'https://w3id.org/did-resolution/v1',
didResolutionMetadata : {},
didDocument : null,
didDocumentMetadata : {},
};

/**
* The `DidResolver` class provides mechanisms for resolving Decentralized Identifiers (DIDs) to
* their corresponding DID documents.
Expand Down Expand Up @@ -79,7 +62,7 @@ export const EMPTY_DID_RESOLUTION_RESULT: DidResolutionResult = {
* const dereferenceResult = await resolver.dereference({ didUri: 'did:example:123456#key-1' });
* ```
*/
export class DidResolver {
export class UniversalResolver implements DidResolver, DidUrlDereferencer {
/**
* A cache for storing resolved DID documents.
*/
Expand All @@ -95,7 +78,7 @@ export class DidResolver {
*
* @param params - The parameters for constructing the `DidResolver`.
*/
constructor({ cache, didResolvers }: DidResolverParams) {
constructor({ cache, didResolvers }: UniversalResolverParams) {
this.cache = cache || DidResolverCacheNoop;

for (const resolver of didResolvers) {
Expand Down Expand Up @@ -153,7 +136,6 @@ export class DidResolver {
/**
* Dereferences a DID (Decentralized Identifier) URL to a corresponding DID resource.
*
*
* This method interprets the DID URL's components, which include the DID method, method-specific
* identifier, path, query, and fragment, and retrieves the related resource as per the DID Core
* specifications.
Expand Down
93 changes: 93 additions & 0 deletions packages/dids/src/types/did-resolution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import type { KeyValueStore } from '@web5/common';

import type { DidDereferencingOptions, DidDereferencingResult, DidResolutionOptions, DidResolutionResult } from './did-core.js';

/**
* Represents the interface for resolving a Decentralized Identifier (DID) to its corresponding DID
* document.
*
* The `DidResolver` interface defines a single method, `resolve`, which takes a DID URL as input
* and returns a `Promise` that resolves to a `DidResolutionResult`. This result contains the DID
* document associated with the given DID, along with metadata about the resolution process.
*
* Implementations of this interface are expected to support resolution of DIDs according to the
* specific rules and methods defined by the DID scheme in use.
*
* More information on DID URL dereferencing can be found in the
* {@link https://www.w3.org/TR/did-core/#did-resolution | DID Core specification}.
*
* @example
* ```typescript
* const resolutionResult = await didResolver.resolve('did:example:123456789abcdefghi');
* ```
*/
export interface DidResolver {
/**
* Resolves a DID URI to a DID document and associated metadata.
*
* This function should resolve the DID URI in accordance with the relevant DID method
* specification, using the provided `options`.
*
* @param didUri - The DID URI to be resolved.
* @param options - Optional. The options used for resolving the DID.
* @returns A {@link DidResolutionResult} object containing the DID document and metadata or an
* error.
*/
resolve(didUrl: string, options?: DidResolutionOptions): Promise<DidResolutionResult>;
}

/**
* Interface for cache implementations used by to store resolved DID documents.
*/
export interface DidResolverCache extends KeyValueStore<string, DidResolutionResult | void> {}

/**
* Represents the interface for dereferencing a DID URL to a specific resource within a DID
* document.
*
* The `DidUrlDereferencer` interface defines a single method, `dereference`, which takes a DID URL
* as input and returns a `Promise` that resolves to a `DidDereferencingResult`. This result
* includes the dereferenced resource (if found) and metadata about the dereferencing process.
*
* Dereferencing a DID URL involves parsing the URL to identify the specific part of the DID
* document being referenced, which could be a verification method, a service endpoint, or the
* entire document itself.
*
* Implementations of this interface must adhere to the dereferencing mechanisms defined in the DID
* Core specifications, handling various components of the DID URL including the DID itself, path,
* query, and fragment.
*
* More information on DID URL dereferencing can be found in the
* {@link https://www.w3.org/TR/did-core/#did-url-dereferencing | DID Core specification}.
*
* @example
* ```typescript
* const dereferenceResult = await didUrlDereferencer.dereference('did:example:123456789abcdefghi#keys-1');
* ```
*/
export interface DidUrlDereferencer {
/**
* Dereferences a DID (Decentralized Identifier) URL to a corresponding DID resource.
*
* This method interprets the DID URL's components, which include the DID method, method-specific
* identifier, path, query, and fragment, and retrieves the related resource as per the DID Core
* specifications.
*
* @param didUrl - The DID URL string to dereference.
* @param options - Input options to the dereference function. Optional.
* @returns a {@link DidDereferencingResult}
*/
dereference(didUrl: string, options?: DidDereferencingOptions): Promise<DidDereferencingResult>;
}

/**
* A constant representing an empty DID Resolution Result. This object is used as the basis for a
* result of DID resolution and is typically augmented with additional properties by the
* DID method resolver.
*/
export const EMPTY_DID_RESOLUTION_RESULT: DidResolutionResult = {
'@context' : 'https://w3id.org/did-resolution/v1',
didResolutionMetadata : {},
didDocument : null,
didDocumentMetadata : {},
};
10 changes: 6 additions & 4 deletions packages/dids/tests/resolver/resolver-cache-level.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import sinon from 'sinon';
import { expect } from 'chai';

import { Level } from 'level';

import type { DidResolver, DidResolverCache } from '../../src/types/did-resolution.js';

import { DidJwk } from '../../src/methods/did-jwk.js';
import { DidResolver, DidResolverCache } from '../../src/resolver/did-resolver.js';
import { UniversalResolver } from '../../src/resolver/universal-resolver.js';
import { DidResolverCacheLevel } from '../../src/resolver/resolver-cache-level.js';

describe('DidResolverCacheLevel', () => {
Expand Down Expand Up @@ -179,7 +181,7 @@ describe('DidResolverCacheLevel', () => {
});
});

describe('with DidResolver', () => {
describe('with UniversalResolver', () => {
let cache: DidResolverCache;
let didResolver: DidResolver;

Expand All @@ -190,7 +192,7 @@ describe('DidResolverCacheLevel', () => {
beforeEach(async () => {
await cache.clear();
const didMethodApis = [DidJwk];
didResolver = new DidResolver({ cache, didResolvers: didMethodApis });
didResolver = new UniversalResolver({ cache, didResolvers: didMethodApis });
});

after(async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@ import type { UnwrapPromise } from '@web5/common';
import { expect } from 'chai';
import * as sinon from 'sinon';

import type { DidResource } from '../../src/types/did-core.js';

import { DidJwk } from '../../src/methods/did-jwk.js';
import { DidResource } from '../../src/types/did-core.js';
import { isDidVerificationMethod } from '../../src/utils.js';
import { DidResolver } from '../../src/resolver/did-resolver.js';
import { UniversalResolver } from '../../src/resolver/universal-resolver.js';
import DidJwkResolveTestVector from '../../../../web5-spec/test-vectors/did_jwk/resolve.json' assert { type: 'json' };

describe('DidResolver', () => {
describe('UniversalResolver', () => {
describe('resolve()', () => {
let didResolver: DidResolver;
let didResolver: UniversalResolver;

beforeEach(() => {
const didMethodApis = [DidJwk];
didResolver = new DidResolver({ didResolvers: didMethodApis });
didResolver = new UniversalResolver({ didResolvers: didMethodApis });
});

it('returns an invalidDid error if the DID cannot be parsed', async () => {
Expand Down Expand Up @@ -55,11 +56,11 @@ describe('DidResolver', () => {
});

describe('dereference()', () => {
let didResolver: DidResolver;
let didResolver: UniversalResolver;

beforeEach(() => {
const didMethodApis = [DidJwk];
didResolver = new DidResolver({ didResolvers: didMethodApis });
didResolver = new UniversalResolver({ didResolvers: didMethodApis });
});

it('returns a result with contentStream set to null and dereferenceMetadata.error set to invalidDidUrl, if the DID URL is invalid', async () => {
Expand Down Expand Up @@ -88,8 +89,8 @@ describe('DidResolver', () => {
});

it('returns a DID service resource as the value of contentStream if found', async () => {
// Create an instance of DidResolver
const resolver = new DidResolver({ didResolvers: [] });
// Create an instance of UniversalResolver
const resolver = new UniversalResolver({ didResolvers: [] });

// Stub the resolve method
const mockDidResolutionResult = {
Expand Down

0 comments on commit f96e9c1

Please sign in to comment.