diff --git a/packages/core/src/modules/connections/ConnectionsModule.ts b/packages/core/src/modules/connections/ConnectionsModule.ts index ad4588f283..7daf396f80 100644 --- a/packages/core/src/modules/connections/ConnectionsModule.ts +++ b/packages/core/src/modules/connections/ConnectionsModule.ts @@ -54,8 +54,10 @@ export class ConnectionsModule { invitation: ConnectionInvitationMessage connectionRecord: ConnectionRecord }> { - const mediationRecord = await this.mediationRecipientService.discoverMediation(config?.mediatorId) - const myRouting = await this.mediationRecipientService.getRouting(mediationRecord) + const myRouting = await this.mediationRecipientService.getRouting({ + mediatorId: config?.mediatorId, + useDefaultMediator: true, + }) const { connectionRecord: connectionRecord, message: invitation } = await this.connectionService.createInvitation({ autoAcceptConnection: config?.autoAcceptConnection, @@ -86,8 +88,7 @@ export class ConnectionsModule { mediatorId?: string } ): Promise { - const mediationRecord = await this.mediationRecipientService.discoverMediation(config?.mediatorId) - const myRouting = await this.mediationRecipientService.getRouting(mediationRecord) + const myRouting = await this.mediationRecipientService.getRouting({ mediatorId: config?.mediatorId }) let connection = await this.connectionService.processInvitation(invitation, { autoAcceptConnection: config?.autoAcceptConnection, diff --git a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts index 2f6051afd0..60a2ab6213 100644 --- a/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts +++ b/packages/core/src/modules/connections/handlers/ConnectionRequestHandler.ts @@ -38,8 +38,7 @@ export class ConnectionRequestHandler implements Handler { // routing object is required for multi use invitation, because we're creating a // new keypair that possibly needs to be registered at a mediator if (connectionRecord.multiUseInvitation) { - const mediationRecord = await this.mediationRecipientService.discoverMediation() - routing = await this.mediationRecipientService.getRouting(mediationRecord) + routing = await this.mediationRecipientService.getRouting() } connectionRecord = await this.connectionService.processRequest(messageContext, routing) diff --git a/packages/core/src/modules/routing/RecipientModule.ts b/packages/core/src/modules/routing/RecipientModule.ts index b8068bf7c2..badc0ed44c 100644 --- a/packages/core/src/modules/routing/RecipientModule.ts +++ b/packages/core/src/modules/routing/RecipientModule.ts @@ -280,7 +280,8 @@ export class RecipientModule { const connection = await this.connectionService.findByInvitationKey(invitation.recipientKeys[0]) if (!connection) { this.logger.debug('Mediation Connection does not exist, creating connection') - const routing = await this.mediationRecipientService.getRouting() + // We don't want to use the current default mediator when connecting to another mediator + const routing = await this.mediationRecipientService.getRouting({ useDefaultMediator: false }) const invitationConnectionRecord = await this.connectionService.processInvitation(invitation, { autoAcceptConnection: true, diff --git a/packages/core/src/modules/routing/services/MediationRecipientService.ts b/packages/core/src/modules/routing/services/MediationRecipientService.ts index 9b04398035..4f8b36b299 100644 --- a/packages/core/src/modules/routing/services/MediationRecipientService.ts +++ b/packages/core/src/modules/routing/services/MediationRecipientService.ts @@ -155,7 +155,17 @@ export class MediationRecipientService { return keylistUpdateMessage } - public async getRouting(mediationRecord?: MediationRecord): Promise { + public async getRouting({ mediatorId, useDefaultMediator = true }: GetRoutingOptions = {}): Promise { + let mediationRecord: MediationRecord | null = null + + if (mediatorId) { + mediationRecord = await this.getById(mediatorId) + } else if (useDefaultMediator) { + // If no mediatorId is provided, and useDefaultMediator is true (default) + // We use the default mediator if available + mediationRecord = await this.findDefaultMediator() + } + let endpoints = this.config.endpoints let routingKeys: string[] = [] @@ -288,3 +298,16 @@ export interface MediationProtocolMsgReturnType { + let agents: Agent[] + + afterEach(async () => { + for (const agent of agents) { + await agent.shutdown() + await agent.wallet.delete() + } + }) + test('Faber starts with connection-less proof requests to Alice', async () => { const { aliceAgent, faberAgent, aliceReplay, credDefId, faberReplay } = await setupProofsTest( 'Faber connection-less Proofs', 'Alice connection-less Proofs', AutoAcceptProof.Never ) + agents = [aliceAgent, faberAgent] testLogger.test('Faber sends presentation request to Alice') const attributes = { @@ -93,6 +123,8 @@ describe('Present Proof', () => { AutoAcceptProof.Always ) + agents = [aliceAgent, faberAgent] + const attributes = { name: new ProofAttributeInfo({ name: 'name', @@ -141,4 +173,159 @@ describe('Present Proof', () => { state: ProofState.Done, }) }) + + test('Faber starts with connection-less proof requests to Alice with auto-accept enabled and both agents having a mediator', async () => { + testLogger.test('Faber sends presentation request to Alice') + + const credentialPreview = CredentialPreview.fromRecord({ + name: 'John', + age: '99', + }) + + const unique = uuid().substring(0, 4) + + const mediatorConfig = getBaseConfig(`Connectionless proofs with mediator Mediator-${unique}`, { + autoAcceptMediationRequests: true, + endpoints: ['rxjs:mediator'], + }) + + const faberMessages = new Subject() + const aliceMessages = new Subject() + const mediatorMessages = new Subject() + + const subjectMap = { + 'rxjs:mediator': mediatorMessages, + } + + // Initialize mediator + const mediatorAgent = new Agent(mediatorConfig.config, mediatorConfig.agentDependencies) + mediatorAgent.registerOutboundTransport(new SubjectOutboundTransport(mediatorMessages, subjectMap)) + mediatorAgent.registerInboundTransport(new SubjectInboundTransport(mediatorMessages)) + await mediatorAgent.initialize() + + const faberMediationInvitation = await mediatorAgent.connections.createConnection() + const aliceMediationInvitation = await mediatorAgent.connections.createConnection() + + const faberConfig = getBaseConfig(`Connectionless proofs with mediator Faber-${unique}`, { + autoAcceptProofs: AutoAcceptProof.Always, + mediatorConnectionsInvite: faberMediationInvitation.invitation.toUrl({ domain: 'https://example.com' }), + }) + + const aliceConfig = getBaseConfig(`Connectionless proofs with mediator Alice-${unique}`, { + autoAcceptProofs: AutoAcceptProof.Always, + // logger: new TestLogger(LogLevel.test), + mediatorConnectionsInvite: aliceMediationInvitation.invitation.toUrl({ domain: 'https://example.com' }), + }) + + const faberAgent = new Agent(faberConfig.config, faberConfig.agentDependencies) + faberAgent.registerOutboundTransport(new SubjectOutboundTransport(faberMessages, subjectMap)) + faberAgent.registerInboundTransport(new SubjectInboundTransport(faberMessages)) + await faberAgent.initialize() + + const aliceAgent = new Agent(aliceConfig.config, aliceConfig.agentDependencies) + aliceAgent.registerOutboundTransport(new SubjectOutboundTransport(aliceMessages, subjectMap)) + aliceAgent.registerInboundTransport(new SubjectInboundTransport(aliceMessages)) + await aliceAgent.initialize() + + agents = [aliceAgent, faberAgent, mediatorAgent] + + const { definition } = await prepareForIssuance(faberAgent, ['name', 'age', 'image_0', 'image_1']) + + const [faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent) + expect(faberConnection.isReady).toBe(true) + expect(aliceConnection.isReady).toBe(true) + + await issueCredential({ + issuerAgent: faberAgent, + issuerConnectionId: faberConnection.id, + holderAgent: aliceAgent, + credentialTemplate: { + credentialDefinitionId: definition.id, + comment: 'some comment about credential', + preview: credentialPreview, + linkedAttachments: [ + new LinkedAttachment({ + name: 'image_0', + attachment: new Attachment({ + filename: 'picture-of-a-cat.png', + data: new AttachmentData({ base64: 'cGljdHVyZSBvZiBhIGNhdA==' }), + }), + }), + new LinkedAttachment({ + name: 'image_1', + attachment: new Attachment({ + filename: 'picture-of-a-dog.png', + data: new AttachmentData({ base64: 'UGljdHVyZSBvZiBhIGRvZw==' }), + }), + }), + ], + }, + }) + const faberReplay = new ReplaySubject() + const aliceReplay = new ReplaySubject() + + faberAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(faberReplay) + aliceAgent.events.observable(ProofEventTypes.ProofStateChanged).subscribe(aliceReplay) + + const attributes = { + name: new ProofAttributeInfo({ + name: 'name', + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: definition.id, + }), + ], + }), + } + + const predicates = { + age: new ProofPredicateInfo({ + name: 'age', + predicateType: PredicateType.GreaterThanOrEqualTo, + predicateValue: 50, + restrictions: [ + new AttributeFilter({ + credentialDefinitionId: definition.id, + }), + ], + }), + } + + // eslint-disable-next-line prefer-const + let { proofRecord: faberProofRecord, requestMessage } = await faberAgent.proofs.createOutOfBandRequest( + { + name: 'test-proof-request', + requestedAttributes: attributes, + requestedPredicates: predicates, + }, + { + autoAcceptProof: AutoAcceptProof.ContentApproved, + } + ) + + const mediationRecord = await faberAgent.mediationRecipient.findDefaultMediator() + if (!mediationRecord) { + throw new Error('Faber agent has no default mediator') + } + + expect(requestMessage).toMatchObject({ + service: { + recipientKeys: [expect.any(String)], + routingKeys: mediationRecord.routingKeys, + serviceEndpoint: mediationRecord.endpoint, + }, + }) + + await aliceAgent.receiveMessage(requestMessage.toJSON()) + + await waitForProofRecordSubject(aliceReplay, { + threadId: faberProofRecord.threadId, + state: ProofState.Done, + }) + + await waitForProofRecordSubject(faberReplay, { + threadId: faberProofRecord.threadId, + state: ProofState.Done, + }) + }) })