@cooperation/vc-storage 1.0.28 → 1.0.30
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/dist/models/CredentialEngine.js +120 -7
- package/dist/models/GoogleDriveStorage.js +8 -0
- package/dist/models/ResumeVC.js +47 -7
- package/dist/tests/email.test.js +31 -0
- package/dist/tests/seed.test.js +75 -0
- package/dist/tests/testEmailVC.js +29 -0
- package/dist/types/models/CredentialEngine.d.ts +9 -0
- package/dist/types/models/ResumeVC.d.ts +12 -0
- package/dist/types/tests/email.test.d.ts +1 -0
- package/dist/types/tests/seed.test.d.ts +1 -0
- package/dist/types/tests/testEmailVC.d.ts +1 -0
- package/dist/types/utils/context.d.ts +1 -1
- package/dist/types/utils/decodedSeed.d.ts +18 -0
- package/dist/utils/context.js +1 -1
- package/dist/utils/decodedSeed.js +77 -0
- package/package.json +6 -2
@@ -5,6 +5,9 @@ import { v4 as uuidv4 } from 'uuid';
|
|
5
5
|
import { extractKeyPairFromCredential, generateDIDSchema, generateUnsignedRecommendation, generateUnsignedVC, } from '../utils/credential.js';
|
6
6
|
import { customDocumentLoader } from '../utils/digitalbazaar.js';
|
7
7
|
import { saveToGoogleDrive } from '../utils/google.js';
|
8
|
+
function delay(ms) {
|
9
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
10
|
+
}
|
8
11
|
/**
|
9
12
|
* Class representing the Credential Engine.
|
10
13
|
* @class CredentialEngine
|
@@ -47,12 +50,19 @@ export class CredentialEngine {
|
|
47
50
|
this.keyPair = key;
|
48
51
|
return key;
|
49
52
|
}
|
50
|
-
generateKeyPair = async (address) => {
|
51
|
-
|
53
|
+
generateKeyPair = async (address, seed) => {
|
54
|
+
// Generate the key pair using the library's method
|
55
|
+
const keyPair = seed
|
56
|
+
? await Ed25519VerificationKey2020.generate({
|
57
|
+
seed: Buffer.from(seed).toString('hex'),
|
58
|
+
})
|
59
|
+
: await Ed25519VerificationKey2020.generate();
|
60
|
+
// Configure key pair attributes
|
52
61
|
const a = address || keyPair.publicKeyMultibase;
|
53
62
|
keyPair.controller = `did:key:${a}`;
|
54
63
|
keyPair.id = `${keyPair.controller}#${a}`;
|
55
64
|
keyPair.revoked = false;
|
65
|
+
// The `signer` is already provided by the `Ed25519VerificationKey2020` instance
|
56
66
|
return keyPair;
|
57
67
|
};
|
58
68
|
async verifyCreds(creds) {
|
@@ -71,11 +81,6 @@ export class CredentialEngine {
|
|
71
81
|
async createDID() {
|
72
82
|
try {
|
73
83
|
const keyPair = await this.generateKeyPair();
|
74
|
-
// const keyFile = await saveToGoogleDrive({
|
75
|
-
// storage: this.storage,
|
76
|
-
// data: keyPair,
|
77
|
-
// type: 'KEYPAIR',
|
78
|
-
// });
|
79
84
|
const didDocument = await generateDIDSchema(keyPair);
|
80
85
|
return { didDocument, keyPair };
|
81
86
|
}
|
@@ -234,4 +239,112 @@ export class CredentialEngine {
|
|
234
239
|
throw error;
|
235
240
|
}
|
236
241
|
}
|
242
|
+
/**
|
243
|
+
* Generate and sign an email Verifiable Credential (VC)
|
244
|
+
* @param {string} email - The email address to create the VC for
|
245
|
+
* @returns {Promise<{signedVC: any, fileId: string}>} The signed VC and its Google Drive file ID
|
246
|
+
*/
|
247
|
+
async generateAndSignEmailVC(email) {
|
248
|
+
try {
|
249
|
+
let keyPair;
|
250
|
+
let didDocument;
|
251
|
+
// Require SEED from environment
|
252
|
+
const encodedSeed = process.env.SEED;
|
253
|
+
if (!encodedSeed) {
|
254
|
+
throw new Error('SEED environment variable not set. Cannot generate or use any DID.');
|
255
|
+
}
|
256
|
+
// Use deterministic keys from environment seed
|
257
|
+
const { getDidFromEnvSeed } = await import('../utils/decodedSeed');
|
258
|
+
const result = await getDidFromEnvSeed();
|
259
|
+
keyPair = result.keyPair;
|
260
|
+
didDocument = result.didDocument;
|
261
|
+
console.log('Using DID from environment seed:', didDocument.id);
|
262
|
+
// Ensure the key has proper ID and controller
|
263
|
+
if (!keyPair.id || !keyPair.controller) {
|
264
|
+
const verificationMethod = didDocument.verificationMethod?.[0] || didDocument.authentication?.[0];
|
265
|
+
if (verificationMethod) {
|
266
|
+
keyPair.id = typeof verificationMethod === 'string' ? verificationMethod : verificationMethod.id;
|
267
|
+
keyPair.controller = didDocument.id;
|
268
|
+
}
|
269
|
+
}
|
270
|
+
console.log('Creating email VC with DID:', didDocument.id);
|
271
|
+
// Generate unsigned email VC
|
272
|
+
const unsignedCredential = {
|
273
|
+
'@context': [
|
274
|
+
'https://www.w3.org/2018/credentials/v1',
|
275
|
+
{
|
276
|
+
email: 'https://schema.org/email',
|
277
|
+
EmailCredential: {
|
278
|
+
'@id': 'https://example.com/EmailCredential',
|
279
|
+
},
|
280
|
+
},
|
281
|
+
],
|
282
|
+
id: `urn:uuid:${uuidv4()}`,
|
283
|
+
type: ['VerifiableCredential', 'EmailCredential'],
|
284
|
+
issuer: {
|
285
|
+
id: didDocument.id,
|
286
|
+
},
|
287
|
+
issuanceDate: new Date().toISOString(),
|
288
|
+
expirationDate: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString(), // 1 year from now
|
289
|
+
credentialSubject: {
|
290
|
+
id: `did:email:${email}`,
|
291
|
+
email: email,
|
292
|
+
},
|
293
|
+
};
|
294
|
+
// Sign the VC
|
295
|
+
const suite = new Ed25519Signature2020({
|
296
|
+
key: keyPair,
|
297
|
+
verificationMethod: keyPair.id,
|
298
|
+
});
|
299
|
+
const signedVC = await dbVc.issue({
|
300
|
+
credential: unsignedCredential,
|
301
|
+
suite,
|
302
|
+
documentLoader: customDocumentLoader,
|
303
|
+
});
|
304
|
+
const rootFolders = await this.storage.findFolders();
|
305
|
+
// Find or create Credentials folder
|
306
|
+
let credentialsFolder = rootFolders.find((f) => f.name === 'Credentials');
|
307
|
+
if (!credentialsFolder) {
|
308
|
+
credentialsFolder = await this.storage.createFolder({
|
309
|
+
folderName: 'Credentials',
|
310
|
+
parentFolderId: 'root',
|
311
|
+
});
|
312
|
+
// Wait and re-check to avoid duplicates
|
313
|
+
await delay(1500);
|
314
|
+
const refreshedFolders = await this.storage.findFolders();
|
315
|
+
const foundAgain = refreshedFolders.find((f) => f.name === 'Credentials');
|
316
|
+
if (foundAgain)
|
317
|
+
credentialsFolder = foundAgain;
|
318
|
+
}
|
319
|
+
// Find or create EMAIL_VC folder
|
320
|
+
const subfolders = await this.storage.findFolders(credentialsFolder.id);
|
321
|
+
let emailVcFolder = subfolders.find((f) => f.name === 'EMAIL_VC');
|
322
|
+
if (!emailVcFolder) {
|
323
|
+
emailVcFolder = await this.storage.createFolder({
|
324
|
+
folderName: 'EMAIL_VC',
|
325
|
+
parentFolderId: credentialsFolder.id,
|
326
|
+
});
|
327
|
+
// Wait and re-check to avoid duplicates
|
328
|
+
await delay(1500);
|
329
|
+
const refreshedSubfolders = await this.storage.findFolders(credentialsFolder.id);
|
330
|
+
const foundAgain = refreshedSubfolders.find((f) => f.name === 'EMAIL_VC');
|
331
|
+
if (foundAgain)
|
332
|
+
emailVcFolder = foundAgain;
|
333
|
+
}
|
334
|
+
// Save the VC in the EMAIL_VC folder
|
335
|
+
const file = await this.storage.saveFile({
|
336
|
+
data: {
|
337
|
+
fileName: `${email}`,
|
338
|
+
mimeType: 'application/json',
|
339
|
+
body: signedVC,
|
340
|
+
},
|
341
|
+
folderId: emailVcFolder.id,
|
342
|
+
});
|
343
|
+
return { signedVC, fileId: file.id };
|
344
|
+
}
|
345
|
+
catch (error) {
|
346
|
+
console.error('Error generating and signing email VC:', error);
|
347
|
+
throw error;
|
348
|
+
}
|
349
|
+
}
|
237
350
|
}
|
@@ -142,6 +142,14 @@ export class GoogleDriveStorage {
|
|
142
142
|
headers: {},
|
143
143
|
body: JSON.stringify({ role: 'reader', type: 'anyone' }),
|
144
144
|
});
|
145
|
+
// Invalidate cache for this parent folder
|
146
|
+
if (this.folderCache[parentFolderId]) {
|
147
|
+
delete this.folderCache[parentFolderId];
|
148
|
+
}
|
149
|
+
// Also clear 'root' cache if parent is root
|
150
|
+
if (parentFolderId === 'root' && this.folderCache['root']) {
|
151
|
+
delete this.folderCache['root'];
|
152
|
+
}
|
145
153
|
return folder;
|
146
154
|
}
|
147
155
|
async getMediaFolderId() {
|
package/dist/models/ResumeVC.js
CHANGED
@@ -7,18 +7,31 @@ import { generateDIDSchema } from '../utils/credential.js';
|
|
7
7
|
import { inlineResumeContext } from '../utils/context.js';
|
8
8
|
export class ResumeVC {
|
9
9
|
async sign({ formData, issuerDid, keyPair }) {
|
10
|
+
// First, generate the unsigned credential with the professional summary
|
10
11
|
const unsignedCredential = this.generateUnsignedCredential({ formData, issuerDid });
|
12
|
+
// Create the signature suite for signing
|
11
13
|
const suite = new Ed25519Signature2020({
|
12
|
-
key: new Ed25519VerificationKey2020(keyPair),
|
14
|
+
key: new Ed25519VerificationKey2020(keyPair),
|
13
15
|
verificationMethod: keyPair.id,
|
14
16
|
});
|
15
17
|
try {
|
16
|
-
|
18
|
+
// 1: Sign the professional summary VC first
|
19
|
+
const professionalSummaryVC = unsignedCredential.credentialSubject.professionalSummary;
|
20
|
+
const signedProfessionalSummaryVC = await dbVc.issue({
|
21
|
+
credential: professionalSummaryVC,
|
22
|
+
suite,
|
23
|
+
documentLoader: customDocumentLoader,
|
24
|
+
});
|
25
|
+
// Replace the unsigned professional summary with the signed one
|
26
|
+
unsignedCredential.credentialSubject.professionalSummary = signedProfessionalSummaryVC;
|
27
|
+
// 2: Now sign the entire resume VC (which includes the signed professional summary)
|
28
|
+
const signedResumeVC = await dbVc.issue({
|
17
29
|
credential: unsignedCredential,
|
18
30
|
suite,
|
19
31
|
documentLoader: customDocumentLoader,
|
20
32
|
});
|
21
|
-
console.log('Signed VC:',
|
33
|
+
console.log('Signed Resume VC:', signedResumeVC);
|
34
|
+
return signedResumeVC;
|
22
35
|
}
|
23
36
|
catch (error) {
|
24
37
|
console.error('Error signing VC:', error.message);
|
@@ -27,8 +40,34 @@ export class ResumeVC {
|
|
27
40
|
}
|
28
41
|
throw error;
|
29
42
|
}
|
30
|
-
return unsignedCredential;
|
31
43
|
}
|
44
|
+
generateProfessionalSummary = (aff) => {
|
45
|
+
let cleanNarrative = aff.narrative || '';
|
46
|
+
if (cleanNarrative.startsWith('<p>') && cleanNarrative.endsWith('</p>')) {
|
47
|
+
// Remove the opening and closing p tags
|
48
|
+
cleanNarrative = cleanNarrative.substring(3, cleanNarrative.length - 4);
|
49
|
+
}
|
50
|
+
// removing all <p> tags
|
51
|
+
cleanNarrative = cleanNarrative.replace(/<\/?p>/g, '');
|
52
|
+
// Replace <b> or <strong> tags with markdown bold syntax (**word**)
|
53
|
+
cleanNarrative = cleanNarrative.replace(/<b>(.*?)<\/b>/g, '**$1**');
|
54
|
+
cleanNarrative = cleanNarrative.replace(/<strong>(.*?)<\/strong>/g, '**$1**');
|
55
|
+
return {
|
56
|
+
'@context': [
|
57
|
+
'https://www.w3.org/2018/credentials/v1',
|
58
|
+
{
|
59
|
+
'@vocab': 'https://schema.hropenstandards.org/4.4/',
|
60
|
+
narrative: 'https://schema.org/narrative',
|
61
|
+
},
|
62
|
+
],
|
63
|
+
type: ['VerifiableCredential', 'NarrativeCredential'],
|
64
|
+
issuer: aff.issuer, // same DID as the issuer of the resume
|
65
|
+
issuanceDate: new Date().toISOString(),
|
66
|
+
credentialSubject: {
|
67
|
+
narrative: cleanNarrative,
|
68
|
+
},
|
69
|
+
};
|
70
|
+
};
|
32
71
|
generateUnsignedCredential({ formData, issuerDid }) {
|
33
72
|
const unsignedResumeVC = {
|
34
73
|
'@context': ['https://www.w3.org/2018/credentials/v1', inlineResumeContext['@context']],
|
@@ -62,9 +101,10 @@ export class ResumeVC {
|
|
62
101
|
},
|
63
102
|
},
|
64
103
|
},
|
65
|
-
|
66
|
-
|
67
|
-
|
104
|
+
professionalSummary: this.generateProfessionalSummary({
|
105
|
+
issuer: issuerDid,
|
106
|
+
narrative: formData.summary || '',
|
107
|
+
}),
|
68
108
|
employmentHistory: formData.experience.items.map((exp) => ({
|
69
109
|
id: exp.id ? `urn:uuid${exp.id}` : `urn:uuid:${uuidv4()}`, // Ensure each entry has an ID
|
70
110
|
organization: {
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import { CredentialEngine } from '../models/CredentialEngine.js';
|
2
|
+
import { GoogleDriveStorage } from '../models/GoogleDriveStorage.js';
|
3
|
+
async function testEmailVC() {
|
4
|
+
try {
|
5
|
+
// Get Google Drive access token from environment
|
6
|
+
const accessToken = 'your access token';
|
7
|
+
// Initialize storage and engine
|
8
|
+
const storage = new GoogleDriveStorage(accessToken);
|
9
|
+
const engine = new CredentialEngine(storage);
|
10
|
+
// Test email
|
11
|
+
const testEmail = 'test@example.com';
|
12
|
+
console.log('Starting email VC generation test...');
|
13
|
+
console.log('Test email:', testEmail);
|
14
|
+
// Generate and sign the email VC
|
15
|
+
const result = await engine.generateAndSignEmailVC(testEmail);
|
16
|
+
console.log('\nTest Results:');
|
17
|
+
console.log('-------------');
|
18
|
+
console.log('File ID:', result.fileId);
|
19
|
+
console.log('Signed VC:', JSON.stringify(result.signedVC, null, 2));
|
20
|
+
// Test retrieving the VC from storage
|
21
|
+
console.log('\nRetrieving VC from storage...');
|
22
|
+
const retrievedVC = await storage.retrieve(result.fileId);
|
23
|
+
console.log('Retrieved VC:', retrievedVC ? 'Success' : 'Failed');
|
24
|
+
console.log('\nTest completed successfully!');
|
25
|
+
}
|
26
|
+
catch (error) {
|
27
|
+
console.error('Test failed:', error);
|
28
|
+
}
|
29
|
+
}
|
30
|
+
// Run the test
|
31
|
+
testEmailVC().catch(console.error);
|
@@ -0,0 +1,75 @@
|
|
1
|
+
// @ts-nocheck
|
2
|
+
import { decodeSecretKeySeed } from 'bnid';
|
3
|
+
import { Ed25519VerificationKey2020 } from '@digitalbazaar/ed25519-verification-key-2020';
|
4
|
+
import { driver as keyDriver } from '@digitalbazaar/did-method-key';
|
5
|
+
import { CryptoLD } from 'crypto-ld';
|
6
|
+
// EXACT SAME SETUP AS YOUR WORKING SCRIPT
|
7
|
+
const cryptoLd = new CryptoLD();
|
8
|
+
cryptoLd.use(Ed25519VerificationKey2020);
|
9
|
+
// Reference decodeSeed implementation
|
10
|
+
const decodeSeed = async (secretKeySeed) => {
|
11
|
+
let secretKeySeedBytes;
|
12
|
+
if (secretKeySeed.startsWith('z')) {
|
13
|
+
secretKeySeedBytes = decodeSecretKeySeed({ secretKeySeed });
|
14
|
+
}
|
15
|
+
else if (secretKeySeed.length >= 32) {
|
16
|
+
secretKeySeedBytes = new TextEncoder().encode(secretKeySeed).slice(0, 32);
|
17
|
+
}
|
18
|
+
else {
|
19
|
+
throw TypeError('"secretKeySeed" must be at least 32 bytes, preferably multibase-encoded.');
|
20
|
+
}
|
21
|
+
return secretKeySeedBytes;
|
22
|
+
};
|
23
|
+
// EXACT SAME FUNCTION AS YOUR WORKING generateSeed BUT WITH PREDEFINED SEEDS
|
24
|
+
async function testGenerateSeed(encodedSeed) {
|
25
|
+
const seed = await decodeSeed(encodedSeed);
|
26
|
+
let didDocument;
|
27
|
+
// EXACT SAME CODE AS YOUR WORKING SCRIPT
|
28
|
+
const didKeyDriver = keyDriver();
|
29
|
+
didKeyDriver.use({
|
30
|
+
multibaseMultikeyHeader: 'z6Mk',
|
31
|
+
fromMultibase: Ed25519VerificationKey2020.from,
|
32
|
+
});
|
33
|
+
const verificationKeyPair = await Ed25519VerificationKey2020.generate({
|
34
|
+
seed,
|
35
|
+
});
|
36
|
+
({ didDocument } = await didKeyDriver.fromKeyPair({ verificationKeyPair }));
|
37
|
+
const did = didDocument.id;
|
38
|
+
return { seed: encodedSeed, decodedSeed: seed, did, didDocument, publicKey: verificationKeyPair.publicKeyMultibase };
|
39
|
+
}
|
40
|
+
// Test with different seeds
|
41
|
+
async function testDifferentSeeds() {
|
42
|
+
console.log('Testing different seeds with EXACT working script approach:\n');
|
43
|
+
const testSeeds = [
|
44
|
+
'z1AjjnVSNmZot5TJcgtFYhn83WASAcphrqgE95AQ436hrGR',
|
45
|
+
'z1AibHGc5Zek2kvdSyC22xGLZCvV7b77KWBFWiQmZGnD6rV',
|
46
|
+
'z1AgfwVqaN3zX1kw2iTvLLekCbXNRJA7XbiFZCQv9TfgKCT',
|
47
|
+
];
|
48
|
+
const results = [];
|
49
|
+
for (const testSeed of testSeeds) {
|
50
|
+
console.log(`Testing seed: ${testSeed}`);
|
51
|
+
console.log(`Decoded hex: ${Buffer.from(await decodeSeed(testSeed)).toString('hex')}`);
|
52
|
+
try {
|
53
|
+
const result = await testGenerateSeed(testSeed);
|
54
|
+
console.log(`Generated DID: ${result.did}`);
|
55
|
+
console.log(`Public key: ${result.publicKey}`);
|
56
|
+
results.push(result);
|
57
|
+
}
|
58
|
+
catch (error) {
|
59
|
+
console.error(`Error: ${error.message}`);
|
60
|
+
}
|
61
|
+
console.log('---');
|
62
|
+
}
|
63
|
+
console.log('\nResults:');
|
64
|
+
const uniqueDids = new Set(results.map((r) => r.did));
|
65
|
+
const uniqueKeys = new Set(results.map((r) => r.publicKey));
|
66
|
+
console.log(`Total results: ${results.length}`);
|
67
|
+
console.log(`Unique DIDs: ${uniqueDids.size}`);
|
68
|
+
console.log(`Unique public keys: ${uniqueKeys.size}`);
|
69
|
+
console.log(`Expected unique: ${testSeeds.length}`);
|
70
|
+
console.log(`Success: ${uniqueDids.size === testSeeds.length ? '✓ YES' : '✗ NO'}`);
|
71
|
+
results.forEach((result, i) => {
|
72
|
+
console.log(`${i + 1}. ${result.seed} -> ${result.did}`);
|
73
|
+
});
|
74
|
+
}
|
75
|
+
testDifferentSeeds().catch(console.error);
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { CredentialEngine } from '../models/CredentialEngine.js';
|
2
|
+
import { GoogleDriveStorage } from '../models/GoogleDriveStorage.js';
|
3
|
+
async function testEmailVC() {
|
4
|
+
try {
|
5
|
+
// Initialize storage and engine
|
6
|
+
const storage = new GoogleDriveStorage('YOUR_ACCESS_TOKEN'); // Replace with actual access token
|
7
|
+
const engine = new CredentialEngine(storage);
|
8
|
+
// Test email
|
9
|
+
const testEmail = 'test@example.com';
|
10
|
+
console.log('Starting email VC generation test...');
|
11
|
+
console.log('Test email:', testEmail);
|
12
|
+
// Generate and sign the email VC
|
13
|
+
const result = await engine.generateAndSignEmailVC(testEmail);
|
14
|
+
console.log('\nTest Results:');
|
15
|
+
console.log('-------------');
|
16
|
+
console.log('File ID:', result.fileId);
|
17
|
+
console.log('Signed VC:', JSON.stringify(result.signedVC, null, 2));
|
18
|
+
// Test retrieving the VC from storage
|
19
|
+
console.log('\nRetrieving VC from storage...');
|
20
|
+
const retrievedVC = await storage.retrieve(result.fileId);
|
21
|
+
console.log('Retrieved VC:', retrievedVC ? 'Success' : 'Failed');
|
22
|
+
console.log('\nTest completed successfully!');
|
23
|
+
}
|
24
|
+
catch (error) {
|
25
|
+
console.error('Test failed:', error);
|
26
|
+
}
|
27
|
+
}
|
28
|
+
// Run the test
|
29
|
+
testEmailVC().catch(console.error);
|
@@ -78,5 +78,14 @@ export declare class CredentialEngine {
|
|
78
78
|
* @returns
|
79
79
|
*/
|
80
80
|
signPresentation(presentation: any): Promise<any>;
|
81
|
+
/**
|
82
|
+
* Generate and sign an email Verifiable Credential (VC)
|
83
|
+
* @param {string} email - The email address to create the VC for
|
84
|
+
* @returns {Promise<{signedVC: any, fileId: string}>} The signed VC and its Google Drive file ID
|
85
|
+
*/
|
86
|
+
generateAndSignEmailVC(email: string): Promise<{
|
87
|
+
signedVC: any;
|
88
|
+
fileId: string;
|
89
|
+
}>;
|
81
90
|
}
|
82
91
|
export {};
|
@@ -4,6 +4,18 @@ export declare class ResumeVC {
|
|
4
4
|
issuerDid: string;
|
5
5
|
keyPair: any;
|
6
6
|
}): Promise<any>;
|
7
|
+
generateProfessionalSummary: (aff: any) => {
|
8
|
+
'@context': (string | {
|
9
|
+
'@vocab': string;
|
10
|
+
narrative: string;
|
11
|
+
})[];
|
12
|
+
type: string[];
|
13
|
+
issuer: any;
|
14
|
+
issuanceDate: string;
|
15
|
+
credentialSubject: {
|
16
|
+
narrative: any;
|
17
|
+
};
|
18
|
+
};
|
7
19
|
generateUnsignedCredential({ formData, issuerDid }: {
|
8
20
|
formData: any;
|
9
21
|
issuerDid: string;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,18 @@
|
|
1
|
+
export declare function decodeSeed(encodedSeed: string): Promise<Uint8Array>;
|
2
|
+
export declare const getDidFromEnvSeed: () => Promise<{
|
3
|
+
keyPair: any;
|
4
|
+
didDocument: {
|
5
|
+
'@context': string[];
|
6
|
+
id: string;
|
7
|
+
verificationMethod: {
|
8
|
+
id: string;
|
9
|
+
type: string;
|
10
|
+
controller: string;
|
11
|
+
publicKeyMultibase: any;
|
12
|
+
}[];
|
13
|
+
authentication: string[];
|
14
|
+
assertionMethod: string[];
|
15
|
+
capabilityDelegation: string[];
|
16
|
+
capabilityInvocation: string[];
|
17
|
+
};
|
18
|
+
}>;
|
package/dist/utils/context.js
CHANGED
@@ -6,7 +6,7 @@ export const inlineResumeContext = {
|
|
6
6
|
formattedName: 'https://schema.org/formattedName',
|
7
7
|
primaryLanguage: 'https://schema.org/primaryLanguage',
|
8
8
|
// Narrative
|
9
|
-
|
9
|
+
professionalSummary: 'https://schema.org/professionalSummary',
|
10
10
|
text: 'https://schema.org/text',
|
11
11
|
// Contact Information
|
12
12
|
contact: 'https://schema.org/ContactPoint',
|
@@ -0,0 +1,77 @@
|
|
1
|
+
import { Ed25519VerificationKey2020 } from '@digitalbazaar/ed25519-verification-key-2020';
|
2
|
+
import { base58btc } from 'multiformats/bases/base58';
|
3
|
+
export async function decodeSeed(encodedSeed) {
|
4
|
+
try {
|
5
|
+
// Ensure the seed has the 'z' prefix
|
6
|
+
const seedWithPrefix = encodedSeed.startsWith('z') ? encodedSeed : `z${encodedSeed}`;
|
7
|
+
// Decode the entire string including the prefix
|
8
|
+
const decoded = base58btc.decode(seedWithPrefix);
|
9
|
+
// The decoded data includes a multicodec header (2 bytes: 0x00 0x20)
|
10
|
+
// We need to remove this header to get the actual 32-byte seed
|
11
|
+
if (decoded.length === 34 && decoded[0] === 0x00 && decoded[1] === 0x20) {
|
12
|
+
// Skip the first 2 bytes to get the actual seed
|
13
|
+
const seed = new Uint8Array(decoded.slice(2));
|
14
|
+
console.log('Decoded seed (removed 2-byte header):', Buffer.from(seed).toString('hex'));
|
15
|
+
return seed;
|
16
|
+
}
|
17
|
+
// If it's already 32 bytes, return as is
|
18
|
+
if (decoded.length === 32) {
|
19
|
+
console.log('Decoded seed (already 32 bytes):', Buffer.from(decoded).toString('hex'));
|
20
|
+
return new Uint8Array(decoded);
|
21
|
+
}
|
22
|
+
throw new Error(`Invalid seed length: ${decoded.length} bytes (expected 32 bytes after removing any headers)`);
|
23
|
+
}
|
24
|
+
catch (error) {
|
25
|
+
console.error('Error decoding seed:', error);
|
26
|
+
throw error;
|
27
|
+
}
|
28
|
+
}
|
29
|
+
export const getDidFromEnvSeed = async () => {
|
30
|
+
// Get seed from environment variable
|
31
|
+
const encodedSeed = process.env.SEED;
|
32
|
+
if (!encodedSeed) {
|
33
|
+
throw new Error('SEED environment variable not set');
|
34
|
+
}
|
35
|
+
console.log('Using seed from environment:', encodedSeed);
|
36
|
+
// Decode the seed (this will remove the 2-byte header if present)
|
37
|
+
const seed = await decodeSeed(encodedSeed);
|
38
|
+
console.log('Decoded seed length:', seed.length);
|
39
|
+
// Create key pair from seed
|
40
|
+
const verificationKeyPair = await Ed25519VerificationKey2020.generate({
|
41
|
+
seed: seed,
|
42
|
+
type: 'Ed25519VerificationKey2020',
|
43
|
+
});
|
44
|
+
console.log('Generated public key:', verificationKeyPair.publicKeyMultibase);
|
45
|
+
// Create DID manually to avoid key agreement issues
|
46
|
+
const fingerprint = verificationKeyPair.fingerprint();
|
47
|
+
const did = `did:key:${fingerprint}`;
|
48
|
+
// Create a proper DID document
|
49
|
+
const didDocument = {
|
50
|
+
'@context': [
|
51
|
+
'https://www.w3.org/ns/did/v1',
|
52
|
+
'https://w3id.org/security/suites/ed25519-2020/v1',
|
53
|
+
'https://w3id.org/security/suites/x25519-2020/v1',
|
54
|
+
],
|
55
|
+
id: did,
|
56
|
+
verificationMethod: [
|
57
|
+
{
|
58
|
+
id: `${did}#${fingerprint}`,
|
59
|
+
type: 'Ed25519VerificationKey2020',
|
60
|
+
controller: did,
|
61
|
+
publicKeyMultibase: verificationKeyPair.publicKeyMultibase,
|
62
|
+
},
|
63
|
+
],
|
64
|
+
authentication: [`${did}#${fingerprint}`],
|
65
|
+
assertionMethod: [`${did}#${fingerprint}`],
|
66
|
+
capabilityDelegation: [`${did}#${fingerprint}`],
|
67
|
+
capabilityInvocation: [`${did}#${fingerprint}`],
|
68
|
+
};
|
69
|
+
// Set the key ID and controller on the key pair
|
70
|
+
verificationKeyPair.id = `${did}#${fingerprint}`;
|
71
|
+
verificationKeyPair.controller = did;
|
72
|
+
console.log('Generated DID:', did);
|
73
|
+
return {
|
74
|
+
keyPair: verificationKeyPair,
|
75
|
+
didDocument,
|
76
|
+
};
|
77
|
+
};
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@cooperation/vc-storage",
|
3
3
|
"type": "module",
|
4
|
-
"version": "1.0.
|
4
|
+
"version": "1.0.30",
|
5
5
|
"description": "Sign and store your verifiable credentials.",
|
6
6
|
"main": "dist/index.js",
|
7
7
|
"types": "dist/types/index.d.ts",
|
@@ -17,12 +17,16 @@
|
|
17
17
|
"license": "ISC",
|
18
18
|
"dependencies": {
|
19
19
|
"@digitalbazaar/did-method-key": "^5.2.0",
|
20
|
-
"@digitalbazaar/ed25519-signature-2020": "^5.
|
20
|
+
"@digitalbazaar/ed25519-signature-2020": "^5.4.0",
|
21
21
|
"@digitalbazaar/ed25519-verification-key-2020": "^4.1.0",
|
22
22
|
"@digitalbazaar/vc": "^6.3.0",
|
23
|
+
"add": "^2.0.6",
|
24
|
+
"bnid": "^3.0.0",
|
23
25
|
"crypto-js": "^4.2.0",
|
26
|
+
"crypto-ld": "^7.0.0",
|
24
27
|
"ethers": "^6.13.2",
|
25
28
|
"jest": "^29.7.0",
|
29
|
+
"multiformats": "^13.3.6",
|
26
30
|
"ts-jest": "^29.2.5",
|
27
31
|
"ts-node": "^10.9.2",
|
28
32
|
"tsc": "^2.0.4",
|