Skip to content

Commit

Permalink
feat: allow connection invitation encoded in oob url param (#1583)
Browse files Browse the repository at this point in the history
Signed-off-by: Ariel Gentile <[email protected]>
  • Loading branch information
genaris authored Sep 23, 2023
1 parent 9732ce4 commit 9d789fa
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 31 deletions.
35 changes: 31 additions & 4 deletions packages/core/src/utils/__tests__/shortenedUrl.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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)
})
Expand Down Expand Up @@ -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)
})
})
66 changes: 39 additions & 27 deletions packages/core/src/utils/parseInvitation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, unknown>): 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.
*
Expand All @@ -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<string, unknown>
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`.'
Expand All @@ -61,18 +91,7 @@ export const oobInvitationFromShortUrl = async (response: Response): Promise<Out
if (response) {
if (response.headers.get('Content-Type')?.startsWith('application/json') && response.ok) {
const invitationJson = await response.json()
const parsedMessageType = parseMessageType(invitationJson['@type'])
if (supportsIncomingMessageType(parsedMessageType, OutOfBandInvitation.type)) {
const invitation = JsonTransformer.fromJSON(invitationJson, OutOfBandInvitation)
MessageValidator.validateSync(invitation)
return invitation
} else if (supportsIncomingMessageType(parsedMessageType, ConnectionInvitationMessage.type)) {
const invitation = JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage)
MessageValidator.validateSync(invitation)
return convertToNewInvitation(invitation)
} else {
throw new AriesFrameworkError(`Invitation with '@type' ${parsedMessageType.messageTypeUri} not supported.`)
}
return parseInvitationJson(invitationJson)
} else if (response['url']) {
// The following if else is for here for trinsic shorten urls
// Because the redirect targets a deep link the automatic redirect does not occur
Expand Down Expand Up @@ -102,15 +121,8 @@ export const parseInvitationShortUrl = async (
dependencies: AgentDependencies
): Promise<OutOfBandInvitation> => {
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']) {
Expand Down

0 comments on commit 9d789fa

Please sign in to comment.