diff --git a/packages/core/src/utils/__tests__/shortenedUrl.test.ts b/packages/core/src/utils/__tests__/shortenedUrl.test.ts index 55180a141d..ecdd0fff5c 100644 --- a/packages/core/src/utils/__tests__/shortenedUrl.test.ts +++ b/packages/core/src/utils/__tests__/shortenedUrl.test.ts @@ -3,7 +3,7 @@ import type { Response } from 'node-fetch' import { Headers } from 'node-fetch' import { ConnectionInvitationMessage } from '../../modules/connections' -import { OutOfBandInvitation } from '../../modules/oob' +import { InvitationType, OutOfBandInvitation } from '../../modules/oob' import { convertToNewInvitation } from '../../modules/oob/helpers' import { JsonTransformer } from '../JsonTransformer' import { MessageValidator } from '../MessageValidator' @@ -80,9 +80,10 @@ let connectionInvitationMock: ConnectionInvitationMessage let connectionInvitationToNew: OutOfBandInvitation beforeAll(async () => { - outOfBandInvitationMock = await JsonTransformer.fromJSON(mockOobInvite, OutOfBandInvitation) + outOfBandInvitationMock = JsonTransformer.fromJSON(mockOobInvite, OutOfBandInvitation) + outOfBandInvitationMock.invitationType = InvitationType.OutOfBand MessageValidator.validateSync(outOfBandInvitationMock) - connectionInvitationMock = await JsonTransformer.fromJSON(mockConnectionInvite, ConnectionInvitationMessage) + connectionInvitationMock = JsonTransformer.fromJSON(mockConnectionInvite, ConnectionInvitationMessage) MessageValidator.validateSync(connectionInvitationMock) connectionInvitationToNew = convertToNewInvitation(connectionInvitationMock) }) @@ -113,8 +114,34 @@ describe('shortened urls resolving to connection invitations', () => { const short = await oobInvitationFromShortUrl(mockedResponseConnectionJson) expect(short).toEqual(connectionInvitationToNew) }) - test('Resolve a mocked Response in the form of a connection invitation encoded in an url', async () => { + test('Resolve a mocked Response in the form of a connection invitation encoded in an url c_i query parameter', async () => { const short = await oobInvitationFromShortUrl(mockedResponseConnectionUrl) expect(short).toEqual(connectionInvitationToNew) }) + test('Resolve a mocked Response in the form of a connection invitation encoded in an url oob query parameter', async () => { + const mockedResponseConnectionInOobUrl = { + status: 200, + ok: true, + headers: dummyHeader, + url: 'https://oob.lissi.io/ssi?oob=eyJAdHlwZSI6ImRpZDpzb3Y6QnpDYnNOWWhNcmpIaXFaRFRVQVNIZztzcGVjL2Nvbm5lY3Rpb25zLzEuMC9pbnZpdGF0aW9uIiwiQGlkIjoiMGU0NmEzYWEtMzUyOC00OTIxLWJmYjItN2JjYjk0NjVjNjZjIiwibGFiZWwiOiJTdGFkdCB8IExpc3NpLURlbW8iLCJzZXJ2aWNlRW5kcG9pbnQiOiJodHRwczovL2RlbW8tYWdlbnQuaW5zdGl0dXRpb25hbC1hZ2VudC5saXNzaS5pZC9kaWRjb21tLyIsImltYWdlVXJsIjoiaHR0cHM6Ly9yb3V0aW5nLmxpc3NpLmlvL2FwaS9JbWFnZS9kZW1vTXVzdGVyaGF1c2VuIiwicmVjaXBpZW50S2V5cyI6WyJEZlcxbzM2ekxuczlVdGlDUGQyalIyS2pvcnRvZkNhcFNTWTdWR2N2WEF6aCJdfQ', + } as Response + + mockedResponseConnectionInOobUrl.headers = dummyHeader + + const expectedOobMessage = convertToNewInvitation( + JsonTransformer.fromJSON( + { + '@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation', + '@id': '0e46a3aa-3528-4921-bfb2-7bcb9465c66c', + label: 'Stadt | Lissi-Demo', + serviceEndpoint: 'https://demo-agent.institutional-agent.lissi.id/didcomm/', + imageUrl: 'https://routing.lissi.io/api/Image/demoMusterhausen', + recipientKeys: ['DfW1o36zLns9UtiCPd2jR2KjortofCapSSY7VGcvXAzh'], + }, + ConnectionInvitationMessage + ) + ) + const short = await oobInvitationFromShortUrl(mockedResponseConnectionInOobUrl) + expect(short).toEqual(expectedOobMessage) + }) }) diff --git a/packages/core/src/utils/parseInvitation.ts b/packages/core/src/utils/parseInvitation.ts index 8913d96070..e9ba61e4c5 100644 --- a/packages/core/src/utils/parseInvitation.ts +++ b/packages/core/src/utils/parseInvitation.ts @@ -35,6 +35,36 @@ const fetchShortUrl = async (invitationUrl: string, dependencies: AgentDependenc return response } +/** + * Parses a JSON containing an invitation message and returns an OutOfBandInvitation instance + * + * @param invitationJson JSON object containing message + * @returns OutOfBandInvitation + */ +export const parseInvitationJson = (invitationJson: Record): OutOfBandInvitation => { + const messageType = invitationJson['@type'] as string + + if (!messageType) { + throw new AriesFrameworkError('Invitation is not a valid DIDComm message') + } + + const parsedMessageType = parseMessageType(messageType) + if (supportsIncomingMessageType(parsedMessageType, OutOfBandInvitation.type)) { + const invitation = JsonTransformer.fromJSON(invitationJson, OutOfBandInvitation) + MessageValidator.validateSync(invitation) + invitation.invitationType = InvitationType.OutOfBand + return invitation + } else if (supportsIncomingMessageType(parsedMessageType, ConnectionInvitationMessage.type)) { + const invitation = JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage) + MessageValidator.validateSync(invitation) + const outOfBandInvitation = convertToNewInvitation(invitation) + outOfBandInvitation.invitationType = InvitationType.Connection + return outOfBandInvitation + } else { + throw new AriesFrameworkError(`Invitation with '@type' ${parsedMessageType.messageTypeUri} not supported.`) + } +} + /** * Parses URL containing encoded invitation and returns invitation message. * @@ -44,12 +74,12 @@ const fetchShortUrl = async (invitationUrl: string, dependencies: AgentDependenc */ export const parseInvitationUrl = (invitationUrl: string): OutOfBandInvitation => { const parsedUrl = parseUrl(invitationUrl).query - if (parsedUrl['oob']) { - const outOfBandInvitation = OutOfBandInvitation.fromUrl(invitationUrl) - return outOfBandInvitation - } else if (parsedUrl['c_i'] || parsedUrl['d_m']) { - const invitation = ConnectionInvitationMessage.fromUrl(invitationUrl) - return convertToNewInvitation(invitation) + + const encodedInvitation = parsedUrl['oob'] ?? parsedUrl['c_i'] ?? parsedUrl['d_m'] + + if (typeof encodedInvitation === 'string') { + const invitationJson = JsonEncoder.fromBase64(encodedInvitation) as Record + return parseInvitationJson(invitationJson) } throw new AriesFrameworkError( 'InvitationUrl is invalid. It needs to contain one, and only one, of the following parameters: `oob`, `c_i` or `d_m`.' @@ -61,18 +91,7 @@ export const oobInvitationFromShortUrl = async (response: Response): Promise => { const parsedUrl = parseUrl(invitationUrl).query - if (parsedUrl['oob']) { - const outOfBandInvitation = OutOfBandInvitation.fromUrl(invitationUrl) - outOfBandInvitation.invitationType = InvitationType.OutOfBand - return outOfBandInvitation - } else if (parsedUrl['c_i']) { - const invitation = ConnectionInvitationMessage.fromUrl(invitationUrl) - const outOfBandInvitation = convertToNewInvitation(invitation) - outOfBandInvitation.invitationType = InvitationType.Connection - return outOfBandInvitation + if (parsedUrl['oob'] || parsedUrl['c_i']) { + return parseInvitationUrl(invitationUrl) } // Legacy connectionless invitation else if (parsedUrl['d_m']) {