A W3C-conformant DID Resolution server implementing the HTTPS Binding.
Digital Bazaar's independent implementation for W3C standardization — providing the second conforming implementation required for the spec to advance to a global standard.
A DID (Decentralized Identifier) is a URI like did:key:z6Mk... or
did:web:example.com. Resolving a DID means fetching the DID Document — a JSON-LD
object that describes the entity: its public keys, authentication methods, and service
endpoints.
This server exposes a single HTTP endpoint that accepts a DID (or DID URL), resolves
it to a DID Document using @digitalbazaar/did-io, and
returns the result in the format the client requests.
Client did-resolver did-io
| | |
|-- GET /did:key:z6Mk... ------>| |
| |-- resolver.get({did}) ------->|
| | |-- driver lookup
| | |-- fetch/derive doc
| |<-- DID Document --------------|
|<-- 200 application/did+ld+json|
GET /1.0/identifiers/{did}
POST /1.0/identifiers/{did}
GET— resolution options passed as query parametersPOST— resolution options passed as a JSON body
Response formats (controlled by Accept header):
| Accept Header | Response |
|---|---|
application/did+ld+json |
DID Document only |
application/did-resolution |
Full result: document + resolution metadata + document metadata |
| (default) | DID Document only |
GET /1.0/identifiers/{did-url}
A DID URL extends a DID with a path, query, or fragment:
did:key:z6Mk...#key-1→ returns a specific verification methoddid:web:example.com?service=files→ HTTP 303 redirect to the service endpoint URLdid:web:example.com?service=files&relativeRef=/path→ redirect with path appended
Response formats (controlled by Accept header):
| Accept Header | Response |
|---|---|
application/did-url-dereferencing |
Full result: content + dereferencing metadata |
text/uri-list |
HTTP 303 redirect to the resource URL |
| (default) | The dereferenced resource directly |
Per the DID Resolution spec:
| Condition | Status |
|---|---|
| Success | 200 OK |
| Invalid DID syntax | 400 Bad Request |
| DID not found | 404 Not Found |
Unsupported Accept type |
406 Not Acceptable |
| DID deactivated | 410 Gone |
| Internal resolver error | 500 Internal Server Error |
| DID method not supported | 501 Not Implemented |
When Accept: application/did-resolution is requested, error responses are
conformant resolution results with an RFC 9457-style error object in
didResolutionMetadata:
{
"@context": "https://w3id.org/did-resolution/v1",
"didDocument": null,
"didDocumentMetadata": {},
"didResolutionMetadata": {
"error": {
"type": "https://www.w3.org/ns/did#METHOD_NOT_SUPPORTED"
}
}
}Error type URIs follow the W3C DID namespace:
https://www.w3.org/ns/did#INVALID_DID, #NOT_FOUND, #METHOD_NOT_SUPPORTED,
#REPRESENTATION_NOT_SUPPORTED, #INTERNAL_ERROR, etc.
If a resolved DID document contains "deactivated": true, the server returns
410 Gone with a null didDocument and "deactivated": true in
didDocumentMetadata. Neither did:key nor did:web support deactivation
(both are static/derived methods with no registry). Ledger-based methods such
as did:veres-one or did:ion do — register such a driver to exercise this
path.
| Method | Description |
|---|---|
did:key |
Self-certifying DID derived from a public key |
did:web |
DID Document hosted at a well-known HTTPS URL |
Additional methods can be added by registering a driver with the CachedResolver — see
Adding a DID Method.
lib/
├── index.js # Entry point — wires server + resolver
├── server.js # HTTP server, route registration
├── resolver.js # did-io CachedResolver instance
├── routes/
│ ├── resolve.js # GET/POST resolution handler
│ └── dereference.js# DID URL dereferencing handler
├── drivers/
│ ├── key.js # did:key driver
│ └── web.js # did:web driver
└── http/
├── errors.js # DID error → HTTP status code mapping
└── headers.js # Content-Type helpers
The server uses Express for routing and the DB-standard ESM module format throughout.
Resolution flow:
- Request arrives at
GET /1.0/identifiers/{did} - Route handler extracts the DID and resolution options
resolver.jscallsdid-ioCachedResolver.get({did})- The appropriate driver (key, web, etc.) fetches or derives the DID Document
- The handler formats the response per the
Acceptheader - Status code is set based on result or error type
npm install# Start the server (default port 8080)
node lib/index.js
# Resolve a DID
curl http://localhost:8080/1.0/identifiers/did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH
# Get full resolution result
curl -H "Accept: application/did-resolution" \
http://localhost:8080/1.0/identifiers/did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH
# Dereference a DID URL fragment (specific verification method)
curl http://localhost:8080/1.0/identifiers/did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH%23z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH
# Dereference a service endpoint (HTTP 303 redirect)
curl -L -H "Accept: text/uri-list" \
http://localhost:8080/1.0/identifiers/did:web:example.com%3Fservice%3Dfiles| Environment Variable | Default | Description |
|---|---|---|
PORT |
8080 |
Port to listen on |
HOST |
0.0.0.0 |
Host to bind to |
-
Install the driver package:
npm install @digitalbazaar/did-method-example
-
Create
lib/drivers/example.js:import {driver} from '@digitalbazaar/did-method-example'; export const exampleDriver = driver();
If the method uses multikey cryptography, also register the key suite:
import {Ed25519VerificationKey2020} from '@digitalbazaar/ed25519-verification-key-2020'; exampleDriver.use({ multibaseMultikeyHeader: 'z6Mk', fromMultibase: Ed25519VerificationKey2020.from });
-
Register it in
lib/resolver.js:import {exampleDriver} from './drivers/example.js'; resolver.use(exampleDriver);
That's it.
npm test # Run test suite
npm run lint # Lint with @digitalbazaar/eslint-configAll 35 tests in the w3c-ccg/did-resolution-mocha-test-suite pass against this implementation.
| Requirement | Status |
|---|---|
GET /1.0/identifiers/{did} binding |
✅ |
POST /1.0/identifiers/{did} binding |
✅ |
Resolution result shape (didDocument, didResolutionMetadata, didDocumentMetadata) |
✅ |
didResolutionMetadata.contentType present on success |
✅ |
Content-Type header matches didResolutionMetadata.contentType |
✅ |
| RFC 9457 error objects with W3C DID namespace URIs | ✅ |
INVALID_DID + 400 for malformed DID input |
✅ |
NOT_FOUND + 404 |
✅ |
METHOD_NOT_SUPPORTED + 501 |
✅ |
REPRESENTATION_NOT_SUPPORTED + 406 |
✅ |
| Deactivated DID → 410 + null document | ✅ (requires a method that supports deactivation) |
303 redirect with empty body for text/uri-list |
✅ |
| DID URL dereferencing result shape | ✅ |
- W3C DID Resolution
- HTTPS Binding
- DID Resolution Mocha Test Suite
- @digitalbazaar/did-io
- Danube Tech Universal Resolver (reference implementation)
BSD-3-Clause © Digital Bazaar