From 6e88033efa8c4b0688527ead59c1df08a3405d00 Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Fri, 28 Nov 2025 14:45:34 +0000 Subject: [PATCH 1/2] feat: add DID document --- src/index.js | 4 ++- src/middleware/index.js | 1 + src/middleware/withDidDocumentHandler.js | 40 ++++++++++++++++++++++++ test/miniflare/freeway.spec.js | 27 ++++++++++++++++ 4 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 src/middleware/withDidDocumentHandler.js diff --git a/src/index.js b/src/index.js index 95b2825..7508e60 100644 --- a/src/index.js +++ b/src/index.js @@ -30,7 +30,8 @@ import { withDelegationsStorage, withDelegationStubs, withOptionsRequest, - withCarParkFetch + withCarParkFetch, + withDidDocumentHandler } from './middleware/index.js' import { instrument } from '@microlabs/otel-cf-workers' import { NoopSpanProcessor, TraceIdRatioBasedSampler } from '@opentelemetry/sdk-trace-base' @@ -60,6 +61,7 @@ const middleware = composeMiddleware( withVersionHeader, withErrorHandler, withGatewayIdentity, + withDidDocumentHandler, withDelegationsStorage, // Handle UCAN invocations (POST requests only) diff --git a/src/middleware/index.js b/src/middleware/index.js index 8ea7a4c..092975c 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -9,6 +9,7 @@ export { withCarParkFetch } from './withCarParkFetch.js' export { withEgressTracker } from './withEgressTracker.js' export { withEgressClient } from './withEgressClient.js' export { withDelegationStubs } from './withDelegationStubs.js' +export { withDidDocumentHandler } from './withDidDocumentHandler.js' export { withGatewayIdentity } from './withGatewayIdentity.js' export { withUcanInvocationHandler } from './withUcanInvocationHandler.js' export { withDelegationsStorage } from './withDelegationsStorage.js' diff --git a/src/middleware/withDidDocumentHandler.js b/src/middleware/withDidDocumentHandler.js new file mode 100644 index 0000000..8fa953a --- /dev/null +++ b/src/middleware/withDidDocumentHandler.js @@ -0,0 +1,40 @@ +/** + * @import { Middleware, Environment } from '@web3-storage/gateway-lib' + * @import { GatewayIdentityContext } from './withGatewayIdentity.types.js' + */ + +/** + * Handles a GET request for `/.well-known/did.json` and passes on all other + * requests to the next handler in the chain. + * + * @type {Middleware} + */ +export function withDidDocumentHandler (handler) { + return async (request, env, ctx) => { + if (request.method !== 'GET' || new URL(request.url).pathname !== '/.well-known/did.json') { + return handler(request, env, ctx) + } + + const webKey = ctx.gatewayIdentity.did() + const publicKeyMultibase = ctx.gatewaySigner.did().replace('did:key:', '') + const verificationMethods = [ + { + id: `${webKey}#owner`, + type: 'Ed25519VerificationKey2020', + controller: webKey, + publicKeyMultibase + }, + ] + + const headers = { 'Content-Type': 'application/json' } + const body = JSON.stringify({ + '@context': ['https://w3id.org/did/v1'], + id: webKey, + verificationMethod: verificationMethods, + assertionMethod: verificationMethods.map(k => k.id), + authentication: verificationMethods.map(k => k.id) + }, null, 2) + + return new Response(body, { headers }) + } +} diff --git a/test/miniflare/freeway.spec.js b/test/miniflare/freeway.spec.js index f44e049..b30121f 100644 --- a/test/miniflare/freeway.spec.js +++ b/test/miniflare/freeway.spec.js @@ -67,6 +67,8 @@ describe('freeway', () => { CONTENT_CLAIMS_SERVICE_URL: claimsService.url.toString(), CARPARK_PUBLIC_BUCKET_URL: url.toString(), GATEWAY_SERVICE_DID: 'did:example:gateway', + // did:key:z6MkvB4hvuVZhkKsi67rUKtNTKN4gpWjvWCoGPpBpGUdJMpF + GATEWAY_PRINCIPAL_KEY: 'MgCatBcQ1htL3Q/kXNZ9idtpoP9EeFxUSXD106hMWXc8si+0B6ZTgeQlqgoY9Z21skaKK6FpFRAAEF50T1c9WhWTa14w=', DAGPB_CONTENT_CACHE: 'DAGPB_CONTENT_CACHE', FF_DAGPB_CONTENT_CACHE_ENABLED: 'true', FF_DAGPB_CONTENT_CACHE_TTL_SECONDS: 300, @@ -763,4 +765,29 @@ describe('freeway', () => { assertBlobEqual(input, await res.blob()) assert.equal(cachedContent.keys.length, 0, 'Cache should be empty') }) + + it('should return DID document', async () => { + const res = await miniflare.dispatchFetch( + 'http://localhost:8787/.well-known/did.json' + ) + assert(res.ok) + const didDoc = await res.json() + + assert(didDoc) + assert(typeof didDoc === 'object') + + assert('id' in didDoc) + assert.equal(didDoc.id, 'did:example:gateway') + + assert('verificationMethod' in didDoc) + assert(Array.isArray(didDoc.verificationMethod)) + assert(didDoc.verificationMethod.length === 1) + + const method = didDoc.verificationMethod[0] + assert('publicKeyMultibase' in method) + assert.equal( + method.publicKeyMultibase, + 'z6MkvB4hvuVZhkKsi67rUKtNTKN4gpWjvWCoGPpBpGUdJMpF' + ) + }) }) From 92ee7aed87a9d7b006872c5979d26cf1379207f7 Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Fri, 28 Nov 2025 15:02:45 +0000 Subject: [PATCH 2/2] chore: appease linter --- src/middleware/withDidDocumentHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middleware/withDidDocumentHandler.js b/src/middleware/withDidDocumentHandler.js index 8fa953a..1405619 100644 --- a/src/middleware/withDidDocumentHandler.js +++ b/src/middleware/withDidDocumentHandler.js @@ -23,7 +23,7 @@ export function withDidDocumentHandler (handler) { type: 'Ed25519VerificationKey2020', controller: webKey, publicKeyMultibase - }, + } ] const headers = { 'Content-Type': 'application/json' }