From ba897ded1b092dd7d21634cfb0de396a59d2e0d5 Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Sun, 30 Jul 2023 23:17:23 +0200 Subject: [PATCH] chore: small fixes/changes --- .../__tests__/agent.ts | 19 +- .../src/api-functions.ts | 395 +++++++++--------- .../uni-resolver-registrar-api/src/types.ts | 11 +- .../src/suites/JsonWebSignature2020.ts | 24 +- 4 files changed, 234 insertions(+), 215 deletions(-) diff --git a/packages/uni-resolver-registrar-api/__tests__/agent.ts b/packages/uni-resolver-registrar-api/__tests__/agent.ts index b1a97483a..eb3c48034 100644 --- a/packages/uni-resolver-registrar-api/__tests__/agent.ts +++ b/packages/uni-resolver-registrar-api/__tests__/agent.ts @@ -23,6 +23,7 @@ import { ITokenPayload, VerifyCallback } from 'passport-azure-ad/common' import { getResolver as getDidWebResolver } from 'web-did-resolver' import { UniResolverApiServer } from '../src' +import { DidWebServer } from '../src/did-web-server' import config from './config.json' import { DB_CONNECTION_NAME, DB_ENCRYPTION_KEY, getDbConnection } from './database' @@ -199,7 +200,7 @@ agent new UniResolverApiServer({ opts: { - enableFeatures: ['did-persist', 'did-resolve', 'did-web-global-resolution'], + enableFeatures: ['did-persist', 'did-resolve'], endpointOpts: { globalAuth: { authentication: { @@ -212,7 +213,21 @@ agent expressArgs, agent, }) - // builder.startListening(expressArgs.express) + + new DidWebServer({ + opts: { + enableFeatures: ['did-web-global-resolution'], + globalAuth: { + authentication: { + enabled: false, + strategy: bearerStrategy, + }, + }, + endpointOpts: {}, + }, + expressArgs, + agent, + }) }) export default agent diff --git a/packages/uni-resolver-registrar-api/src/api-functions.ts b/packages/uni-resolver-registrar-api/src/api-functions.ts index 2ec29a091..187211310 100644 --- a/packages/uni-resolver-registrar-api/src/api-functions.ts +++ b/packages/uni-resolver-registrar-api/src/api-functions.ts @@ -1,190 +1,192 @@ -import { DIDResolutionResult } from '@sphereon/did-uni-client' -import { getAgentDIDMethods, toDidDocument, toDidResolutionResult } from '@sphereon/ssi-sdk-ext.did-utils' -import { JwkKeyUse } from '@sphereon/ssi-sdk-ext.key-utils' -import { checkAuth, ISingleEndpointOpts, sendErrorResponse } from '@sphereon/ssi-sdk.express-support' -import { parseDid } from '@sphereon/ssi-types' -import { IIdentifier } from '@veramo/core' -import { Request, Response, Router } from 'express' -import { v4 } from 'uuid' +import {DIDResolutionResult} from '@sphereon/did-uni-client' +import {getAgentDIDMethods, toDidDocument, toDidResolutionResult} from '@sphereon/ssi-sdk-ext.did-utils' +import {JwkKeyUse} from '@sphereon/ssi-sdk-ext.key-utils' +import {checkAuth, ISingleEndpointOpts, sendErrorResponse} from '@sphereon/ssi-sdk.express-support' +import {parseDid} from '@sphereon/ssi-types' +import {IIdentifier} from '@veramo/core' +import {Request, Response, Router} from 'express' +import {v4} from 'uuid' import { - CreateState, - DidRegistrationCreateRequest, - DidStateValue, - ICreateDidEndpointOpts, - IGlobalDidWebEndpointOpts, - IRequiredContext, + CreateState, + DidRegistrationCreateRequest, + DidStateValue, + ICreateDidEndpointOpts, + IGlobalDidWebEndpointOpts, + IRequiredContext, } from './types' export function createDidEndpoint(router: Router, context: IRequiredContext, opts?: ICreateDidEndpointOpts) { - if (opts?.enabled === false) { - console.log(`create DID endpoint is disabled`) - return - } - const path = opts?.path ?? '/identifiers' - - router.post(path, checkAuth(opts?.endpoint), async (request: Request, response: Response) => { - try { - const createRequest: DidRegistrationCreateRequest = request.body - if (!createRequest) { - return sendErrorResponse(response, 400, 'No DID create request present') - } - const did = createRequest.did - const didMethod = (request.query.method as string) ?? (did ? parseDid(did).method : opts?.defaultMethod) - const allDidMethods = await getAgentDIDMethods(context) - if (!didMethod) { - return sendErrorResponse(response, 400, 'No DID method supplied or deductible') - } else if (did && parseDid(did).method != didMethod) { - return sendErrorResponse(response, 400, 'DID method did not match method param') - } else if (!allDidMethods.includes(didMethod)) { - return sendErrorResponse(response, 400, 'DID method not supported') - } - const provider = `did:${didMethod}` - const jobId = createRequest.jobId ?? `urn:uuid:${v4()}` - let alias: string | undefined = undefined - if (didMethod === 'web') { - if (!did) { - return sendErrorResponse(response, 400, 'Please provide a value for "did" in the request body when creating a DID web') - } - alias = parseDid(did).id - if (!alias) { - return sendErrorResponse(response, 400, 'Could not determine alias from did:web DID value: ' + did) - } - } + if (opts?.enabled === false) { + console.log(`create DID endpoint is disabled`) + return + } + const path = opts?.path ?? '/identifiers' - let identifier: IIdentifier | undefined - let state: DidStateValue | undefined - if (opts?.noErrorOnExistingDid && did) { + router.post(path, checkAuth(opts?.endpoint), async (request: Request, response: Response) => { try { - identifier = await context.agent.didManagerGet({ did }) - state = 'exists' + const createRequest: DidRegistrationCreateRequest = request.body + if (!createRequest) { + return sendErrorResponse(response, 400, 'No DID create request present') + } + const did = createRequest.did + const didMethod = (request.query.method as string) ?? (did ? parseDid(did).method : opts?.defaultMethod) + const allDidMethods = await getAgentDIDMethods(context) + if (!didMethod) { + return sendErrorResponse(response, 400, 'No DID method supplied or deductible') + } else if (did && parseDid(did).method != didMethod) { + return sendErrorResponse(response, 400, 'DID method did not match method param') + } else if (!allDidMethods.includes(didMethod)) { + return sendErrorResponse(response, 400, 'DID method not supported') + } + const provider = `did:${didMethod}` + const jobId = createRequest.jobId ?? `urn:uuid:${v4()}` + let alias: string | undefined = undefined + if (didMethod === 'web') { + if (!did) { + return sendErrorResponse(response, 400, 'Please provide a value for "did" in the request body when creating a DID web') + } + alias = parseDid(did).id + if (!alias) { + return sendErrorResponse(response, 400, 'Could not determine alias from did:web DID value: ' + did) + } + } + + let identifier: IIdentifier | undefined + let state: DidStateValue | undefined + if (opts?.noErrorOnExistingDid && did) { + try { + identifier = await context.agent.didManagerGet({did}) + state = 'exists' + } catch (e) { + // Okay, since we will create a new one + } + } + if (identifier === undefined) { + if (createRequest.options.storeSecrets === false) { + return sendErrorResponse(response, 400, 'Only storeSecrets mode is supported currently') + /*const memoryKMS = new SphereonKeyManager({ + store: new MemoryKeyStore(), + kms: {'mem': new KeyManagementSystem(new MemoryPrivateKeyStore())} + }) + identifier = await memoryKMS..didManagerCreate({provider, alias, kms: opts?.kms})*/ + } else if (createRequest.options.storeSecrets || opts?.storeSecrets) { + identifier = await context.agent.didManagerCreate({provider, alias, kms: opts?.kms}) + state = 'finished' + } else { + return sendErrorResponse(response, 400, 'Only storeSecrets mode is supported currently') + } + } + if (!identifier || !state) { + return sendErrorResponse(response, 400, 'An identifier and did state should be present at this point') + } + + const didDocument = toDidDocument(identifier, {did, use: [JwkKeyUse.Signature, JwkKeyUse.Encryption]}) + const createState: CreateState = { + jobId, + didState: { + did: identifier.did, + state, + didDocument, + }, + } + response.statusCode = 200 + return response.send(createState) } catch (e) { - // Okay, since we will create a new one - } - } - if (identifier === undefined) { - if (createRequest.options.storeSecrets === false) { - return sendErrorResponse(response, 400, 'Only storeSecrets mode is supported currently') - /*const memoryKMS = new SphereonKeyManager({ - store: new MemoryKeyStore(), - kms: {'mem': new KeyManagementSystem(new MemoryPrivateKeyStore())} - }) - identifier = await memoryKMS..didManagerCreate({provider, alias, kms: opts?.kms})*/ - } else if (createRequest.options.storeSecrets || opts?.storeSecrets) { - identifier = await context.agent.didManagerCreate({ provider, alias, kms: opts?.kms }) - state = 'finished' - } else { - return sendErrorResponse(response, 400, 'Only storeSecrets mode is supported currently') + return sendErrorResponse(response, 500, e.message as string, e) } - } - if (!identifier || !state) { - return sendErrorResponse(response, 400, 'An identifier and did state should be present at this point') - } - - const didDocument = toDidDocument(identifier, { did, use: [JwkKeyUse.Signature, JwkKeyUse.Encryption] }) - const createState: CreateState = { - jobId, - didState: { - did: identifier.did, - state, - didDocument, - }, - } - response.statusCode = 200 - return response.send(createState) - } catch (e) { - return sendErrorResponse(response, 500, e.message as string, e) - } - }) + }) } export function getDidMethodsEndpoint(router: Router, context: IRequiredContext, opts?: ISingleEndpointOpts) { - if (opts?.enabled === false) { - console.log(`Get DID methods endpoint is disabled`) - return - } - const path = opts?.path ?? '/methods' - router.get(path, checkAuth(opts?.endpoint), async (request: Request, response: Response) => { - try { - const methods = await getAgentDIDMethods(context) // these are already without the 'did:' prefix - response.statusCode = 200 - return response.send(methods) - } catch (e) { - return sendErrorResponse(response, 500, e.message as string, e) + if (opts?.enabled === false) { + console.log(`Get DID methods endpoint is disabled`) + return } - }) + const path = opts?.path ?? '/methods' + router.get(path, checkAuth(opts?.endpoint), async (request: Request, response: Response) => { + try { + const methods = await getAgentDIDMethods(context) // these are already without the 'did:' prefix + response.statusCode = 200 + return response.send(methods) + } catch (e) { + return sendErrorResponse(response, 500, e.message as string, e) + } + }) } async function agentDidToResolutionResult(context: IRequiredContext, did: string) { - try { - const identifier = await context.agent.didManagerGet({ did }) - console.log(JSON.stringify(identifier, null, 2)) - return toDidResolutionResult(identifier, { - did, - supportedMethods: await getAgentDIDMethods(context), - }) - } catch (error) { - console.log(JSON.stringify(error.message)) - return { - didDocument: null, - didResolutionMetadata: { - error: 'notFound', - }, - didDocumentMetadata: {}, + try { + const identifier = await context.agent.didManagerGet({did}) + console.log(JSON.stringify(identifier, null, 2)) + return toDidResolutionResult(identifier, { + did, + supportedMethods: await getAgentDIDMethods(context), + }) + } catch (error) { + console.log(JSON.stringify(error.message)) + return { + didDocument: null, + didResolutionMetadata: { + error: 'notFound', + }, + didDocumentMetadata: {}, + } } - } } -export function resolveDidEndpoint(router: Router, context: IRequiredContext, opts?: ISingleEndpointOpts & { mode?: 'local' | 'resolve' }) { - if (opts?.enabled === false) { - console.log(`Resolve DID endpoint is disabled`) - return - } - const path = opts?.path ?? '/identifiers/:identifier' - router.get(path, checkAuth(opts?.endpoint), async (request: Request, response: Response) => { - try { - const did = request.params.identifier - if (!did) { - return sendErrorResponse(response, 400, 'no identifier provided') - } - const mode = request.query.mode?.toString().toLowerCase() ?? opts?.mode?.toLowerCase() - let resolutionResult: DIDResolutionResult | undefined - if (mode === 'local' || mode === 'hybrid') { - resolutionResult = await agentDidToResolutionResult(context, did) - } - if (mode !== 'local' && !resolutionResult?.didDocument) { - resolutionResult = await context.agent.resolveDid({ didUrl: did }) - } - - response.statusCode = 200 - return response.send(resolutionResult) - } catch (e) { - return sendErrorResponse(response, 500, e.message as string, e) +export function resolveDidEndpoint(router: Router, context: IRequiredContext, opts?: ISingleEndpointOpts & { + mode?: 'local' | 'hybrid' | 'global' +}) { + if (opts?.enabled === false) { + console.log(`Resolve DID endpoint is disabled`) + return } - }) + const path = opts?.path ?? '/identifiers/:identifier' + router.get(path, checkAuth(opts?.endpoint), async (request: Request, response: Response) => { + try { + const did = request.params.identifier + if (!did) { + return sendErrorResponse(response, 400, 'no identifier provided') + } + const mode = request.query.mode?.toString().toLowerCase() ?? opts?.mode?.toLowerCase() ?? 'hybrid' + let resolutionResult: DIDResolutionResult | undefined + if (mode === 'local' || mode === 'hybrid') { + resolutionResult = await agentDidToResolutionResult(context, did) + } + if (mode !== 'local' && !resolutionResult?.didDocument) { + resolutionResult = await context.agent.resolveDid({didUrl: did}) + } + + response.statusCode = 200 + return response.send(resolutionResult) + } catch (e) { + return sendErrorResponse(response, 500, e.message as string, e) + } + }) } export function deactivateDidEndpoint(router: Router, context: IRequiredContext, opts?: ISingleEndpointOpts) { - if (opts?.enabled === false) { - console.log(`Deactivate DID endpoint is disabled`) - return - } - router.delete(opts?.path ?? '/identifiers/:identifier', checkAuth(opts?.endpoint), async (request: Request, response: Response) => { - try { - const did = request.params.identifier - if (!did) { - return sendErrorResponse(response, 400, 'no DID provided') - } - - const result = await context.agent.didManagerDelete({ did }) - if (!result) { - return sendErrorResponse(response, 404, `id ${did} not found`) - } - response.statusCode = 200 - return response.send() - } catch (e) { - return sendErrorResponse(response, 500, e.message as string, e) + if (opts?.enabled === false) { + console.log(`Deactivate DID endpoint is disabled`) + return } - }) + router.delete(opts?.path ?? '/identifiers/:identifier', checkAuth(opts?.endpoint), async (request: Request, response: Response) => { + try { + const did = request.params.identifier + if (!did) { + return sendErrorResponse(response, 400, 'no DID provided') + } + + const result = await context.agent.didManagerDelete({did}) + if (!result) { + return sendErrorResponse(response, 404, `id ${did} not found`) + } + response.statusCode = 200 + return response.send() + } catch (e) { + return sendErrorResponse(response, 500, e.message as string, e) + } + }) } /** @@ -203,36 +205,39 @@ export function deactivateDidEndpoint(router: Router, context: IRequiredContext, * @param opts */ export function didWebDomainEndpoint(router: Router, context: IRequiredContext, opts?: IGlobalDidWebEndpointOpts) { - if (opts?.enabled === false) { - console.log(`DID Web domain resolution endpoint is disabled`) - return - } - router.get(opts?.path ?? ':path(*)/did.json', checkAuth(opts?.endpoint), async (request: Request, response: Response) => { - try { - const path = request.params.path - if (!path || path.length === 0) { - return sendErrorResponse(response, 404, 'Not found') - } - let did: string - did = `did:web:${opts?.hostname ?? request.hostname}` - if (path !== '.well-known') { - if (opts?.disableSubPaths) { - return sendErrorResponse(response, 404, 'Not found') - } - const suffix = path.replace(/\//g, ':').replace(/%2F/g, ':') - if (!suffix.startsWith(':')) { - did += ':' - } - did += suffix - } else if (opts?.disableWellKnown) { - return sendErrorResponse(response, 404, 'Not found') - } - - const resolutionResult = await agentDidToResolutionResult(context, did) - response.statusCode = 200 - return response.send(resolutionResult) - } catch (e) { - return sendErrorResponse(response, 500, e.message as string, e) + if (opts?.enabled === false) { + console.log(`DID Web domain resolution endpoint is disabled`) + return } - }) + router.get(opts?.path ?? ':path(*)/did.json', checkAuth(opts?.endpoint), async (request: Request, response: Response) => { + try { + const path = request.params.path + if (!path || path.length === 0) { + return sendErrorResponse(response, 404, 'Not found') + } + let did: string + did = `did:web:${opts?.hostname ?? request.hostname}` + if (path !== '.well-known') { + if (opts?.disableSubPaths) { + return sendErrorResponse(response, 404, 'Not found') + } + const suffix = path.replace(/\//g, ':').replace(/%2F/g, ':') + if (!suffix.startsWith(':')) { + did += ':' + } + did += suffix + } else if (opts?.disableWellKnown) { + return sendErrorResponse(response, 404, 'Not found') + } + + const resolutionResult = await agentDidToResolutionResult(context, did) + if (!resolutionResult || !resolutionResult.didDocument || resolutionResult?.didResolutionMetadata?.error === 'notFound') { + return sendErrorResponse(response, 404, 'Not found') + } + response.statusCode = 200 + return response.send(resolutionResult.didDocument) + } catch (e) { + return sendErrorResponse(response, 500, e.message as string, e) + } + }) } diff --git a/packages/uni-resolver-registrar-api/src/types.ts b/packages/uni-resolver-registrar-api/src/types.ts index efe254cf8..5e193c6d9 100644 --- a/packages/uni-resolver-registrar-api/src/types.ts +++ b/packages/uni-resolver-registrar-api/src/types.ts @@ -77,9 +77,15 @@ export interface DidState { didDocument?: DIDDocument } +export interface IDidWebServiceOpts { + globalAuth?: GenericAuthArgs + endpointOpts?: IGlobalDidWebEndpointOpts + enableFeatures?: DidWebServiceFeatures[] // Feature to enable. If not defined or empty. Has to be defined or no features will be enabled +} + export interface IDidAPIOpts { endpointOpts?: IDidAPIEndpointOpts - enableFeatures?: didApiFeatures[] // Feature to enable. If not defined or empty, all features will be enabled + enableFeatures?: DidApiFeatures[] // Feature to enable. If not defined or empty, all features will be enabled } export interface IDidAPIEndpointOpts { @@ -105,4 +111,5 @@ export interface ICreateDidEndpointOpts extends ISingleEndpointOpts { defaultMethod?: string } -export type didApiFeatures = 'did-resolve' | 'did-persist' | 'did-web-global-resolution' +export type DidWebServiceFeatures = 'did-web-global-resolution' +export type DidApiFeatures = 'did-resolve' | 'did-persist' diff --git a/packages/vc-handler-ld-local/src/suites/JsonWebSignature2020.ts b/packages/vc-handler-ld-local/src/suites/JsonWebSignature2020.ts index 78d46c029..202c542b4 100644 --- a/packages/vc-handler-ld-local/src/suites/JsonWebSignature2020.ts +++ b/packages/vc-handler-ld-local/src/suites/JsonWebSignature2020.ts @@ -1,21 +1,13 @@ -import {JwkKeyUse, toJwk} from '@sphereon/ssi-sdk-ext.key-utils' -import {IProof, IVerifiableCredential} from '@sphereon/ssi-types' -import { - CredentialPayload, - DIDDocument, - IAgentContext, - IKey, - PresentationPayload, - TKeyType, - VerifiableCredential -} from '@veramo/core' -import {asArray, encodeJoseBlob} from '@veramo/utils' +import { JwkKeyUse, toJwk } from '@sphereon/ssi-sdk-ext.key-utils' +import { IProof, IVerifiableCredential } from '@sphereon/ssi-types' +import { CredentialPayload, DIDDocument, IAgentContext, IKey, PresentationPayload, TKeyType, VerifiableCredential } from '@veramo/core' +import { asArray, encodeJoseBlob } from '@veramo/utils' import * as u8a from 'uint8arrays' -import {RequiredAgentMethods, SphereonLdSignature} from '../ld-suites' +import { RequiredAgentMethods, SphereonLdSignature } from '../ld-suites' -import {JsonWebKey} from './impl/JsonWebKeyWithRSASupport' -import {JsonWebSignature} from './impl/JsonWebSignatureWithRSASupport' +import { JsonWebKey } from './impl/JsonWebKeyWithRSASupport' +import { JsonWebSignature } from './impl/JsonWebSignatureWithRSASupport' /** * Veramo wrapper for the JsonWebSignature2020 suite by Transmute Industries @@ -72,7 +64,7 @@ export class SphereonJsonWebSignature2020 extends SphereonLdSignature { }, } - const publicKeyJwk = key.meta?.publicKeyJwk ?? (await toJwk(key.publicKeyHex, key.type, {use: JwkKeyUse.Signature, key})) + const publicKeyJwk = key.meta?.publicKeyJwk ?? (await toJwk(key.publicKeyHex, key.type, { use: JwkKeyUse.Signature, key })) const verificationKey = await JsonWebKey.from( { id,