Skip to content

Commit

Permalink
fix: handle multi use invitations (#1066)
Browse files Browse the repository at this point in the history
Signed-off-by: Bryce McMath <[email protected]>
  • Loading branch information
bryce-mcmath authored Jan 19, 2024
1 parent d716e96 commit 73a2246
Show file tree
Hide file tree
Showing 7 changed files with 310 additions and 6 deletions.
1 change: 1 addition & 0 deletions packages/legacy/core/App/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export { BifoldError } from './types/error'
export { EventTypes } from './constants'
export { didMigrateToAskar, migrateToAskar } from './utils/migration'
export { createLinkSecretIfRequired, getAgentModules } from './utils/agent'
export { removeExistingInvitationIfRequired } from './utils/helpers'

export type { AnimatedComponents } from './animated-components'
export type {
Expand Down
6 changes: 6 additions & 0 deletions packages/legacy/core/App/screens/Connection.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DidExchangeState } from '@aries-framework/core'
import { useConnectionById } from '@aries-framework/react-hooks'
import { CommonActions, useFocusEffect } from '@react-navigation/native'
import { StackScreenProps } from '@react-navigation/stack'
Expand Down Expand Up @@ -136,6 +137,11 @@ const Connection: React.FC<ConnectionProps> = ({ navigation, route }) => {
}, [state.shouldShowDelayMessage])

