diff --git a/src/index.js b/src/index.js index 3c4029b..463db17 100644 --- a/src/index.js +++ b/src/index.js @@ -27,6 +27,7 @@ import { withDelegationStubs, withOptionsRequest, withCarParkFetch, + withDidDocumentHandler, withFormatRawHandler, withFormatCarHandler } from './middleware/index.js' @@ -50,6 +51,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 b7b1989..ff10eca 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -11,6 +11,7 @@ export { withEgressTracker } from './withEgressTracker.js' export { withFormatCarHandler } from './withFormatCarHandler.js' export { withFormatRawHandler } from './withFormatRawHandler.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..1405619 --- /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' + ) + }) })