@docknetwork/wallet-sdk-core 0.4.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +39 -0
- package/babel.config.js +16 -0
- package/jest.config.ts +39 -0
- package/lib/account-provider.d.ts +8 -0
- package/lib/account-provider.d.ts.map +1 -0
- package/lib/account-provider.js +15 -0
- package/lib/account-provider.js.map +1 -0
- package/lib/biometric-provider.d.ts +29 -0
- package/lib/biometric-provider.d.ts.map +1 -0
- package/lib/biometric-provider.js +54 -0
- package/lib/biometric-provider.js.map +1 -0
- package/lib/credential-provider.d.ts +58 -0
- package/lib/credential-provider.d.ts.map +1 -0
- package/lib/credential-provider.js +198 -0
- package/lib/credential-provider.js.map +1 -0
- package/lib/did-provider.d.ts +79 -0
- package/lib/did-provider.d.ts.map +1 -0
- package/lib/did-provider.js +215 -0
- package/lib/did-provider.js.map +1 -0
- package/lib/ecosystem-tools.d.ts +11 -0
- package/lib/ecosystem-tools.d.ts.map +1 -0
- package/lib/ecosystem-tools.js +33 -0
- package/lib/ecosystem-tools.js.map +1 -0
- package/lib/helpers.d.ts +8 -0
- package/lib/helpers.d.ts.map +1 -0
- package/lib/helpers.js +63 -0
- package/lib/helpers.js.map +1 -0
- package/lib/message-provider.d.ts +36 -0
- package/lib/message-provider.d.ts.map +1 -0
- package/lib/message-provider.js +172 -0
- package/lib/message-provider.js.map +1 -0
- package/lib/messages/message-helpers.d.ts +110 -0
- package/lib/messages/message-helpers.d.ts.map +1 -0
- package/lib/messages/message-helpers.js +106 -0
- package/lib/messages/message-helpers.js.map +1 -0
- package/lib/network-resolver.d.ts +19 -0
- package/lib/network-resolver.d.ts.map +1 -0
- package/lib/network-resolver.js +80 -0
- package/lib/network-resolver.js.map +1 -0
- package/lib/types.d.ts +63 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +3 -0
- package/lib/types.js.map +1 -0
- package/lib/v1-helpers.d.ts +20 -0
- package/lib/v1-helpers.d.ts.map +1 -0
- package/lib/v1-helpers.js +147 -0
- package/lib/v1-helpers.js.map +1 -0
- package/lib/verification-controller.d.ts +48 -0
- package/lib/verification-controller.d.ts.map +1 -0
- package/lib/verification-controller.js +219 -0
- package/lib/verification-controller.js.map +1 -0
- package/lib/wallet-to-wallet-verification/walletToWalletVerificationProvider.d.ts +20 -0
- package/lib/wallet-to-wallet-verification/walletToWalletVerificationProvider.d.ts.map +1 -0
- package/lib/wallet-to-wallet-verification/walletToWalletVerificationProvider.js +140 -0
- package/lib/wallet-to-wallet-verification/walletToWalletVerificationProvider.js.map +1 -0
- package/lib/wallet-wasm.d.ts +10 -0
- package/lib/wallet-wasm.d.ts.map +1 -0
- package/lib/wallet-wasm.js +62 -0
- package/lib/wallet-wasm.js.map +1 -0
- package/lib/wallet.d.ts +11 -0
- package/lib/wallet.d.ts.map +1 -0
- package/lib/wallet.js +173 -0
- package/lib/wallet.js.map +1 -0
- package/package.json +34 -0
- package/setup-tests.ts +1 -0
- package/src/account-provider.ts +18 -0
- package/src/biometric-provider.ts +82 -0
- package/src/credential-provider.test.ts +164 -0
- package/src/credential-provider.ts +272 -0
- package/src/did-provider.test.ts +203 -0
- package/src/did-provider.ts +263 -0
- package/src/ecosystem-tools.ts +37 -0
- package/src/fixtures/any-credential-proof-request.json +30 -0
- package/src/fixtures/biometrics-credential-bbs-revocation.json +62 -0
- package/src/fixtures/customer-credential.json +39 -0
- package/src/fixtures/iiw-credential.json +59 -0
- package/src/fixtures/iiw-template.json +33 -0
- package/src/fixtures/university-degree-bbs.json +57 -0
- package/src/fixtures/university-degree-proof-request.json +32 -0
- package/src/helpers.ts +61 -0
- package/src/message-provider.test.ts +115 -0
- package/src/message-provider.ts +221 -0
- package/src/messages/message-helpers.ts +125 -0
- package/src/messages/relay-service-mocks/demo-app-messages.json +450 -0
- package/src/network-resolver.test.ts +142 -0
- package/src/network-resolver.ts +122 -0
- package/src/types.ts +75 -0
- package/src/v1-helpers.ts +160 -0
- package/src/verification-controller.test.ts +149 -0
- package/src/verification-controller.ts +276 -0
- package/src/wallet-to-wallet-verification/walletToWalletVerificationProvider.ts +216 -0
- package/src/wallet-wasm.ts +74 -0
- package/src/wallet.test.ts +72 -0
- package/src/wallet.ts +211 -0
- package/tsconfig.build.json +26 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +30 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@context": [
|
|
3
|
+
"https://www.w3.org/2018/credentials/v1",
|
|
4
|
+
{
|
|
5
|
+
"dk": "https://ld.dock.io/credentials#",
|
|
6
|
+
"UniversityDegree": "dk:UniversityDegree",
|
|
7
|
+
"degreeName": "dk:degreeName",
|
|
8
|
+
"degreeType": "dk:degreeType",
|
|
9
|
+
"dateEarned": "dk:dateEarned",
|
|
10
|
+
"name": "dk:name",
|
|
11
|
+
"email": "dk:email",
|
|
12
|
+
"dateOfBirth": "dk:dateOfBirth",
|
|
13
|
+
"description": "dk:description",
|
|
14
|
+
"logo": "dk:logo"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"id": "https://creds-testnet.dock.io/cc2e0be92a9533ca68466ad011ea916216fedac6473d17b1bd0fc3a01b623b09",
|
|
18
|
+
"type": [
|
|
19
|
+
"VerifiableCredential",
|
|
20
|
+
"UniversityDegree"
|
|
21
|
+
],
|
|
22
|
+
"credentialSubject": {
|
|
23
|
+
"id": "recipient id",
|
|
24
|
+
"degreeName": "degree name",
|
|
25
|
+
"degreeType": "degree type",
|
|
26
|
+
"dateEarned": "2023-02-07",
|
|
27
|
+
"name": "full name",
|
|
28
|
+
"email": "email@email.com",
|
|
29
|
+
"dateOfBirth": "2023-02-08"
|
|
30
|
+
},
|
|
31
|
+
"issuanceDate": "2023-02-06T11:15:19.995Z",
|
|
32
|
+
"name": "University Degree",
|
|
33
|
+
"proof": {
|
|
34
|
+
"@context": [
|
|
35
|
+
{
|
|
36
|
+
"sec": "https://w3id.org/security#",
|
|
37
|
+
"proof": {
|
|
38
|
+
"@id": "sec:proof",
|
|
39
|
+
"@type": "@id",
|
|
40
|
+
"@container": "@graph"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"https://ld.dock.io/security/bbs/v1"
|
|
44
|
+
],
|
|
45
|
+
"type": "Bls12381BBS+SignatureDock2022",
|
|
46
|
+
"created": "2023-02-06T11:42:24Z",
|
|
47
|
+
"verificationMethod": "did:dock:5GAFgFX6dUMYzZKiRKHvV4nK92X64D5ekfy3WVpNEeMUnu38#keys-2",
|
|
48
|
+
"proofPurpose": "assertionMethod",
|
|
49
|
+
"proofValue": "znokfKbFLiBqTjuZVkg3FGftrogKGm47DKDSNRjwqd8SBQiw7iqeYpTdapL9cRGhax4R9pVwrDicWrm4P9fAmAAVjSHz79jZGnX9AVWesbn7HFssbguZgx2ZzXS6ySyfPfDa8qhUuMsepV6oDcydhEA4Hd"
|
|
50
|
+
},
|
|
51
|
+
"issuer": {
|
|
52
|
+
"name": "My New DID",
|
|
53
|
+
"description": "",
|
|
54
|
+
"logo": "",
|
|
55
|
+
"id": "did:dock:5GAFgFX6dUMYzZKiRKHvV4nK92X64D5ekfy3WVpNEeMUnu38"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"qr": "https://creds-testnet.dock.io/proof/6de279ba-caf3-4979-a067-553284b40767",
|
|
3
|
+
"id": "6de279ba-caf3-4979-a067-553284b40767",
|
|
4
|
+
"name": "University Degree Credential",
|
|
5
|
+
"nonce": "b75fab9a5006216173500bfd3df5d0c5",
|
|
6
|
+
"created": "2023-08-09T20:19:46.278Z",
|
|
7
|
+
"updated": "2023-08-09T20:19:46.278Z",
|
|
8
|
+
"verified": false,
|
|
9
|
+
"response_url": "https://api-testnet.dock.io/proof-requests/6de279ba-caf3-4979-a067-553284b40767/send-presentation",
|
|
10
|
+
"request": {
|
|
11
|
+
"id": "6de279ba-caf3-4979-a067-553284b40767",
|
|
12
|
+
"input_descriptors": [
|
|
13
|
+
{
|
|
14
|
+
"id": "Credential 1",
|
|
15
|
+
"name": "University Degree Credential",
|
|
16
|
+
"purpose": "Any University Degree Credential",
|
|
17
|
+
"constraints": {
|
|
18
|
+
"fields": [
|
|
19
|
+
{
|
|
20
|
+
"path": ["$.name"],
|
|
21
|
+
"filter": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"pattern": "University Degree"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
"type": "proof-request"
|
|
32
|
+
}
|
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import assert from 'assert';
|
|
3
|
+
|
|
4
|
+
// Sentry implementation will be injected by the wallet-app
|
|
5
|
+
let sentryCaptureException: any = error => {
|
|
6
|
+
console.error(error);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export function setSentryCaptureException(impl: any) {
|
|
10
|
+
sentryCaptureException = impl;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function captureException(error) {
|
|
14
|
+
if (sentryCaptureException) {
|
|
15
|
+
sentryCaptureException(error);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
export const WalletDocumentTypes = {
|
|
21
|
+
// This is used to store encrypted DIDComm messasges recieved from relay service
|
|
22
|
+
DIDCommMessage: 'DIDCommMessage',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function isURL(str) {
|
|
26
|
+
try {
|
|
27
|
+
// eslint-disable-next-line no-new
|
|
28
|
+
new URL(str);
|
|
29
|
+
return true;
|
|
30
|
+
} catch (err) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getJSONFromURL(url: string) {
|
|
36
|
+
return axios
|
|
37
|
+
.get(url)
|
|
38
|
+
.then(res => res.data)
|
|
39
|
+
.catch(err => {
|
|
40
|
+
console.error(`Error fetching ${url}`, err);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function getJSON(jsonOrURL: string | any) {
|
|
45
|
+
assert(!!jsonOrURL, 'jsonOrURL is required');
|
|
46
|
+
|
|
47
|
+
if (typeof jsonOrURL === 'object') {
|
|
48
|
+
return jsonOrURL;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (typeof jsonOrURL === 'string') {
|
|
52
|
+
if (isURL(jsonOrURL)) {
|
|
53
|
+
console.log(`Fetching ${jsonOrURL}`);
|
|
54
|
+
return getJSONFromURL(jsonOrURL);
|
|
55
|
+
} else {
|
|
56
|
+
return JSON.parse(jsonOrURL);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
throw new Error(`Invalid data ${jsonOrURL}`);
|
|
61
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {IDIDProvider, createDIDProvider} from './did-provider';
|
|
2
|
+
import {IMessageProvider, createMessageProvider} from './message-provider';
|
|
3
|
+
import {IWallet, createWallet} from './wallet';
|
|
4
|
+
|
|
5
|
+
describe('MessageProvider', () => {
|
|
6
|
+
let messageProvider: IMessageProvider;
|
|
7
|
+
let wallet: IWallet;
|
|
8
|
+
let didProvider: IDIDProvider;
|
|
9
|
+
let relayService: any;
|
|
10
|
+
let didCommMessages = [
|
|
11
|
+
{
|
|
12
|
+
_id: '651e965410fc3fcfffdd17f1',
|
|
13
|
+
to: 'did:key:z6MkoQzWru66w91EH7U9Xsv5eYXQabw9U3ZJd5GkatMWkmZT',
|
|
14
|
+
from: 'did:key:z6MkoQzWru66w91EH7U9Xsv5eYXQabw9U3ZJd5GkatMWkmZT',
|
|
15
|
+
msg: '<encrypted-message>',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
_id: '951e965410fc3fcfffdd17f1',
|
|
19
|
+
to: 'did:key:z6MkoQzWru66w91EH7U9Xsv5eYXQabw9U3ZJd5GkatMWkmZT',
|
|
20
|
+
from: 'did:key:z6MkoQzWru66w91EH7U9Xsv5eYXQabw9U3ZJd5GkatMWkmZT',
|
|
21
|
+
msg: '<encrypted-message>',
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
beforeEach(async () => {
|
|
26
|
+
relayService = {
|
|
27
|
+
sendMessage: jest.fn(),
|
|
28
|
+
getMessages: jest.fn().mockResolvedValue(didCommMessages),
|
|
29
|
+
resolveDidcommMessage: jest.fn().mockImplementation(({ message }) => message),
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
wallet = await createWallet({
|
|
33
|
+
databasePath: ':memory:',
|
|
34
|
+
});
|
|
35
|
+
didProvider = createDIDProvider({
|
|
36
|
+
wallet,
|
|
37
|
+
});
|
|
38
|
+
didProvider.getDIDKeyPairs = jest.fn().mockResolvedValue([{controller: "someDid"}])
|
|
39
|
+
messageProvider = createMessageProvider({
|
|
40
|
+
wallet,
|
|
41
|
+
didProvider,
|
|
42
|
+
relayService,
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should fetch messages and store encrypted data in the wallet', async () => {
|
|
47
|
+
const messages = await messageProvider.fetchMessages();
|
|
48
|
+
expect(messages).toEqual(didCommMessages);
|
|
49
|
+
|
|
50
|
+
for(let message of didCommMessages) {
|
|
51
|
+
const walletMessage = await wallet.getDocumentById(message._id);
|
|
52
|
+
expect(walletMessage.encryptedMessage).toEqual(message);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should decrypt encrypted messages in the wallet', async () => {
|
|
57
|
+
jest.spyOn(wallet.eventManager, 'emit').mockReset();
|
|
58
|
+
|
|
59
|
+
await messageProvider.fetchMessages()
|
|
60
|
+
await messageProvider.processDIDCommMessages();
|
|
61
|
+
|
|
62
|
+
expect(wallet.eventManager.emit).toHaveBeenCalledWith('didcomm-messages-received', didCommMessages);
|
|
63
|
+
expect(wallet.eventManager.emit).toHaveBeenCalledWith('didcomm-message-decrypted', {"decryptedMessage": didCommMessages[0], "messageId": didCommMessages[0]._id});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
it('should handle errors when fetching messages', async () => {
|
|
68
|
+
relayService.getMessages.mockRejectedValue(new Error('Fetching failed'));
|
|
69
|
+
|
|
70
|
+
await expect(messageProvider.fetchMessages()).rejects.toThrow('Failed to fetch messages: Fetching failed');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should handle errors when sending messages', async () => {
|
|
74
|
+
relayService.sendMessage.mockRejectedValue(new Error('Sending failed'));
|
|
75
|
+
|
|
76
|
+
await expect(
|
|
77
|
+
messageProvider.sendMessage({
|
|
78
|
+
did: 'someDid',
|
|
79
|
+
message: 'someMessage',
|
|
80
|
+
recipientDid: 'recipientDid',
|
|
81
|
+
})
|
|
82
|
+
).rejects.toThrow('Failed to send message: Sending failed');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// it('should handle errors when processing DIDComm messages', async () => {
|
|
86
|
+
// relayService.resolveDidcommMessage.mockRejectedValue(new Error('Decryption failed'));
|
|
87
|
+
|
|
88
|
+
// await expect(messageProvider.processDIDCommMessages()).rejects.toThrow('Failed to process DIDComm messages: Decryption failed');
|
|
89
|
+
// });
|
|
90
|
+
|
|
91
|
+
it('should skip did that fail lookup when processing DIDComm messages', async () => {
|
|
92
|
+
jest.spyOn(wallet.eventManager, 'emit').mockReset();
|
|
93
|
+
const messageProvider2 = createMessageProvider({
|
|
94
|
+
wallet,
|
|
95
|
+
didProvider,
|
|
96
|
+
relayService: {
|
|
97
|
+
...relayService,
|
|
98
|
+
resolveDidcommMessage: jest.fn().mockRejectedValueOnce(new Error('A DID document lookup was successful, but the DID in question does not exist. This is different from a network error.')).mockImplementation(({ message }) => message),
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
await messageProvider2.fetchMessages()
|
|
103
|
+
await messageProvider2.processDIDCommMessages();
|
|
104
|
+
|
|
105
|
+
expect(wallet.eventManager.emit).toHaveBeenCalledWith('didcomm-messages-received', didCommMessages);
|
|
106
|
+
expect(wallet.eventManager.emit).toHaveBeenCalledWith('didcomm-message-decrypted', {"decryptedMessage": didCommMessages[1], "messageId": didCommMessages[1]._id});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should handle errors when marking messages as read', async () => {
|
|
110
|
+
await messageProvider.fetchMessages();
|
|
111
|
+
wallet.removeDocument = jest.fn().mockRejectedValue(new Error('Removal failed'));
|
|
112
|
+
|
|
113
|
+
await expect(messageProvider.markMessageAsRead('651e965410fc3fcfffdd17f1')).rejects.toThrow('Failed to mark message as read: Removal failed');
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import {IDIDProvider} from './did-provider';
|
|
2
|
+
import {WalletDocumentTypes, captureException} from './helpers';
|
|
3
|
+
import {IWallet} from './types';
|
|
4
|
+
import {relayService as defaultRelayService} from '@docknetwork/wallet-sdk-wasm/src/services/relay-service';
|
|
5
|
+
|
|
6
|
+
const FETCH_MESSAGE_LIMIT = 10;
|
|
7
|
+
|
|
8
|
+
export interface IMessageProvider {
|
|
9
|
+
sendMessage: (message: any) => Promise<void>;
|
|
10
|
+
fetchMessages: () => Promise<void>;
|
|
11
|
+
processDIDCommMessages: () => Promise<void>;
|
|
12
|
+
startAutoFetch: (timeout?: number) => () => void;
|
|
13
|
+
addMessageListener: (handler: () => void) => () => void;
|
|
14
|
+
waitForMessage: () => Promise<any>;
|
|
15
|
+
markMessageAsRead: (messageId: string) => Promise<void>;
|
|
16
|
+
clearCache: () => Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function getKeyPairDocs(didProvider: IDIDProvider, did?: string) {
|
|
20
|
+
try {
|
|
21
|
+
const keyPairDocs = await didProvider.getDIDKeyPairs();
|
|
22
|
+
if (did) {
|
|
23
|
+
return keyPairDocs.find(doc => doc.controller === did);
|
|
24
|
+
}
|
|
25
|
+
return keyPairDocs;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
throw new Error(`Failed to get key pair docs: ${error.message}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function createMessageProvider({
|
|
32
|
+
wallet,
|
|
33
|
+
didProvider,
|
|
34
|
+
relayService = defaultRelayService,
|
|
35
|
+
}: {
|
|
36
|
+
wallet: IWallet;
|
|
37
|
+
didProvider: IDIDProvider;
|
|
38
|
+
relayService?: any;
|
|
39
|
+
}) {
|
|
40
|
+
async function markMessageAsRead(messageId: string) {
|
|
41
|
+
try {
|
|
42
|
+
const message = await wallet.getDocumentById(messageId);
|
|
43
|
+
|
|
44
|
+
if (message.type !== WalletDocumentTypes.DIDCommMessage) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`Document with id ${messageId} is not a DIDCommMessage`,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
await wallet.removeDocument(messageId);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
captureException(error);
|
|
53
|
+
throw new Error(`Failed to mark message as read: ${error.message}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function processDIDCommMessages(limit = 1) {
|
|
58
|
+
try {
|
|
59
|
+
const messages = await wallet.getDocumentsByType(
|
|
60
|
+
WalletDocumentTypes.DIDCommMessage,
|
|
61
|
+
);
|
|
62
|
+
const keyPairDocs = await getKeyPairDocs(didProvider);
|
|
63
|
+
let count = 0;
|
|
64
|
+
for (const message of messages) {
|
|
65
|
+
if (count >= limit) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
if (!message.encryptedMessage) {
|
|
70
|
+
await wallet.removeDocument(message.id);
|
|
71
|
+
throw new Error(
|
|
72
|
+
`Message with payload ${JSON.stringify(message)} is invalid`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
const decryptedMessage = await relayService.resolveDidcommMessage({
|
|
76
|
+
keyPairDocs,
|
|
77
|
+
message: message.encryptedMessage,
|
|
78
|
+
});
|
|
79
|
+
wallet.eventManager.emit('didcomm-message-decrypted', {
|
|
80
|
+
decryptedMessage,
|
|
81
|
+
messageId: message.id,
|
|
82
|
+
});
|
|
83
|
+
count++;
|
|
84
|
+
// the wallet app will call markMessageAsRead after the message is processed
|
|
85
|
+
} catch (err) {
|
|
86
|
+
if (err.message?.includes('the DID in question does not exist')) {
|
|
87
|
+
// the DID lookup failed (a testnet credential was issued to a mainnet did), so we can't
|
|
88
|
+
// decrypt the message remove the message from the wallet
|
|
89
|
+
await wallet.removeDocument(message.id);
|
|
90
|
+
}
|
|
91
|
+
captureException(err);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
} catch (error) {
|
|
95
|
+
captureException(error);
|
|
96
|
+
throw new Error(`Failed to process DIDComm messages: ${error.message}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function fetchMessages() {
|
|
101
|
+
try {
|
|
102
|
+
const keyPairDocs = await getKeyPairDocs(didProvider);
|
|
103
|
+
const encryptedMessages = await relayService.getMessages({
|
|
104
|
+
keyPairDocs,
|
|
105
|
+
limit: FETCH_MESSAGE_LIMIT,
|
|
106
|
+
skipMessageResolution: true,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (encryptedMessages.length) {
|
|
110
|
+
console.log(`Fetched ${encryptedMessages.length} messages`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
for (const message of encryptedMessages) {
|
|
114
|
+
try {
|
|
115
|
+
await wallet.addDocument({
|
|
116
|
+
id: message._id,
|
|
117
|
+
type: WalletDocumentTypes.DIDCommMessage,
|
|
118
|
+
encryptedMessage: message,
|
|
119
|
+
});
|
|
120
|
+
} catch (err) {
|
|
121
|
+
// this message will be lost if it fails to be stored in the wallet
|
|
122
|
+
captureException(err);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (encryptedMessages.length > 0) {
|
|
127
|
+
wallet.eventManager.emit(
|
|
128
|
+
'didcomm-messages-received',
|
|
129
|
+
encryptedMessages,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return encryptedMessages;
|
|
134
|
+
} catch (error) {
|
|
135
|
+
captureException(error);
|
|
136
|
+
throw new Error(`Failed to fetch messages: ${error.message}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
function addMessageListener(handler) {
|
|
142
|
+
const listener = async message => {
|
|
143
|
+
await Promise.resolve(handler(message.decryptedMessage));
|
|
144
|
+
await markMessageAsRead(message.messageId);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
wallet.eventManager.addListener('didcomm-message-decrypted', listener);
|
|
148
|
+
return () =>
|
|
149
|
+
wallet.eventManager.removeListener(
|
|
150
|
+
'didcomm-message-decrypted',
|
|
151
|
+
listener,
|
|
152
|
+
);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
let listnerIntervalId = null;
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
async sendMessage({
|
|
159
|
+
did,
|
|
160
|
+
recipientDid,
|
|
161
|
+
message,
|
|
162
|
+
// didcomm message parameters
|
|
163
|
+
from,
|
|
164
|
+
to,
|
|
165
|
+
body,
|
|
166
|
+
type,
|
|
167
|
+
}) {
|
|
168
|
+
|
|
169
|
+
// TODO: rename relay service parameters to make it easier to understand
|
|
170
|
+
if (from) {
|
|
171
|
+
did = from;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (to) {
|
|
175
|
+
recipientDid = to;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!message && body) {
|
|
179
|
+
message = body;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const keyPairDoc = await getKeyPairDocs(didProvider, did);
|
|
184
|
+
if (!keyPairDoc) {
|
|
185
|
+
throw new Error(`${did} not found in didDocs`);
|
|
186
|
+
}
|
|
187
|
+
await relayService.sendMessage({keyPairDoc, message, recipientDid, type});
|
|
188
|
+
} catch (error) {
|
|
189
|
+
captureException(error);
|
|
190
|
+
throw new Error(`Failed to send message: ${error.message}`);
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
waitForMessage() {
|
|
194
|
+
return new Promise((resolve: any) => {
|
|
195
|
+
let removeListener = addMessageListener(async (message) => {
|
|
196
|
+
removeListener();
|
|
197
|
+
await resolve(message);
|
|
198
|
+
})
|
|
199
|
+
});
|
|
200
|
+
},
|
|
201
|
+
startAutoFetch(timeout = 2000) {
|
|
202
|
+
clearInterval(listnerIntervalId);
|
|
203
|
+
listnerIntervalId = setInterval(async () => {
|
|
204
|
+
await fetchMessages();
|
|
205
|
+
await processDIDCommMessages();
|
|
206
|
+
}, timeout);
|
|
207
|
+
|
|
208
|
+
return () => clearInterval(listnerIntervalId);
|
|
209
|
+
},
|
|
210
|
+
clearCache: async () => {
|
|
211
|
+
return Promise.all((await wallet.getDocumentsByType(WalletDocumentTypes.DIDCommMessage)).map(document => {
|
|
212
|
+
markMessageAsRead(document.id);
|
|
213
|
+
return wallet.removeDocument(document.id);
|
|
214
|
+
}));
|
|
215
|
+
},
|
|
216
|
+
fetchMessages,
|
|
217
|
+
addMessageListener,
|
|
218
|
+
processDIDCommMessages,
|
|
219
|
+
markMessageAsRead,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DIDComm Message helpers
|
|
3
|
+
* Check https://identity.foundation/didcomm-messaging/spec/#out-of-band-messages for more details
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const MessageTypes = {
|
|
7
|
+
Invitation: 'https://didcomm.org/out-of-band/2.0/invitation',
|
|
8
|
+
RequestPresentation:
|
|
9
|
+
'https://didcomm.org/present-proof/1.0/request-presentation',
|
|
10
|
+
Presentation: 'https://didcomm.org/present-proof/1.0/presentation',
|
|
11
|
+
Ack: 'https://didcomm.org/ack/1.0/ack',
|
|
12
|
+
IssuerDirect: 'https://didcomm.org/issue-credential/2.0/issue-credential',
|
|
13
|
+
Basic: 'https://didcomm.org/basicmessage/1.0/message',
|
|
14
|
+
IssueWithData: 'https://didcomm.org/issue-credential/2.0/offer-credential',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const Goals = {
|
|
18
|
+
WalletToWalletVerification: 'wallet-to-wallet-verification',
|
|
19
|
+
PresentationAckFromVerifier: 'presentation-ack-from-verifier',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
*
|
|
24
|
+
* @return OOB message to start a wallet to wallet verification flow
|
|
25
|
+
* The holder will scan it as QR code and should have the context to start the verification flow
|
|
26
|
+
*
|
|
27
|
+
*/
|
|
28
|
+
export function buildVerificationFlowInviteMessage({verifierDID, proofRequestId}) {
|
|
29
|
+
return {
|
|
30
|
+
type: MessageTypes.Invitation,
|
|
31
|
+
from: verifierDID,
|
|
32
|
+
body: {
|
|
33
|
+
goal_code: Goals.WalletToWalletVerification,
|
|
34
|
+
goal: 'Invite for a Wallet to Wallet Verification',
|
|
35
|
+
proofRequestId: proofRequestId,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Sender: Verifier
|
|
42
|
+
* OOB message to request a verifiable presentation from the holder
|
|
43
|
+
*/
|
|
44
|
+
export function buildRequestVerifiablePresentationMessage({
|
|
45
|
+
verifierDID,
|
|
46
|
+
holderDID,
|
|
47
|
+
proofRequest,
|
|
48
|
+
proofRequestId
|
|
49
|
+
}) {
|
|
50
|
+
return {
|
|
51
|
+
type: MessageTypes.RequestPresentation,
|
|
52
|
+
from: verifierDID,
|
|
53
|
+
to: holderDID,
|
|
54
|
+
body: {
|
|
55
|
+
proofRequest,
|
|
56
|
+
proofRequestId,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Sender: Holder
|
|
63
|
+
* Start a wallet to wallet verification flow
|
|
64
|
+
*/
|
|
65
|
+
export function buildAckWalletToWalletVerificationMessage({
|
|
66
|
+
holderDID,
|
|
67
|
+
verifierDID,
|
|
68
|
+
proofRequestId,
|
|
69
|
+
}) {
|
|
70
|
+
return {
|
|
71
|
+
type: MessageTypes.Ack,
|
|
72
|
+
from: holderDID,
|
|
73
|
+
to: verifierDID,
|
|
74
|
+
body: {
|
|
75
|
+
goal_code: Goals.WalletToWalletVerification,
|
|
76
|
+
goal: 'Ack for for a Wallet to Wallet Verification invite',
|
|
77
|
+
proofRequestId: proofRequestId,
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Sender: Holder
|
|
85
|
+
* Send a verifiable presentation to the verifier
|
|
86
|
+
*/
|
|
87
|
+
export function buildVerifiablePresentationMessage({
|
|
88
|
+
verifierDID,
|
|
89
|
+
holderDID,
|
|
90
|
+
proofRequestId,
|
|
91
|
+
presentation,
|
|
92
|
+
}) {
|
|
93
|
+
return {
|
|
94
|
+
type: MessageTypes.Presentation,
|
|
95
|
+
from: verifierDID,
|
|
96
|
+
to: holderDID,
|
|
97
|
+
body: {
|
|
98
|
+
proofRequestId,
|
|
99
|
+
presentation,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Sender: Verifier
|
|
106
|
+
* Sends an the presentation result to the holder
|
|
107
|
+
*/
|
|
108
|
+
export function buildVerifiablePresentationAckMessage({
|
|
109
|
+
verifierDID,
|
|
110
|
+
holderDID,
|
|
111
|
+
proofRequestId,
|
|
112
|
+
presentationResult,
|
|
113
|
+
}) {
|
|
114
|
+
return {
|
|
115
|
+
type: MessageTypes.Ack,
|
|
116
|
+
from: verifierDID,
|
|
117
|
+
to: holderDID,
|
|
118
|
+
body: {
|
|
119
|
+
goal_code: Goals.PresentationAckFromVerifier,
|
|
120
|
+
goal: 'Verifier is sending an Ack for a presentation sent by the holder',
|
|
121
|
+
proofRequestId,
|
|
122
|
+
presentationResult,
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|