-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsignCred.ts
More file actions
210 lines (191 loc) · 6.86 KB
/
signCred.ts
File metadata and controls
210 lines (191 loc) · 6.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
import { CredentialEngine, GoogleDriveStorage } from '@cooperation/vc-storage'
import { FormData } from '../[formType]/form/types/Types'
import { normalizeSkillClaimFormData, SkillClaimFormData } from './normalization/hrContextSkillClaim'
import { ISkillClaimCredential } from 'hr-context'
interface RecommendationI {
recommendationText: string
qualifications: string
expirationDate: string
fullName: string
howKnow: string
explainAnswer: string
portfolio: { googleId?: string; name: string; url: string }[]
}
function getCredentialEngine(accessToken: string): CredentialEngine {
if (!accessToken) {
throw new Error('Access token is required to instantiate CredentialEngine.')
}
const storage = new GoogleDriveStorage(accessToken)
return new CredentialEngine(storage)
}
/**
* Create a DID using MetaMask address
* @param metaMaskAddress - The user's MetaMask address
* @param accessToken - The access token for authentication
* @returns DID Document, Key Pair, and Issuer ID
*/
export async function createDIDWithMetaMask(
metaMaskAddress: string,
accessToken: string
) {
const credentialEngine = getCredentialEngine(accessToken)
const { didDocument, keyPair } = await credentialEngine.createWalletDID(metaMaskAddress)
return { didDocument, keyPair, issuerId: didDocument.id }
}
/**
* Create a DID
* @param accessToken - The access token for authentication
* @returns DID Document, Key Pair, and Issuer ID
*/
export const createDID = async (accessToken: string) => {
const credentialEngine = getCredentialEngine(accessToken)
const { didDocument, keyPair } = await credentialEngine.createDID()
console.log('DID:', didDocument)
return { didDocument, keyPair, issuerId: didDocument.id }
}
/**
* Sign a Verifiable Credential
* @param accessToken - The access token for authentication
* @param data - The data to include in the credential
* @param issuerDid - The issuer's DID
* @param keyPair - The key pair used for signing
* @param type - The type of credential ('RECOMMENDATION' or 'VC')
* @param formType - The form type to determine specific credential type
* @returns The signed Verifiable Credential
*/
const signCred = async (
accessToken: string,
data: any,
issuerDid: string,
keyPair: string,
type: 'RECOMMENDATION' | 'VC',
formType?: string
) => {
if (!accessToken) {
throw new Error('Access token is not provided')
}
console.log('[VC SIGNING] formType:', formType, 'type:', type)
let signedVC
try {
const credentialEngine = getCredentialEngine(accessToken)
if (type === 'RECOMMENDATION') {
const formData = generateRecommendationData(data)
console.log('[VC SIGNING] RECOMMENDATION data:', formData)
signedVC = await credentialEngine.signVC({
data: formData,
type: 'RECOMMENDATION',
keyPair,
issuerId: issuerDid
})
} else {
// Use specific credential signing methods based on form type
const processedData = processCredentialData(data, formType)
console.log('[VC SIGNING] VC data for formType:', formType, 'data:', processedData)
switch (formType) {
case 'role':
signedVC = await credentialEngine.signEmploymentCredential(
processedData,
keyPair,
issuerDid
)
break
case 'volunteer':
signedVC = await credentialEngine.signVolunteeringCredential(
processedData,
keyPair,
issuerDid
)
break
case 'performance-review':
signedVC = await credentialEngine.signPerformanceReviewCredential(
processedData,
keyPair,
issuerDid
)
break
case 'skill':
case 'identity-verification':
default:
// Sign skill claim credential using the HR Context data model
const normalizedData: SkillClaimFormData = normalizeSkillClaimFormData(processedData as unknown as FormData)
signedVC = await credentialEngine.signSkillClaimVC(normalizedData as unknown as ISkillClaimCredential, keyPair, issuerDid)
break
}
}
return signedVC
} catch (error) {
console.error('Error during VC signing:', error)
throw error
}
}
/**
* Process credential data to match expected format for the package
* @param data - The form data
* @param formType - The form type to determine specific processing
* @returns Processed data object
*/
const processCredentialData = (data: FormData, formType?: string) => {
// For skills and identity verification, we need to transform the data into the old format
if (formType === 'skill' || formType === 'identity-verification') {
return {
expirationDate: new Date(
new Date().setFullYear(new Date().getFullYear() + 1)
).toISOString(),
fullName: data.fullName || '',
duration: data.credentialDuration || '',
criteriaNarrative: data.credentialDescription || '',
achievementDescription:
typeof data.description === 'string'
? data.description
: String(data.description || ''),
achievementName: data.credentialName || '',
portfolio:
data.portfolio && data.portfolio.length > 0
? data.portfolio.map(({ googleId, ...rest }) => rest)
: [{ name: '', url: '' }],
evidenceLink: data?.evidenceLink || '',
evidenceDescription: data.evidenceDescription || '',
credentialType: formType || data.persons || ''
}
}
// For employment, volunteer, and performance review, pass the form data directly
const processedData = { ...data }
// Remove UI-specific fields that shouldn't be in the credential
delete processedData.showDuration
delete processedData.currentVolunteer
delete processedData.timeSpent
// Clean up portfolio data (remove googleId for the credential)
if (processedData.portfolio && processedData.portfolio.length > 0) {
processedData.portfolio = processedData.portfolio.map(({ googleId, ...rest }) => rest)
}
// Add required fields that might be missing
if (!processedData.evidenceDescription) {
processedData.evidenceDescription = ''
}
// Add expirationDate (required by the package)
if (!processedData.expirationDate) {
processedData.expirationDate = new Date(
new Date().setFullYear(new Date().getFullYear() + 1)
).toISOString()
}
return processedData
}
/**
* Generate credential data for 'RECOMMENDATION' type
* @param data - The form data
* @returns RecommendationI object
*/
const generateRecommendationData = (data: any): RecommendationI => {
return {
recommendationText: data.recommendationText,
qualifications: data.qualifications,
expirationDate: new Date(
new Date().setFullYear(new Date().getFullYear() + 1)
).toISOString(),
fullName: data.fullName,
howKnow: data.howKnow,
explainAnswer: data.explainAnswer,
portfolio: data.portfolio || []
}
}
export { signCred }