Skip to content

Commit

Permalink
feat(core): d_m invitation parameter and invitation image (#456)
Browse files Browse the repository at this point in the history
Signed-off-by: Berend Sliedrecht <berend@animo.id>
  • Loading branch information
berendsliedrecht committed Sep 15, 2021
1 parent f3790c9 commit f92c322
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { validateOrReject } from 'class-validator'
import { URL } from 'url'

import { JsonEncoder } from '../../../utils/JsonEncoder'
import { JsonTransformer } from '../../../utils/JsonTransformer'
Expand Down Expand Up @@ -68,17 +69,44 @@ describe('ConnectionInvitationMessage', () => {
recipientKeys: ['recipientKeyOne', 'recipientKeyTwo'],
serviceEndpoint: 'https://example.com',
label: 'test',
imageUrl: 'test-image-path',
})

const invitationUrl = invitation.toUrl({
domain: 'example.com',
domain: 'https://example.com',
useLegacyDidSovPrefix: true,
})

const [, encodedInvitation] = invitationUrl.split('?c_i=')
const urlSearchParameters = new URL(invitationUrl).searchParams
const encodedInvitation = urlSearchParameters.get('c_i') as string
expect(JsonEncoder.fromBase64(encodedInvitation)['@type']).toBe(
'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation'
)
})
})

describe('fromUrl', () => {
it('should correctly convert a valid invitation url to a `ConnectionInvitationMessage` with `d_m` as parameter', async () => {
const invitationUrl =
'https://trinsic.studio/link/?d_m=eyJsYWJlbCI6InRlc3QiLCJpbWFnZVVybCI6Imh0dHBzOi8vdHJpbnNpY2FwaWFzc2V0cy5henVyZWVkZ2UubmV0L2ZpbGVzL2IyODhkMTE3LTNjMmMtNGFjNC05MzVhLWE1MDBkODQzYzFlOV9kMGYxN2I0OS0wNWQ5LTQ4ZDAtODJlMy1jNjg3MGI4MjNjMTUucG5nIiwic2VydmljZUVuZHBvaW50IjoiaHR0cHM6Ly9hcGkucG9ydGFsLnN0cmVldGNyZWQuaWQvYWdlbnQvTVZob1VaQjlHdUl6bVJzSTNIWUNuZHpBcXVKY1ZNdFUiLCJyb3V0aW5nS2V5cyI6WyJCaFZRdEZHdGJ4NzZhMm13Y3RQVkJuZWtLaG1iMTdtUHdFMktXWlVYTDFNaSJdLCJyZWNpcGllbnRLZXlzIjpbIkcyOVF6bXBlVXN0dUVHYzlXNzlYNnV2aUhTUTR6UlV2VWFFOHpXV2VZYjduIl0sIkBpZCI6IjgxYzZiNDUzLWNkMTUtNDQwMC04MWU5LTkwZTJjM2NhY2I1NCIsIkB0eXBlIjoiZGlkOnNvdjpCekNic05ZaE1yakhpcVpEVFVBU0hnO3NwZWMvY29ubmVjdGlvbnMvMS4wL2ludml0YXRpb24ifQ%3D%3D&orig=https://trinsic.studio/url/6dd56daf-e153-40dd-b849-2b345b6853f6'

const invitation = await ConnectionInvitationMessage.fromUrl(invitationUrl)

await expect(validateOrReject(invitation)).resolves.toBeUndefined()
})
it('should correctly convert a valid invitation url to a `ConnectionInvitationMessage` with `c_i` as parameter', async () => {
const invitationUrl =
'https://example.com?c_i=eyJAdHlwZSI6ICJkaWQ6c292OkJ6Q2JzTlloTXJqSGlxWkRUVUFTSGc7c3BlYy9jb25uZWN0aW9ucy8xLjAvaW52aXRhdGlvbiIsICJAaWQiOiAiZmM3ODFlMDItMjA1YS00NGUzLWE5ZTQtYjU1Y2U0OTE5YmVmIiwgInNlcnZpY2VFbmRwb2ludCI6ICJodHRwczovL2RpZGNvbW0uZmFiZXIuYWdlbnQuYW5pbW8uaWQiLCAibGFiZWwiOiAiQW5pbW8gRmFiZXIgQWdlbnQiLCAicmVjaXBpZW50S2V5cyI6IFsiR0hGczFQdFRabjdmYU5LRGVnMUFzU3B6QVAyQmpVckVjZlR2bjc3SnBRTUQiXX0='

const invitation = await ConnectionInvitationMessage.fromUrl(invitationUrl)

await expect(validateOrReject(invitation)).resolves.toBeUndefined()
})

it('should throw error if url does not contain `c_i` or `d_m`', async () => {
const invitationUrl = 'https://example.com?param=123'

await expect(ConnectionInvitationMessage.fromUrl(invitationUrl)).rejects.toThrowError()
})
})
})
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Transform } from 'class-transformer'
import { Equals, IsString, ValidateIf, IsArray, IsOptional, validateOrReject } from 'class-validator'
import { URL } from 'url'

import { AgentMessage } from '../../../agent/AgentMessage'
import { AriesFrameworkError } from '../../../error'
Expand All @@ -12,6 +13,7 @@ export interface InlineInvitationData {
recipientKeys: string[]
serviceEndpoint: string
routingKeys?: string[]
imageUrl?: string
}