useEffect(() => {
// for non-connectionless, invalid connections stay in the below state
if (connection && connection.state === DidExchangeState.RequestSent) {
return
}

if (
!connectionId &&
!oobRecord &&
Expand Down
22 changes: 22 additions & 0 deletions packages/legacy/core/App/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,26 @@ export const receiveMessageFromDeepLink = async (url: string, agent: Agent | und
return message
}

/**
* Useful for multi use invitations
* @param agent an Agent instance
* @param invitationId id of invitation
*/
export const removeExistingInvitationIfRequired = async (
agent: Agent | undefined,
invitationId: string
): Promise<void> => {
try {
const oobRecord = await agent?.oob.findByReceivedInvitationId(invitationId)
if (oobRecord) {
await agent?.oob.deleteById(oobRecord.id)
}
} catch (error) {
// findByReceivedInvitationId will throw if unsuccessful but that's not a problem
// it just means there is nothing to delete
}
}

/**
*
* @param uri a URI containing a base64 encoded connection invite in the query parameter
Expand Down Expand Up @@ -843,7 +863,9 @@ export const connectFromInvitation = async (
// don't throw an error, will try to connect again below
}
}

if (!record) {
await removeExistingInvitationIfRequired(agent, invitation.id)
record = await agent?.oob.receiveInvitation(invitation, { reuseConnection })
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"_tags": {},
"metadata": {},
"connectionTypes": [],
"id": "43be26c3-4b24-4915-9a84-367c5aac5bca",
"createdAt": "2023-06-30T18:44:29.268Z",
"did": "did:peer:1zQmYDZQFXdHuZam9e1kNtEevQjL9boEBGszBDGX2N8X8xeQ",
"invitationDid": "did:peer:2.SeyJzIjoiaHR0cHM6Ly90cmFjdGlvbi1kdHMtYWNhcHktZGV2LmFwcHMuc2lsdmVyLmRldm9wcy5nb3YuYmMuY2EiLCJ0IjoiZGlkLWNvbW11bmljYXRpb24iLCJwcmlvcml0eSI6MCwicmVjaXBpZW50S2V5cyI6WyJkaWQ6a2V5Ono2TWtuY3B2aG9GUXdnendyQTNRQXk3a1ZkYlB5d0I5c1FzUjFuTGszN3VKVmltSCN6Nk1rbmNwdmhvRlF3Z3p3ckEzUUF5N2tWZGJQeXdCOXNRc1IxbkxrMzd1SlZpbUgiXX0",
"theirLabel": "BestBC College",
"state": "response-received",
"role": "requester",
"autoAcceptConnection": true,
"threadId": "5f35f941-854a-4f76-ad51-5ef7f16b238c",
"mediatorId": "5497adc9-01d4-4f39-a9a8-b947dc662d5d",
"protocol": "https://didcomm.org/connections/1.0",
"outOfBandId": "99c18620-ec05-4b81-b569-a8065d62263f",
"updatedAt": "2023-06-30T18:44:29.272Z"
}
28 changes: 26 additions & 2 deletions packages/legacy/core/__tests__/screens/Connection.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const proofNotifPath = path.join(__dirname, '../fixtures/proof-notif.json')
const proofNotif = JSON.parse(fs.readFileSync(proofNotifPath, 'utf8'))
const connectionPath = path.join(__dirname, '../fixtures/connection-v1.json')
const connection = JSON.parse(fs.readFileSync(connectionPath, 'utf8'))
const connectionResponseReceivedPath = path.join(__dirname, '../fixtures/connection-v1-response-received.json')
const connectionResponseReceived = JSON.parse(fs.readFileSync(connectionResponseReceivedPath, 'utf8'))
const outOfBandInvitation = { goalCode: 'aries.vc.verify.once' }
const props = { params: { connectionId: connection.id } }

Expand Down Expand Up @@ -134,7 +136,7 @@ describe('ConnectionModal Component', () => {
expect(tree).toMatchSnapshot()
})

test('Connection with no goal code', async () => {
test('Connection with no goal code, request-sent state', async () => {
const navigation = useNavigation()
// @ts-ignore-next-line
useNotifications.mockReturnValue({ total: 1, notifications: [proofNotif] })
Expand All @@ -152,11 +154,33 @@ describe('ConnectionModal Component', () => {

const tree = render(element)

expect(tree).toMatchSnapshot()
expect(navigation.getParent()?.dispatch).not.toBeCalled()
})

test('Connection with no goal code, response-received state', async () => {
const navigation = useNavigation()
// @ts-ignore-next-line
useNotifications.mockReturnValue({ total: 1, notifications: [proofNotif] })
// @ts-ignore-next-line
useOutOfBandByConnectionId.mockReturnValue({ outOfBandInvitation: {} })
// @ts-ignore-next-line
useConnectionById.mockReturnValue(connectionResponseReceived)
// @ts-ignore-next-line
useProofById.mockReturnValue(proofNotif)
const element = (
<ConfigurationContext.Provider value={configurationContext}>
<ConnectionModal navigation={useNavigation()} route={props as any} />
</ConfigurationContext.Provider>
)

const tree = render(element)

expect(tree).toMatchSnapshot()
expect(navigation.getParent()?.dispatch).toBeCalledTimes(1)
expect(CommonActions.reset).toBeCalledWith({
index: 1,
routes: [{ name: 'Tab Stack' }, { name: 'Chat', params: { connectionId: connection.id } }],
routes: [{ name: 'Tab Stack' }, { name: 'Chat', params: { connectionId: connectionResponseReceived.id } }],
})
})

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,195 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ConnectionModal Component Connection with no goal code 1`] = `
exports[`ConnectionModal Component Connection with no goal code, request-sent state 1`] = `
<Modal
animationType="slide"
hardwareAccelerated={false}
onRequestClose={[Function]}
transparent={true}
visible={true}
>
<RNCSafeAreaView
style={
Object {
"backgroundColor": "#000000",
}
}
>
<RCTScrollView
style={
Array [
Object {
"backgroundColor": "#000000",
"height": "100%",
"padding": 20,
},
]
}
>
<View>
<View
style={
Array [
Object {
"alignItems": "center",
},
]
}
>
<Text
style={
Array [
Object {
"color": "#FFFFFF",
"fontSize": 26,
"fontWeight": "bold",
},
Object {
"fontWeight": "normal",
"marginTop": 30,
"textAlign": "center",
},
]
}
testID="com.ariesbifold:id/CredentialOnTheWay"
>
Connection.JustAMoment
</Text>
</View>
<View
style={
Array [
Object {
"marginTop": 20,
},
]
}
>
<View
style={
Object {
"alignItems": "center",
"justifyContent": "center",
}
}
>
<
fill="#FFFFFF"
height={130}
style={
Object {
"position": "absolute",
}
}
width={130}
/>
<View
collapsable={false}
style={
Object {
"transform": Array [
Object {
"rotate": "0deg",
},
],
}
}
>
<
fill="#FFFFFF"
height={250}
width={250}
/>
</View>
</View>
</View>
</View>
</RCTScrollView>
<View
style={
Array [
Object {
"margin": 20,
"marginTop": "auto",
},
]
}
>
<View
accessibilityLabel="Loading.BackToHome"
accessibilityRole="button"
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"disabled": false,
"expanded": undefined,
"selected": undefined,
}
}
accessibilityValue={
Object {
"max": undefined,
"min": undefined,
"now": undefined,
"text": undefined,
}
}
accessible={true}
collapsable={false}
focusable={true}
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Object {
"borderColor": "#42803E",
"borderRadius": 4,
"borderWidth": 2,
"opacity": 1,
"padding": 16,
}
}
testID="com.ariesbifold:id/BackToHome"
>
<View
style={
Object {
"alignItems": "center",
"flexDirection": "row",
"justifyContent": "center",
}
}
>
<Text
style={
Array [
Object {
"color": "#42803E",
"fontSize": 18,
"fontWeight": "bold",
"textAlign": "center",
},
false,
false,
false,
]
}
>
Loading.BackToHome
</Text>
</View>
</View>
</View>
</RNCSafeAreaView>
</Modal>
`;

exports[`ConnectionModal Component Connection with no goal code, response-received state 1`] = `
<Modal
animationType="slide"
hardwareAccelerated={false}
Expand Down
Loading

0 comments on commit 73a2246

Please sign in to comment.