@docknetwork/wallet-sdk-core 1.5.14 → 1.7.6
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/.claude/settings.local.json +12 -0
- package/generate-docs.js +33 -0
- package/jsdoc.conf.json +28 -0
- package/lib/biometric-provider.d.ts +124 -32
- package/lib/biometric-provider.d.ts.map +1 -1
- package/lib/biometric-provider.js +146 -7
- package/lib/biometric-provider.js.map +1 -1
- package/lib/cloud-wallet.d.ts +8 -6
- package/lib/cloud-wallet.d.ts.map +1 -1
- package/lib/cloud-wallet.js +41 -57
- package/lib/cloud-wallet.js.map +1 -1
- package/lib/credential-provider.d.ts +58 -33
- package/lib/credential-provider.d.ts.map +1 -1
- package/lib/credential-provider.js +212 -11
- package/lib/credential-provider.js.map +1 -1
- package/lib/credentials/oidvc.js +4 -5
- package/lib/credentials/oidvc.js.map +1 -1
- package/lib/did-provider.d.ts +102 -36
- package/lib/did-provider.d.ts.map +1 -1
- package/lib/did-provider.js +185 -27
- package/lib/did-provider.js.map +1 -1
- package/lib/ecosystem-tools.js +4 -5
- package/lib/ecosystem-tools.js.map +1 -1
- package/lib/helpers.js +6 -6
- package/lib/helpers.js.map +1 -1
- package/lib/message-provider.d.ts +39 -13
- package/lib/message-provider.d.ts.map +1 -1
- package/lib/message-provider.js +173 -22
- package/lib/message-provider.js.map +1 -1
- package/lib/messages/message-helpers.js +6 -6
- package/lib/messages/message-helpers.js.map +1 -1
- package/lib/network-resolver.js +5 -5
- package/lib/network-resolver.js.map +1 -1
- package/lib/qr-handlers/builtin/index.d.ts +30 -0
- package/lib/qr-handlers/builtin/index.d.ts.map +1 -0
- package/lib/qr-handlers/builtin/index.js +46 -0
- package/lib/qr-handlers/builtin/index.js.map +1 -0
- package/lib/qr-handlers/builtin/oid4vc-handler.d.ts +137 -0
- package/lib/qr-handlers/builtin/oid4vc-handler.d.ts.map +1 -0
- package/lib/qr-handlers/builtin/oid4vc-handler.js +134 -0
- package/lib/qr-handlers/builtin/oid4vc-handler.js.map +1 -0
- package/lib/qr-handlers/index.d.ts +76 -0
- package/lib/qr-handlers/index.d.ts.map +1 -0
- package/lib/qr-handlers/index.js +92 -0
- package/lib/qr-handlers/index.js.map +1 -0
- package/lib/qr-handlers/processor.d.ts +110 -0
- package/lib/qr-handlers/processor.d.ts.map +1 -0
- package/lib/qr-handlers/processor.js +251 -0
- package/lib/qr-handlers/processor.js.map +1 -0
- package/lib/qr-handlers/types.d.ts +205 -0
- package/lib/qr-handlers/types.d.ts.map +1 -0
- package/lib/qr-handlers/types.js +10 -0
- package/lib/qr-handlers/types.js.map +1 -0
- package/lib/types.d.ts +613 -13
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js +16 -0
- package/lib/types.js.map +1 -1
- package/lib/verification-controller.d.ts +3 -4
- package/lib/verification-controller.d.ts.map +1 -1
- package/lib/verification-controller.js +10 -3
- package/lib/verification-controller.js.map +1 -1
- package/lib/wallet-to-wallet-verification/walletToWalletVerificationProvider.d.ts +0 -1
- package/lib/wallet-to-wallet-verification/walletToWalletVerificationProvider.d.ts.map +1 -1
- package/lib/wallet-to-wallet-verification/walletToWalletVerificationProvider.js +2 -2
- package/lib/wallet-to-wallet-verification/walletToWalletVerificationProvider.js.map +1 -1
- package/lib/wallet-wasm.d.ts +2 -2
- package/lib/wallet-wasm.d.ts.map +1 -1
- package/lib/wallet-wasm.js +15 -17
- package/lib/wallet-wasm.js.map +1 -1
- package/lib/wallet.d.ts +36 -20
- package/lib/wallet.d.ts.map +1 -1
- package/lib/wallet.js +172 -26
- package/lib/wallet.js.map +1 -1
- package/package.json +19 -11
- package/src/biometric-provider.ts +157 -42
- package/src/cloud-wallet.ts +21 -60
- package/src/credential-provider.test.ts +191 -1
- package/src/credential-provider.ts +208 -27
- package/src/did-provider.ts +183 -34
- package/src/message-provider.ts +177 -38
- package/src/qr-handlers/builtin/index.ts +30 -0
- package/src/qr-handlers/builtin/oid4vc-handler.ts +198 -0
- package/src/qr-handlers/index.ts +76 -0
- package/src/qr-handlers/processor.test.ts +514 -0
- package/src/qr-handlers/processor.ts +311 -0
- package/src/qr-handlers/types.ts +228 -0
- package/src/types.ts +671 -11
- package/src/verification-controller.test.ts +1 -2
- package/src/verification-controller.ts +14 -2
- package/src/wallet-wasm.ts +5 -8
- package/src/wallet.ts +173 -24
- package/tsconfig.build.tsbuildinfo +1 -1
package/src/cloud-wallet.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module cloud-wallet
|
|
3
|
+
* @description Cloud wallet functionality for the Truvera Wallet SDK.
|
|
4
|
+
* This module provides the main cloud wallet creation and management functions.
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import {
|
|
2
8
|
DataStore,
|
|
3
9
|
DataStoreEvents,
|
|
4
10
|
} from '@docknetwork/wallet-sdk-data-store/src/types';
|
|
5
11
|
import { logger } from '@docknetwork/wallet-sdk-data-store/src/logger';
|
|
6
|
-
import { edvService
|
|
7
|
-
import hkdf from 'futoin-hkdf';
|
|
8
|
-
import crypto from '@docknetwork/universal-wallet/crypto';
|
|
12
|
+
import { edvService } from '@docknetwork/wallet-sdk-wasm/src/services/edv';
|
|
9
13
|
import { utilCryptoService } from '@docknetwork/wallet-sdk-wasm/src/services/util-crypto';
|
|
10
14
|
|
|
11
15
|
export const SYNC_MARKER_TYPE = 'SyncMarkerDocument';
|
|
12
16
|
export const MNEMONIC_WORD_COUNT = 12;
|
|
13
17
|
export const KEY_MAPPING_TYPE = 'KeyMappingDocument';
|
|
14
|
-
export const HKDF_LENGTH = 32;
|
|
15
|
-
export const HKDF_HASH = 'SHA-256';
|
|
16
18
|
const MASTER_KEY_SUFFIX = 'master-key';
|
|
17
19
|
|
|
18
20
|
/**
|
|
@@ -25,9 +27,7 @@ export function deriveBiometricKey(
|
|
|
25
27
|
biometricData: Buffer,
|
|
26
28
|
identifier: string,
|
|
27
29
|
): Buffer {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return hkdf(biometricData, HKDF_LENGTH, { salt, hash: HKDF_HASH });
|
|
30
|
+
return edvService.deriveBiometricKey(biometricData, identifier);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/**
|
|
@@ -55,15 +55,7 @@ export async function deriveBiometricEncryptionKey(
|
|
|
55
55
|
biometricData: Buffer,
|
|
56
56
|
identifier: string
|
|
57
57
|
): Promise<{ key: Buffer; iv: Buffer }> {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const randomBytes = crypto.getRandomValues(new Uint8Array(16));
|
|
61
|
-
const iv = Buffer.from(randomBytes);
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
key,
|
|
65
|
-
iv
|
|
66
|
-
};
|
|
58
|
+
return edvService.deriveBiometricEncryptionKey(biometricData, identifier);
|
|
67
59
|
}
|
|
68
60
|
|
|
69
61
|
/**
|
|
@@ -78,24 +70,7 @@ export async function encryptMasterKey(
|
|
|
78
70
|
encryptionKey: Buffer,
|
|
79
71
|
iv: Buffer
|
|
80
72
|
): Promise<Uint8Array> {
|
|
81
|
-
|
|
82
|
-
const ivData = new Uint8Array(iv);
|
|
83
|
-
|
|
84
|
-
const key = await crypto.subtle.importKey(
|
|
85
|
-
'raw',
|
|
86
|
-
keyData,
|
|
87
|
-
{ name: 'AES-GCM' },
|
|
88
|
-
false,
|
|
89
|
-
['encrypt']
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
const encryptedBuffer = await crypto.subtle.encrypt(
|
|
93
|
-
{ name: 'AES-GCM', iv: ivData },
|
|
94
|
-
key,
|
|
95
|
-
masterKey
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
return new Uint8Array(encryptedBuffer);
|
|
73
|
+
return edvService.encryptMasterKey(masterKey, encryptionKey, iv);
|
|
99
74
|
}
|
|
100
75
|
|
|
101
76
|
/**
|
|
@@ -110,28 +85,7 @@ export async function decryptMasterKey(
|
|
|
110
85
|
decryptionKey: Buffer,
|
|
111
86
|
iv: Buffer
|
|
112
87
|
): Promise<Uint8Array> {
|
|
113
|
-
|
|
114
|
-
const keyData = new Uint8Array(decryptionKey);
|
|
115
|
-
const ivData = new Uint8Array(iv);
|
|
116
|
-
|
|
117
|
-
const key = await crypto.subtle.importKey(
|
|
118
|
-
'raw',
|
|
119
|
-
keyData,
|
|
120
|
-
{ name: 'AES-GCM' },
|
|
121
|
-
false,
|
|
122
|
-
['decrypt']
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
const decryptedBuffer = await crypto.subtle.decrypt(
|
|
126
|
-
{ name: 'AES-GCM', iv: ivData },
|
|
127
|
-
key,
|
|
128
|
-
encryptedKey
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
return new Uint8Array(decryptedBuffer);
|
|
132
|
-
} catch (error) {
|
|
133
|
-
throw new Error('Decryption failed: Invalid key or corrupted data');
|
|
134
|
-
}
|
|
88
|
+
return edvService.decryptMasterKey(encryptedKey, decryptionKey, iv);
|
|
135
89
|
}
|
|
136
90
|
|
|
137
91
|
/**
|
|
@@ -147,14 +101,14 @@ export async function initializeKeyMappingVault(
|
|
|
147
101
|
authKey: string,
|
|
148
102
|
biometricData: Buffer,
|
|
149
103
|
identifier: string
|
|
150
|
-
): Promise<
|
|
104
|
+
): Promise<typeof edvService> {
|
|
151
105
|
const {
|
|
152
106
|
hmacKey,
|
|
153
107
|
agreementKey,
|
|
154
108
|
verificationKey
|
|
155
109
|
} = await deriveKeyMappingVaultKeys(biometricData, identifier);
|
|
156
110
|
|
|
157
|
-
const keyMappingEdvService =
|
|
111
|
+
const keyMappingEdvService = edvService;
|
|
158
112
|
await keyMappingEdvService.initialize({
|
|
159
113
|
hmacKey,
|
|
160
114
|
agreementKey,
|
|
@@ -219,7 +173,7 @@ export async function enrollUserWithBiometrics(
|
|
|
219
173
|
* @returns The decrypted master key for CloudWalletVault
|
|
220
174
|
*/
|
|
221
175
|
export async function getKeyMappingMasterKey(
|
|
222
|
-
keyMappingEdv:
|
|
176
|
+
keyMappingEdv: typeof edvService,
|
|
223
177
|
identifier: string,
|
|
224
178
|
decryptionKey: Buffer,
|
|
225
179
|
): Promise<Uint8Array> {
|
|
@@ -227,6 +181,13 @@ export async function getKeyMappingMasterKey(
|
|
|
227
181
|
equals: {
|
|
228
182
|
'content.id': identifier
|
|
229
183
|
}
|
|
184
|
+
}).catch(error => {
|
|
185
|
+
if (error.message && error.message.includes('does not exist')) {
|
|
186
|
+
logger.error('KeyMappingVault does not exist, skipping find');
|
|
187
|
+
return { documents: [] };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
throw error;
|
|
230
191
|
});
|
|
231
192
|
|
|
232
193
|
if (!result.documents || result.documents.length === 0) {
|
|
@@ -227,7 +227,7 @@ describe('CredentialProvider', () => {
|
|
|
227
227
|
});
|
|
228
228
|
|
|
229
229
|
await provider.syncCredentialStatus({forceFetch: true});
|
|
230
|
-
|
|
230
|
+
|
|
231
231
|
// Clear mocks
|
|
232
232
|
jest.clearAllMocks();
|
|
233
233
|
|
|
@@ -255,6 +255,196 @@ describe('CredentialProvider', () => {
|
|
|
255
255
|
expect(credentialServiceRPC.verifyCredential).toHaveBeenCalledTimes(2);
|
|
256
256
|
});
|
|
257
257
|
|
|
258
|
+
it('should always refetch credentials with Invalid status even when cached', async () => {
|
|
259
|
+
// First, create credentials with Invalid status
|
|
260
|
+
jest
|
|
261
|
+
.spyOn(credentialServiceRPC, 'verifyCredential')
|
|
262
|
+
.mockImplementation(async () => {
|
|
263
|
+
return {
|
|
264
|
+
verified: false,
|
|
265
|
+
error: 'Credential validation failed',
|
|
266
|
+
};
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
await provider.syncCredentialStatus({forceFetch: true});
|
|
270
|
+
|
|
271
|
+
// Verify initial calls
|
|
272
|
+
expect(credentialServiceRPC.verifyCredential).toHaveBeenCalledTimes(2);
|
|
273
|
+
|
|
274
|
+
// Check that status is set to Invalid
|
|
275
|
+
const initialStatusDoc = await wallet.getDocumentById(
|
|
276
|
+
`${customerCredential.id}#status`,
|
|
277
|
+
);
|
|
278
|
+
expect(initialStatusDoc.status).toBe(CredentialStatus.Invalid);
|
|
279
|
+
|
|
280
|
+
// Clear mocks to track only subsequent calls
|
|
281
|
+
jest.clearAllMocks();
|
|
282
|
+
|
|
283
|
+
// Now mock successful verification
|
|
284
|
+
jest
|
|
285
|
+
.spyOn(credentialServiceRPC, 'verifyCredential')
|
|
286
|
+
.mockImplementation(async () => {
|
|
287
|
+
return {
|
|
288
|
+
verified: true,
|
|
289
|
+
};
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Call syncCredentialStatus again WITHOUT forceFetch
|
|
293
|
+
// Invalid status should trigger refetch even without forceFetch
|
|
294
|
+
const statusDocs = await provider.syncCredentialStatus({});
|
|
295
|
+
|
|
296
|
+
// Verify that verifyCredential was called again despite no forceFetch
|
|
297
|
+
expect(credentialServiceRPC.verifyCredential).toHaveBeenCalledTimes(2);
|
|
298
|
+
|
|
299
|
+
// Check that status is now updated to Verified
|
|
300
|
+
expect(statusDocs.length).toBe(2);
|
|
301
|
+
for (const statusDoc of statusDocs) {
|
|
302
|
+
expect(statusDoc.status).toBe(CredentialStatus.Verified);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('should always refetch credentials with Pending status even when cached', async () => {
|
|
307
|
+
// First, manually create a Pending status document
|
|
308
|
+
const pendingStatusDoc = {
|
|
309
|
+
type: 'CredentialStatus',
|
|
310
|
+
id: `${customerCredential.id}#status`,
|
|
311
|
+
createdAt: new Date().toISOString(),
|
|
312
|
+
updatedAt: new Date().toISOString(),
|
|
313
|
+
status: CredentialStatus.Pending,
|
|
314
|
+
error: null,
|
|
315
|
+
warning: null,
|
|
316
|
+
};
|
|
317
|
+
await wallet.updateDocument(pendingStatusDoc);
|
|
318
|
+
|
|
319
|
+
// Mock successful verification
|
|
320
|
+
jest
|
|
321
|
+
.spyOn(credentialServiceRPC, 'verifyCredential')
|
|
322
|
+
.mockImplementation(async () => {
|
|
323
|
+
return {
|
|
324
|
+
verified: true,
|
|
325
|
+
};
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// Call syncCredentialStatus without forceFetch
|
|
329
|
+
// Pending status should trigger refetch
|
|
330
|
+
const statusDocs = await provider.syncCredentialStatus({
|
|
331
|
+
credentialIds: [customerCredential.id],
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// Verify that verifyCredential was called for the Pending credential
|
|
335
|
+
expect(credentialServiceRPC.verifyCredential).toHaveBeenCalledTimes(1);
|
|
336
|
+
|
|
337
|
+
// Check that status is now updated to Verified
|
|
338
|
+
expect(statusDocs.length).toBe(1);
|
|
339
|
+
expect(statusDocs[0].status).toBe(CredentialStatus.Verified);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('should cache Revoked status and not refetch within 24 hours', async () => {
|
|
343
|
+
// First, create credentials with Revoked status
|
|
344
|
+
jest
|
|
345
|
+
.spyOn(credentialServiceRPC, 'verifyCredential')
|
|
346
|
+
.mockImplementation(async () => {
|
|
347
|
+
return {
|
|
348
|
+
verified: false,
|
|
349
|
+
error: 'Revocation check failed',
|
|
350
|
+
};
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
await provider.syncCredentialStatus({forceFetch: true});
|
|
354
|
+
|
|
355
|
+
// Verify initial calls
|
|
356
|
+
expect(credentialServiceRPC.verifyCredential).toHaveBeenCalledTimes(2);
|
|
357
|
+
|
|
358
|
+
// Check that status is set to Revoked
|
|
359
|
+
const revokedStatusDoc = await wallet.getDocumentById(
|
|
360
|
+
`${customerCredential.id}#status`,
|
|
361
|
+
);
|
|
362
|
+
expect(revokedStatusDoc.status).toBe(CredentialStatus.Revoked);
|
|
363
|
+
|
|
364
|
+
// Clear mocks to track only subsequent calls
|
|
365
|
+
jest.clearAllMocks();
|
|
366
|
+
|
|
367
|
+
// Call syncCredentialStatus again WITHOUT forceFetch
|
|
368
|
+
// Revoked status should NOT trigger refetch within 24 hours
|
|
369
|
+
const statusDocs = await provider.syncCredentialStatus({});
|
|
370
|
+
|
|
371
|
+
// Verify that verifyCredential was NOT called (cached)
|
|
372
|
+
expect(credentialServiceRPC.verifyCredential).toHaveBeenCalledTimes(0);
|
|
373
|
+
|
|
374
|
+
// Check that status remains Revoked
|
|
375
|
+
expect(statusDocs.length).toBe(2);
|
|
376
|
+
for (const statusDoc of statusDocs) {
|
|
377
|
+
expect(statusDoc.status).toBe(CredentialStatus.Revoked);
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it('should cache Verified status and not refetch within 24 hours', async () => {
|
|
382
|
+
// First, create credentials with Verified status
|
|
383
|
+
jest
|
|
384
|
+
.spyOn(credentialServiceRPC, 'verifyCredential')
|
|
385
|
+
.mockImplementation(async () => {
|
|
386
|
+
return {
|
|
387
|
+
verified: true,
|
|
388
|
+
};
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
await provider.syncCredentialStatus({forceFetch: true});
|
|
392
|
+
|
|
393
|
+
// Verify initial calls
|
|
394
|
+
expect(credentialServiceRPC.verifyCredential).toHaveBeenCalledTimes(2);
|
|
395
|
+
|
|
396
|
+
// Check that status is set to Verified
|
|
397
|
+
const verifiedStatusDoc = await wallet.getDocumentById(
|
|
398
|
+
`${customerCredential.id}#status`,
|
|
399
|
+
);
|
|
400
|
+
expect(verifiedStatusDoc.status).toBe(CredentialStatus.Verified);
|
|
401
|
+
|
|
402
|
+
// Clear mocks to track only subsequent calls
|
|
403
|
+
jest.clearAllMocks();
|
|
404
|
+
|
|
405
|
+
// Call syncCredentialStatus again WITHOUT forceFetch
|
|
406
|
+
// Verified status should NOT trigger refetch within 24 hours
|
|
407
|
+
const statusDocs = await provider.syncCredentialStatus({});
|
|
408
|
+
|
|
409
|
+
// Verify that verifyCredential was NOT called (cached)
|
|
410
|
+
expect(credentialServiceRPC.verifyCredential).toHaveBeenCalledTimes(0);
|
|
411
|
+
|
|
412
|
+
// Check that status remains Verified
|
|
413
|
+
expect(statusDocs.length).toBe(2);
|
|
414
|
+
for (const statusDoc of statusDocs) {
|
|
415
|
+
expect(statusDoc.status).toBe(CredentialStatus.Verified);
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('should refetch credentials without status document', async () => {
|
|
420
|
+
// Remove any existing status documents
|
|
421
|
+
await wallet.removeDocument(`${customerCredential.id}#status`).catch(() => {});
|
|
422
|
+
await wallet.removeDocument(`${biometricsBBSRevocation.id}#status`).catch(() => {});
|
|
423
|
+
|
|
424
|
+
// Mock successful verification
|
|
425
|
+
jest
|
|
426
|
+
.spyOn(credentialServiceRPC, 'verifyCredential')
|
|
427
|
+
.mockImplementation(async () => {
|
|
428
|
+
return {
|
|
429
|
+
verified: true,
|
|
430
|
+
};
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
// Call syncCredentialStatus
|
|
434
|
+
// Missing status docs should trigger fetch
|
|
435
|
+
const statusDocs = await provider.syncCredentialStatus({});
|
|
436
|
+
|
|
437
|
+
// Verify that verifyCredential was called for both credentials
|
|
438
|
+
expect(credentialServiceRPC.verifyCredential).toHaveBeenCalledTimes(2);
|
|
439
|
+
|
|
440
|
+
// Check that status documents were created
|
|
441
|
+
expect(statusDocs.length).toBe(2);
|
|
442
|
+
for (const statusDoc of statusDocs) {
|
|
443
|
+
expect(statusDoc.status).toBe(CredentialStatus.Verified);
|
|
444
|
+
expect(statusDoc.type).toBe('CredentialStatus');
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
|
|
258
448
|
afterEach(() => {
|
|
259
449
|
(credentialServiceRPC.verifyCredential as any).mockReset();
|
|
260
450
|
});
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module credential-provider
|
|
3
|
+
* @description Verifiable credential management functionality for the Truvera Wallet SDK.
|
|
4
|
+
* This module provides functions for importing, verifying, storing, and managing verifiable credentials.
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import {credentialServiceRPC} from '@docknetwork/wallet-sdk-wasm/src/services/credential';
|
|
2
|
-
import {IWallet} from './types';
|
|
8
|
+
import {IWallet, ICredentialProvider} from './types';
|
|
9
|
+
export type {ICredentialProvider};
|
|
3
10
|
import assert from 'assert';
|
|
4
11
|
import {blockchainService} from '@docknetwork/wallet-sdk-wasm/src/services/blockchain';
|
|
5
12
|
import {acquireOpenIDCredentialFromURI} from './credentials/oidvc';
|
|
@@ -7,29 +14,10 @@ import {IDIDProvider} from './did-provider';
|
|
|
7
14
|
|
|
8
15
|
export type Credential = any;
|
|
9
16
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
isBBSPlusCredential(credential: any): boolean;
|
|
15
|
-
isValid(credential: any, forceFetch?: boolean): Promise<{
|
|
16
|
-
status: string;
|
|
17
|
-
error?: string;
|
|
18
|
-
warning?: string;
|
|
19
|
-
}>;
|
|
20
|
-
addCredential(credential: any): Promise<Credential>;
|
|
21
|
-
importCredentialFromURI(
|
|
22
|
-
params: importCredentialFromUriParams,
|
|
23
|
-
): Promise<Credential>;
|
|
24
|
-
syncCredentialStatus(
|
|
25
|
-
params: SyncCredentialStatusParams,
|
|
26
|
-
): Promise<CredentialStatusDocument[]>;
|
|
27
|
-
getCredentialStatus(
|
|
28
|
-
credential: Credential,
|
|
29
|
-
): Promise<{status: string; error?: string}>;
|
|
30
|
-
removeCredential(credential: Credential): Promise<void>;
|
|
31
|
-
}
|
|
32
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Internal function to check if a credential uses BBS+ signature
|
|
19
|
+
* @private
|
|
20
|
+
*/
|
|
33
21
|
export function isBBSPlusCredential(credential) {
|
|
34
22
|
return (
|
|
35
23
|
(typeof credential?.proof?.type === 'string' &&
|
|
@@ -47,6 +35,10 @@ type importCredentialFromUriParams = {
|
|
|
47
35
|
getAuthCode?: (authorizationURL: string) => Promise<string>;
|
|
48
36
|
};
|
|
49
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Internal function to import credential from URI
|
|
40
|
+
* @private
|
|
41
|
+
*/
|
|
50
42
|
export async function importCredentialFromURI({
|
|
51
43
|
uri,
|
|
52
44
|
wallet,
|
|
@@ -68,6 +60,10 @@ export async function importCredentialFromURI({
|
|
|
68
60
|
await addCredential({wallet, credential});
|
|
69
61
|
}
|
|
70
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Internal function to check if credential is expired
|
|
65
|
+
* @private
|
|
66
|
+
*/
|
|
71
67
|
export function isCredentialExpired(credential) {
|
|
72
68
|
return (
|
|
73
69
|
!!credential.expirationDate &&
|
|
@@ -78,7 +74,7 @@ export function isCredentialExpired(credential) {
|
|
|
78
74
|
/**
|
|
79
75
|
* Uses Dock SDK to verify a credential
|
|
80
76
|
* @param credential
|
|
81
|
-
* @returns
|
|
77
|
+
* @returns {Promise<Object>} Verification result with status and optional error/warning messages
|
|
82
78
|
*/
|
|
83
79
|
export async function isValid({
|
|
84
80
|
credential,
|
|
@@ -172,7 +168,26 @@ export async function isValid({
|
|
|
172
168
|
|
|
173
169
|
export const ACUMM_WITNESS_PROP_KEY = '$$accum__witness$$';
|
|
174
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Internal function to add credential to wallet
|
|
173
|
+
* @private
|
|
174
|
+
*/
|
|
175
175
|
export async function addCredential({wallet, credential}) {
|
|
176
|
+
// Check if the credential is an SD-JWT (string format)
|
|
177
|
+
if (typeof credential === 'string') {
|
|
178
|
+
try {
|
|
179
|
+
const isSDJWT = await credentialServiceRPC.isSDJWTCredential({credential});
|
|
180
|
+
|
|
181
|
+
if (isSDJWT) {
|
|
182
|
+
// Convert SD-JWT to W3C format (includes _sd_jwt metadata for unwrapping)
|
|
183
|
+
credential = await credentialServiceRPC.credentialToW3C({credential});
|
|
184
|
+
}
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.error('Error checking/converting SD-JWT credential:', error);
|
|
187
|
+
throw new Error('Failed to process SD-JWT credential: ' + error.message);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
176
191
|
const acummWitness = credential[ACUMM_WITNESS_PROP_KEY];
|
|
177
192
|
|
|
178
193
|
if (acummWitness) {
|
|
@@ -195,6 +210,10 @@ export async function addCredential({wallet, credential}) {
|
|
|
195
210
|
return response;
|
|
196
211
|
}
|
|
197
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Internal function to get membership witness for credential
|
|
215
|
+
* @private
|
|
216
|
+
*/
|
|
198
217
|
async function getMembershipWitness({credentialId, wallet}) {
|
|
199
218
|
const document = await wallet.getDocumentById(`${credentialId}#witness`);
|
|
200
219
|
return document?.value;
|
|
@@ -230,6 +249,10 @@ type CredentialStatusDocument = {
|
|
|
230
249
|
* @param param0
|
|
231
250
|
* @returns CredentialStatusDocument[]
|
|
232
251
|
*/
|
|
252
|
+
/**
|
|
253
|
+
* Internal function to sync credential status from blockchain
|
|
254
|
+
* @private
|
|
255
|
+
*/
|
|
233
256
|
async function syncCredentialStatus({
|
|
234
257
|
wallet,
|
|
235
258
|
credentialIds,
|
|
@@ -268,7 +291,9 @@ async function syncCredentialStatus({
|
|
|
268
291
|
|
|
269
292
|
statusDocs.push(statusDoc);
|
|
270
293
|
|
|
271
|
-
|
|
294
|
+
// Revoked and Expired statuses should be cached
|
|
295
|
+
// The user can fore refresh that from the credentials screen
|
|
296
|
+
if (!statusDoc.status || statusDoc.status === CredentialStatus.Invalid || statusDoc.status === CredentialStatus.Pending) {
|
|
272
297
|
shouldFetch = true;
|
|
273
298
|
}
|
|
274
299
|
|
|
@@ -312,7 +337,11 @@ async function syncCredentialStatus({
|
|
|
312
337
|
/**
|
|
313
338
|
* Removes a credential and its related documents from the wallet
|
|
314
339
|
* @param param0
|
|
315
|
-
* @returns
|
|
340
|
+
* @returns {Promise<void>}
|
|
341
|
+
*/
|
|
342
|
+
/**
|
|
343
|
+
* Internal function to remove credential and related documents
|
|
344
|
+
* @private
|
|
316
345
|
*/
|
|
317
346
|
export async function removeCredential({
|
|
318
347
|
wallet,
|
|
@@ -338,6 +367,35 @@ export async function removeCredential({
|
|
|
338
367
|
}
|
|
339
368
|
}
|
|
340
369
|
|
|
370
|
+
/**
|
|
371
|
+
* Creates a credential provider instance bound to a wallet
|
|
372
|
+
* @param {Object} params - Provider configuration
|
|
373
|
+
* @param {IWallet} params.wallet - The wallet instance to use for credential storage
|
|
374
|
+
* @returns {ICredentialProvider} A credential provider instance with all verifiable credential management methods
|
|
375
|
+
* @see {@link ICredentialProvider} - The interface defining all available credential provider methods
|
|
376
|
+
* @example
|
|
377
|
+
* import { createCredentialProvider } from '@docknetwork/wallet-sdk-core';
|
|
378
|
+
*
|
|
379
|
+
* const credentialProvider = createCredentialProvider({wallet});
|
|
380
|
+
*
|
|
381
|
+
* // Add a credential
|
|
382
|
+
* const addedCredential = await credentialProvider.addCredential(myCredential);
|
|
383
|
+
*
|
|
384
|
+
* // Validate a credential
|
|
385
|
+
* const result = await credentialProvider.isValid(credential);
|
|
386
|
+
* if (result.status === 'verified') {
|
|
387
|
+
* console.log('Credential is valid');
|
|
388
|
+
* }
|
|
389
|
+
*
|
|
390
|
+
* // Get all credentials
|
|
391
|
+
* const allCredentials = credentialProvider.getCredentials();
|
|
392
|
+
*
|
|
393
|
+
* // Import from URI
|
|
394
|
+
* await credentialProvider.importCredentialFromURI({
|
|
395
|
+
* uri: 'https://example.com/credential-offer',
|
|
396
|
+
* didProvider
|
|
397
|
+
* });
|
|
398
|
+
*/
|
|
341
399
|
export function createCredentialProvider({
|
|
342
400
|
wallet,
|
|
343
401
|
}: {
|
|
@@ -348,21 +406,103 @@ export function createCredentialProvider({
|
|
|
348
406
|
}
|
|
349
407
|
|
|
350
408
|
return {
|
|
409
|
+
/**
|
|
410
|
+
* Imports a credential from a URI (supports OpenID credential offers)
|
|
411
|
+
* @memberof ICredentialProvider
|
|
412
|
+
* @param {Object} params - Import parameters
|
|
413
|
+
* @param {string} params.uri - The URI containing the credential offer
|
|
414
|
+
* @param {any} params.didProvider - DID provider instance for key management
|
|
415
|
+
* @param {Function} [params.getAuthCode] - Optional callback to handle authorization
|
|
416
|
+
* @returns {Promise<any>} The imported credential
|
|
417
|
+
* @throws {Error} If import fails
|
|
418
|
+
* @example
|
|
419
|
+
* const credential = await credentialProvider.importCredentialFromURI({
|
|
420
|
+
* uri: 'https://issuer.example.com/credential-offer',
|
|
421
|
+
* didProvider,
|
|
422
|
+
* getAuthCode: async (url) => getUserAuthCode(url)
|
|
423
|
+
* });
|
|
424
|
+
*/
|
|
351
425
|
importCredentialFromURI: async (params: importCredentialFromUriParams) =>
|
|
352
426
|
importCredentialFromURI({
|
|
353
427
|
...params,
|
|
354
428
|
wallet,
|
|
355
429
|
}),
|
|
430
|
+
/**
|
|
431
|
+
* Retrieves credentials from the wallet, optionally filtered by type
|
|
432
|
+
* @memberof ICredentialProvider
|
|
433
|
+
* @param {string} [type='VerifiableCredential'] - The credential type to filter by
|
|
434
|
+
* @returns {any[]} Array of credentials matching the specified type
|
|
435
|
+
* @example
|
|
436
|
+
* const allCredentials = credentialProvider.getCredentials();
|
|
437
|
+
* const certificates = credentialProvider.getCredentials('Certificate');
|
|
438
|
+
*/
|
|
356
439
|
getCredentials,
|
|
440
|
+
/**
|
|
441
|
+
* Gets the membership witness for a credential (used for BBS+ credentials)
|
|
442
|
+
* @memberof ICredentialProvider
|
|
443
|
+
* @param {string} credentialId - The credential ID to get the witness for
|
|
444
|
+
* @returns {Promise<any>} The membership witness data
|
|
445
|
+
* @example
|
|
446
|
+
* const witness = await credentialProvider.getMembershipWitness('credential-123');
|
|
447
|
+
*/
|
|
357
448
|
getMembershipWitness: async (credentialId: string) =>
|
|
358
449
|
getMembershipWitness({credentialId, wallet}),
|
|
450
|
+
/**
|
|
451
|
+
* Retrieves a credential by its ID
|
|
452
|
+
* @memberof ICredentialProvider
|
|
453
|
+
* @param {string} id - The unique identifier of the credential
|
|
454
|
+
* @returns {any} The credential document
|
|
455
|
+
* @throws {Error} If credential is not found
|
|
456
|
+
* @example
|
|
457
|
+
* const credential = await credentialProvider.getById('credential-123');
|
|
458
|
+
*/
|
|
359
459
|
getById: (id: string) => wallet.getDocumentById(id),
|
|
460
|
+
/**
|
|
461
|
+
* Checks if a credential uses BBS+ signature
|
|
462
|
+
* @memberof ICredentialProvider
|
|
463
|
+
* @param {any} credential - The credential to check
|
|
464
|
+
* @returns {boolean} True if the credential uses BBS+ signature
|
|
465
|
+
* @example
|
|
466
|
+
* const isBBS = credentialProvider.isBBSPlusCredential(credential);
|
|
467
|
+
* if (isBBS) {
|
|
468
|
+
* console.log('This credential uses BBS+ signatures');
|
|
469
|
+
* }
|
|
470
|
+
*/
|
|
360
471
|
isBBSPlusCredential,
|
|
472
|
+
/**
|
|
473
|
+
* Validates a credential by verifying its cryptographic proof and status
|
|
474
|
+
* @memberof ICredentialProvider
|
|
475
|
+
* @param {any} credential - The credential to validate
|
|
476
|
+
* @param {boolean} [forceFetch=false] - Whether to force refresh the credential status
|
|
477
|
+
* @returns {Promise<Object>} Validation result
|
|
478
|
+
* @returns {string} returns.status - Validation status (verified, revoked, expired, invalid, pending)
|
|
479
|
+
* @returns {string} [returns.error] - Error message if validation failed
|
|
480
|
+
* @returns {string} [returns.warning] - Warning message if any
|
|
481
|
+
* @throws {Error} If validation fails
|
|
482
|
+
* @example
|
|
483
|
+
* const result = await credentialProvider.isValid(credential);
|
|
484
|
+
* if (result.status === 'verified') {
|
|
485
|
+
* console.log('Credential is valid');
|
|
486
|
+
* } else if (result.status === 'revoked') {
|
|
487
|
+
* console.log('Credential has been revoked');
|
|
488
|
+
* }
|
|
489
|
+
*/
|
|
361
490
|
isValid: async credential =>
|
|
362
491
|
isValid({
|
|
363
492
|
credential,
|
|
364
493
|
wallet,
|
|
365
494
|
}) as any,
|
|
495
|
+
/**
|
|
496
|
+
* Gets the current status of a credential (cached, fast operation)
|
|
497
|
+
* @memberof ICredentialProvider
|
|
498
|
+
* @param {any} credential - The credential to check
|
|
499
|
+
* @returns {Promise<Object>} Current credential status
|
|
500
|
+
* @returns {string} returns.status - Current status of the credential
|
|
501
|
+
* @returns {string} [returns.error] - Error message if any
|
|
502
|
+
* @example
|
|
503
|
+
* const status = await credentialProvider.getCredentialStatus(credential);
|
|
504
|
+
* console.log(`Credential status: ${status.status}`);
|
|
505
|
+
*/
|
|
366
506
|
getCredentialStatus: async (credential: Credential) => {
|
|
367
507
|
assert(!!credential, 'credential is required');
|
|
368
508
|
|
|
@@ -379,10 +519,51 @@ export function createCredentialProvider({
|
|
|
379
519
|
error: statusDoc?.error,
|
|
380
520
|
};
|
|
381
521
|
},
|
|
522
|
+
/**
|
|
523
|
+
* Synchronizes credential status from the blockchain
|
|
524
|
+
* @memberof ICredentialProvider
|
|
525
|
+
* @param {Object} params - Sync parameters
|
|
526
|
+
* @param {string[]} [params.credentialIds] - Optional list of credential IDs to sync
|
|
527
|
+
* @param {boolean} [params.forceFetch=false] - Whether to force refresh from blockchain
|
|
528
|
+
* @returns {Promise<any[]>} Array of credential status documents
|
|
529
|
+
* @example
|
|
530
|
+
* // Sync all credentials
|
|
531
|
+
* await credentialProvider.syncCredentialStatus({});
|
|
532
|
+
*
|
|
533
|
+
* // Sync specific credentials
|
|
534
|
+
* await credentialProvider.syncCredentialStatus({
|
|
535
|
+
* credentialIds: ['cred-1', 'cred-2'],
|
|
536
|
+
* forceFetch: true
|
|
537
|
+
* });
|
|
538
|
+
*/
|
|
382
539
|
syncCredentialStatus: async (props: SyncCredentialStatusParams) => {
|
|
383
540
|
return syncCredentialStatus({wallet, ...props});
|
|
384
541
|
},
|
|
542
|
+
/**
|
|
543
|
+
* Adds a credential to the wallet
|
|
544
|
+
* @memberof ICredentialProvider
|
|
545
|
+
* @param {any} credential - The credential to add
|
|
546
|
+
* @returns {Promise<any>} The added credential document
|
|
547
|
+
* @example
|
|
548
|
+
* const addedCredential = await credentialProvider.addCredential({
|
|
549
|
+
* '@context': ['https://www.w3.org/2018/credentials/v1'],
|
|
550
|
+
* type: ['VerifiableCredential'],
|
|
551
|
+
* issuer: 'did:dock:issuer123',
|
|
552
|
+
* credentialSubject: { name: 'Alice' }
|
|
553
|
+
* });
|
|
554
|
+
*/
|
|
385
555
|
addCredential: credential => addCredential({wallet, credential}),
|
|
556
|
+
/**
|
|
557
|
+
* Removes a credential and all its related documents from the wallet
|
|
558
|
+
* @memberof ICredentialProvider
|
|
559
|
+
* @param {any} credential - The credential to remove
|
|
560
|
+
* @returns {Promise<void>}
|
|
561
|
+
* @throws {Error} If credential is not found
|
|
562
|
+
* @example
|
|
563
|
+
* await credentialProvider.removeCredential(credential);
|
|
564
|
+
* // Or by ID
|
|
565
|
+
* await credentialProvider.removeCredential('credential-123');
|
|
566
|
+
*/
|
|
386
567
|
removeCredential: credential => removeCredential({wallet, credential}),
|
|
387
568
|
// TODO: move import credential from json or URL to this provider
|
|
388
569
|
};
|