diff --git a/examples/rest-api/setup.ts b/examples/rest-api/setup.ts index ab6fddb03..1984db0b6 100644 --- a/examples/rest-api/setup.ts +++ b/examples/rest-api/setup.ts @@ -1,15 +1,16 @@ import * as Daf from 'daf-core' import * as DafEthr from 'daf-ethr-did' import * as DafLibSodium from 'daf-libsodium' -import { W3cActionHandler, W3cMessageHandler} from 'daf-w3c' +import { W3cActionHandler, W3cMessageHandler } from 'daf-w3c' import { JwtMessageHandler } from 'daf-did-jwt' -import { DIDCommActionHandler, DIDCommMessageHandler} from 'daf-did-comm' +import { DIDCommActionHandler, DIDCommMessageHandler } from 'daf-did-comm' import { DafResolver } from 'daf-resolver' import Debug from 'debug' Debug.enable('*') -const messageHandler = new JwtMessageHandler() +const messageHandler = new DIDCommMessageHandler() +messageHandler.setNext(new JwtMessageHandler()) messageHandler.setNext(new W3cMessageHandler()) const actionHandler = new DIDCommActionHandler() diff --git a/examples/send-vc/index.ts b/examples/send-vc/index.ts index 88bafd265..78da90403 100644 --- a/examples/send-vc/index.ts +++ b/examples/send-vc/index.ts @@ -1,5 +1,5 @@ import { AbstractIdentity, EventTypes, Entities, Message } from 'daf-core' -import { ActionSendJWT } from 'daf-did-comm' +import { ActionSendDIDComm, ActionTypes } from 'daf-did-comm' import { ActionSignW3cVc } from 'daf-w3c' import { agent } from './setup' import { createConnection } from 'typeorm' @@ -40,15 +40,16 @@ async function main() { }, } as ActionSignW3cVc) - // Send verifiable credential using DIDComm or TrustGraph + // Send verifiable credential using DIDComm await agent.handleAction({ - type: 'action.sendJwt', + type: ActionTypes.sendMessageDIDCommAlpha1, data: { from: identity.did, to: 'did:web:uport.me', - jwt: vcJwt, + type: 'jwt', + body: vcJwt, }, - } as ActionSendJWT) + } as ActionSendDIDComm) } // This is triggered when DAF successfully saves a new message diff --git a/packages/daf-cli/src/credential.ts b/packages/daf-cli/src/credential.ts index bbf9cac34..abc26236e 100644 --- a/packages/daf-cli/src/credential.ts +++ b/packages/daf-cli/src/credential.ts @@ -65,12 +65,13 @@ program if (!cmd.send) { await agent.handleMessage({ raw: jwt, metaData: [{ type: 'cli' }] }) } else { - const sendAction: DIDComm.ActionSendJWT = { - type: DIDComm.ActionTypes.sendJwt, + const sendAction: DIDComm.ActionSendDIDComm = { + type: DIDComm.ActionTypes.sendMessageDIDCommAlpha1, data: { from: answers.iss, to: answers.sub, - jwt, + type: 'jwt', + body: jwt, }, } try { @@ -210,12 +211,13 @@ program if (!cmd.send) { await agent.handleMessage({ raw: jwt, metaData: [{ type: 'cli' }] }) } else { - const sendAction: DIDComm.ActionSendJWT = { - type: DIDComm.ActionTypes.sendJwt, + const sendAction: DIDComm.ActionSendDIDComm = { + type: DIDComm.ActionTypes.sendMessageDIDCommAlpha1, data: { from: answers.iss, to: aud, - jwt, + type: 'jwt', + body: jwt, }, } try { diff --git a/packages/daf-cli/src/graphql.ts b/packages/daf-cli/src/graphql.ts index 44bb8bc1d..7d6e16927 100644 --- a/packages/daf-cli/src/graphql.ts +++ b/packages/daf-cli/src/graphql.ts @@ -3,6 +3,7 @@ import program from 'commander' import { Gql } from 'daf-core' import { W3cGql } from 'daf-w3c' import { TrustGraphGql } from 'daf-trust-graph' +import { DIDCommGql } from 'daf-did-comm' import { SdrGql } from 'daf-selective-disclosure' import merge from 'lodash.merge' import { agent, dataStore } from './setup' @@ -20,6 +21,7 @@ program Gql.Core.typeDefs, Gql.IdentityManager.typeDefs, TrustGraphGql.typeDefs, + DIDCommGql.typeDefs, W3cGql.typeDefs, SdrGql.typeDefs, ], @@ -27,6 +29,7 @@ program Gql.Core.resolvers, Gql.IdentityManager.resolvers, TrustGraphGql.resolvers, + DIDCommGql.resolvers, W3cGql.resolvers, SdrGql.resolvers, ), diff --git a/packages/daf-cli/src/sdr.ts b/packages/daf-cli/src/sdr.ts index f8204ee03..2c4a74344 100644 --- a/packages/daf-cli/src/sdr.ts +++ b/packages/daf-cli/src/sdr.ts @@ -142,12 +142,13 @@ program if (!cmd.send) { await agent.handleMessage({ raw: jwt, metaData: [{ type: 'cli' }] }) } else if (answers.sub !== '') { - const sendAction: DIDComm.ActionSendJWT = { - type: DIDComm.ActionTypes.sendJwt, + const sendAction: DIDComm.ActionSendDIDComm = { + type: DIDComm.ActionTypes.sendMessageDIDCommAlpha1, data: { from: answers.iss, to: answers.sub, - jwt, + type: 'jwt', + body: jwt, }, } try { diff --git a/packages/daf-core/src/agent.ts b/packages/daf-core/src/agent.ts index 92e2165b2..6ff5c2f6c 100644 --- a/packages/daf-core/src/agent.ts +++ b/packages/daf-core/src/agent.ts @@ -87,38 +87,45 @@ export class Agent extends EventEmitter { return result } - public async handleMessage({ raw, metaData, save = true}: - { raw: string, metaData?: MetaData[], save?: boolean}): Promise { - - debug('Handle message %o', { raw, metaData, save, }) - if (!this.messageHandler) { - return Promise.reject('Message handler not provided') + public async handleMessage({ + raw, + metaData, + save = true, + }: { + raw: string + metaData?: MetaData[] + save?: boolean + }): Promise { + debug('Handle message %o', { raw, metaData, save }) + if (!this.messageHandler) { + return Promise.reject('Message handler not provided') + } + + try { + const message = await this.messageHandler.handle(new Message({ raw, metaData }), this) + if (message.isValid()) { + debug('Emitting event', EventTypes.validatedMessage) + this.emit(EventTypes.validatedMessage, message) } - try { - const message = await this.messageHandler.handle(new Message({ raw, metaData }), this) - if (message.isValid()) { - debug('Emitting event', EventTypes.validatedMessage) - this.emit(EventTypes.validatedMessage, message) - } - - debug('Validated message %o', message) - if (save) { - await message.save() - debug('Emitting event', EventTypes.savedMessage) - this.emit(EventTypes.savedMessage, message) - } - return message - } catch (error) { - this.emit(EventTypes.error, error) - return Promise.reject(error) + debug('Validated message %o', message) + if (save) { + await message.save() + debug('Emitting event', EventTypes.savedMessage) + this.emit(EventTypes.savedMessage, message) } + return message + } catch (error) { + this.emit(EventTypes.error, error) + return Promise.reject(error) + } } public async handleAction(action: Action): Promise { if (!this.actionHandler) { return Promise.reject('Action handler not provided') } + debug('Handle action %o', action) return this.actionHandler.handleAction(action, this) } } diff --git a/packages/daf-did-comm/src/action-handler.ts b/packages/daf-did-comm/src/action-handler.ts index b7b3434fd..8b083b9e4 100644 --- a/packages/daf-did-comm/src/action-handler.ts +++ b/packages/daf-did-comm/src/action-handler.ts @@ -5,14 +5,18 @@ import Debug from 'debug' const debug = Debug('daf:did-comm:action-handler') export const ActionTypes = { - sendJwt: 'action.sendJwt', + sendMessageDIDCommAlpha1: 'send.message.didcomm-alpha-1', } -export interface ActionSendJWT extends Action { +export interface ActionSendDIDComm extends Action { + url?: string + save?: boolean data: { + id?: string from: string to: string - jwt: string + type: string + body: any } } @@ -22,51 +26,50 @@ export class DIDCommActionHandler extends AbstractActionHandler { } public async handleAction(action: Action, agent: Agent) { - if (action.type === ActionTypes.sendJwt) { - const { data } = action as ActionSendJWT + if (action.type === ActionTypes.sendMessageDIDCommAlpha1) { + const { data, url, save = true } = action as ActionSendDIDComm debug('Resolving didDoc') const didDoc = await agent.didResolver.resolve(data.to) - const service = didDoc && didDoc.service && didDoc.service.find(item => item.type == 'Messaging') + let serviceEndpoint + if (url) { + serviceEndpoint = url + } else { + const service = didDoc && didDoc.service && didDoc.service.find(item => item.type == 'Messaging') + serviceEndpoint = service?.serviceEndpoint + } - if (service) { + if (serviceEndpoint) { try { - let body = data.jwt - + data.id = data.id || uuid.v4() + let postPayload = JSON.stringify(data) try { const identity = await agent.identityManager.getIdentity(data.from) - const dm = JSON.stringify({ - '@type': 'JWT', - id: uuid.v4(), - data: data.jwt, - }) - debug(dm) - const key = await identity.keyByType('Ed25519') const publicKey = didDoc?.publicKey.find(item => item.type == 'Ed25519VerificationKey2018') if (!publicKey?.publicKeyHex) throw Error('Recipient does not have encryption publicKey') - body = await key.encrypt( + postPayload = await key.encrypt( { type: 'Ed25519', publicKeyHex: publicKey?.publicKeyHex, kid: publicKey?.publicKeyHex, }, - dm, + postPayload, ) - debug('Encrypted:', body) + debug('Encrypted:', postPayload) } catch (e) {} - debug('Sending to %s', service.serviceEndpoint) - const res = await fetch(service.serviceEndpoint, { + debug('Sending to %s', serviceEndpoint) + const res = await fetch(serviceEndpoint, { method: 'POST', - body, + body: postPayload, }) debug('Status', res.status, res.statusText) if (res.status == 200) { - return agent.handleMessage({ raw: data.jwt, metaData: [{ type: 'DIDComm-sent' }] }) + return agent.handleMessage({ raw: data.body, metaData: [{ type: 'DIDComm-sent' }], save }) } return res.status == 200 diff --git a/packages/daf-did-comm/src/graphql.ts b/packages/daf-did-comm/src/graphql.ts index 664174ef8..27141c1ce 100644 --- a/packages/daf-did-comm/src/graphql.ts +++ b/packages/daf-did-comm/src/graphql.ts @@ -1,38 +1,47 @@ import { Agent } from 'daf-core' -import { ActionTypes } from './action-handler' +import { ActionTypes, ActionSendDIDComm } from './action-handler' interface Context { agent: Agent } -const actionSendJwt = async ( +const sendMessageDidCommAlpha1 = async ( _: any, args: { - from: string - to: string - jwt: string + save: boolean + url?: string + data: { + from: string + to: string + body: string + type: string + } }, ctx: Context, ) => { return await ctx.agent.handleAction({ - type: ActionTypes.sendJwt, - data: { - from: args.from, - to: args.to, - jwt: args.jwt, - }, - }) + type: ActionTypes.sendMessageDIDCommAlpha1, + save: args.save, + url: args.url, + data: args.data, + } as ActionSendDIDComm) } export const resolvers = { Mutation: { - actionSendJwt, + sendMessageDidCommAlpha1, }, } export const typeDefs = ` + input SendMessageDidCommAlpha1Input { + from: String! + to: String! + type: String! + body: String! + } extend type Mutation { - actionSendJwt(from: String!, to: String!, jwt: String!): Boolean + sendMessageDidCommAlpha1(data: SendMessageDidCommAlpha1Input!, url: String, save: Boolean = true): Message } ` export default { diff --git a/packages/daf-did-comm/src/index.ts b/packages/daf-did-comm/src/index.ts index f636fb241..ac5d8b992 100644 --- a/packages/daf-did-comm/src/index.ts +++ b/packages/daf-did-comm/src/index.ts @@ -1,4 +1,4 @@ -export { DIDCommActionHandler, ActionSendJWT, ActionTypes } from './action-handler' +export { DIDCommActionHandler, ActionSendDIDComm, ActionTypes } from './action-handler' export { DIDCommMessageHandler } from './message-handler' import DIDCommGql from './graphql' export { DIDCommGql } diff --git a/packages/daf-did-comm/src/message-handler.ts b/packages/daf-did-comm/src/message-handler.ts index 4ceca8a4b..10f523659 100644 --- a/packages/daf-did-comm/src/message-handler.ts +++ b/packages/daf-did-comm/src/message-handler.ts @@ -24,12 +24,12 @@ export class DIDCommMessageHandler extends AbstractMessageHandler { try { const json = JSON.parse(decrypted) - if (json['@type'] === 'JWT') { - message.raw = json.data + if (json['type'] === 'jwt') { + message.raw = json.body message.addMetaData({ type: 'DIDComm' }) } else { - if (json['@id']) message.id = json['@id'] - if (json['@type']) message.type = json['@type'] + if (json['id']) message.id = json['id'] + if (json['type']) message.type = json['type'] message.raw = decrypted message.data = json message.addMetaData({ type: 'DIDComm' }) @@ -45,6 +45,17 @@ export class DIDCommMessageHandler extends AbstractMessageHandler { return super.handle(message, agent) } } + } else if (parsed.type === 'jwt') { + message.raw = parsed.body + if (parsed['id']) message.id = parsed['id'] + message.addMetaData({ type: 'DIDComm' }) + return super.handle(message, agent) + } else { + message.data = parsed.body + if (parsed['id']) message.id = parsed['id'] + if (parsed['type']) message.type = parsed['type'] + message.addMetaData({ type: 'DIDComm' }) + return super.handle(message, agent) } } catch (e) { // not a JSON string