export interface DIDInvitationData {
Expand Down Expand Up @@ -41,6 +43,7 @@ export class ConnectionInvitationMessage extends AgentMessage {
this.recipientKeys = options.recipientKeys
this.serviceEndpoint = options.serviceEndpoint
this.routingKeys = options.routingKeys
this.imageUrl = options.imageUrl
}

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
Expand Down Expand Up @@ -85,6 +88,10 @@ export class ConnectionInvitationMessage extends AgentMessage {
@ValidateIf((o: ConnectionInvitationMessage) => o.did === undefined)
public routingKeys?: string[]

@IsOptional()
@IsString()
public imageUrl?: string

/**
* Create an invitation url from this instance
*
Expand All @@ -101,23 +108,30 @@ export class ConnectionInvitationMessage extends AgentMessage {
}

/**
* Create a `ConnectionInvitationMessage` instance from the `c_i` parameter of an URL
* Create a `ConnectionInvitationMessage` instance from the `c_i` or `d_m` parameter of an URL
*
* @param invitationUrl invitation url containing c_i parameter
* @param invitationUrl invitation url containing c_i or d_m parameter
*
* @throws Error when url can not be decoded to JSON, or decoded message is not a valid `ConnectionInvitationMessage`
* @throws Error when the url does not contain c_i or d_m as parameter
*/
public static async fromUrl(invitationUrl: string) {
// TODO: properly extract c_i param from invitation URL
const [, encodedInvitation] = invitationUrl.split('c_i=')
const invitationJson = JsonEncoder.fromBase64(encodedInvitation)
const urlSearchParameters = new URL(invitationUrl).searchParams
const encodedInvitation = urlSearchParameters.get('c_i') ?? urlSearchParameters.get('d_m')

const invitation = JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage)
if (encodedInvitation) {
const invitationJson = JsonEncoder.fromBase64(encodedInvitation)
const invitation = JsonTransformer.fromJSON(invitationJson, ConnectionInvitationMessage)

// TODO: should validation happen here?
await validateOrReject(invitation)
// TODO: should validation happen here?
await validateOrReject(invitation)

return invitation
return invitation
} else {
throw new AriesFrameworkError(
'InvitationUrl is invalid. It needs to contain one of the following parameters; `c_i` or `d_m`'
)
}
}
}

Expand Down
17 changes: 9 additions & 8 deletions packages/react-native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,23 @@
"dependencies": {
"@aries-framework/core": "*",
"@azure/core-asynciterator-polyfill": "^1.0.0",
"events": "^3.3.0"
"events": "^3.3.0",
"react-native-url-polyfill": "^1.3.0"
},
"devDependencies": {
"@types/react-native": "^0.64.10",
"@types/indy-sdk-react-native": "npm:@types/indy-sdk@^1.16.6",
"@types/react-native": "^0.64.10",
"indy-sdk-react-native": "^0.1.13",
"react": "17.0.1",
"react-native": "0.64.2",
"rimraf": "~3.0.2",
"typescript": "~4.3.0",
"indy-sdk-react-native": "^0.1.13",
"react-native-fs": "^2.18.0",
"react-native-get-random-values": "^1.7.0",
"react-native-fs": "^2.18.0"
"rimraf": "~3.0.2",
"typescript": "~4.3.0"
},
"peerDependencies": {
"indy-sdk-react-native": "^0.1.13",
"react-native-get-random-values": "^1.7.0",
"react-native-fs": "^2.18.0"
"react-native-fs": "^2.18.0",
"react-native-get-random-values": "^1.7.0"
}
}
1 change: 1 addition & 0 deletions packages/react-native/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'react-native-get-random-values'
import '@azure/core-asynciterator-polyfill'
import 'react-native-url-polyfill/auto'

import type { AgentDependencies } from '@aries-framework/core'

Expand Down
24 changes: 24 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3088,6 +3088,14 @@ buffer-from@1.x, buffer-from@^1.0.0:
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==

buffer@^5.4.3:
version "5.7.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
dependencies:
base64-js "^1.3.1"
ieee754 "^1.1.13"

buffer@^6.0.0, buffer@^6.0.2, buffer@^6.0.3:
version "6.0.3"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
Expand Down Expand Up @@ -8298,6 +8306,13 @@ react-native-get-random-values@^1.7.0:
dependencies:
fast-base64-decode "^1.0.0"

react-native-url-polyfill@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/react-native-url-polyfill/-/react-native-url-polyfill-1.3.0.tgz#c1763de0f2a8c22cc3e959b654c8790622b6ef6a"
integrity sha512-w9JfSkvpqqlix9UjDvJjm1EjSt652zVQ6iwCIj1cVVkwXf4jQhQgTNXY6EVTwuAmUjg6BC6k9RHCBynoLFo3IQ==
dependencies:
whatwg-url-without-unicode "8.0.0-3"

react-native@0.64.2:
version "0.64.2"
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.64.2.tgz#233b6ed84ac4749c8bc2a2d6cf63577a1c437d18"
Expand Down Expand Up @@ -10058,6 +10073,15 @@ whatwg-mimetype@^2.3.0:
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==

whatwg-url-without-unicode@8.0.0-3:
version "8.0.0-3"
resolved "https://registry.yarnpkg.com/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz#ab6df4bf6caaa6c85a59f6e82c026151d4bb376b"
integrity sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==
dependencies:
buffer "^5.4.3"
punycode "^2.1.1"
webidl-conversions "^5.0.0"

whatwg-url@^8.0.0, whatwg-url@^8.4.0, whatwg-url@^8.5.0:
version "8.7.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77"
Expand Down

0 comments on commit f92c322

Please sign in to comment.