@idos-network/issuer 1.0.0-rc.1.0
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 +21 -0
- package/README.md +19 -0
- package/dist/index.d.ts +205 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21274 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
- package/src/assets/add-user.drawio.svg +558 -0
- package/src/assets/country-codes.xml +271 -0
- package/src/assets/ed25519-signature-2020-v1.json +93 -0
- package/src/assets/idos-credential-subject-v1.json +32 -0
- package/src/assets/idos-credentials-v1.json +9 -0
- package/src/assets/v1.json +273 -0
- package/src/index.ts +155 -0
- package/src/services/credential.service.ts +207 -0
- package/src/services/credentials-builder.service.ts +203 -0
- package/src/services/credentials-builder.test.ts +100 -0
- package/src/services/grant.service.ts +59 -0
- package/src/services/user.service.ts +75 -0
- package/src/types.d.ts +4 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@context": {
|
|
3
|
+
"@version": 1.1,
|
|
4
|
+
"@protected": true,
|
|
5
|
+
|
|
6
|
+
"id": "@id",
|
|
7
|
+
"type": "@type",
|
|
8
|
+
|
|
9
|
+
"VerifiableCredential": {
|
|
10
|
+
"@id": "https://www.w3.org/2018/credentials#VerifiableCredential",
|
|
11
|
+
"@context": {
|
|
12
|
+
"@version": 1.1,
|
|
13
|
+
"@protected": true,
|
|
14
|
+
|
|
15
|
+
"id": "@id",
|
|
16
|
+
"type": "@type",
|
|
17
|
+
|
|
18
|
+
"cred": "https://www.w3.org/2018/credentials#",
|
|
19
|
+
"sec": "https://w3id.org/security#",
|
|
20
|
+
"xsd": "http://www.w3.org/2001/XMLSchema#",
|
|
21
|
+
|
|
22
|
+
"credentialSchema": {
|
|
23
|
+
"@id": "cred:credentialSchema",
|
|
24
|
+
"@type": "@id",
|
|
25
|
+
"@context": {
|
|
26
|
+
"@version": 1.1,
|
|
27
|
+
"@protected": true,
|
|
28
|
+
|
|
29
|
+
"id": "@id",
|
|
30
|
+
"type": "@type",
|
|
31
|
+
|
|
32
|
+
"cred": "https://www.w3.org/2018/credentials#",
|
|
33
|
+
|
|
34
|
+
"JsonSchemaValidator2018": "cred:JsonSchemaValidator2018"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"credentialStatus": { "@id": "cred:credentialStatus", "@type": "@id" },
|
|
38
|
+
"credentialSubject": { "@id": "cred:credentialSubject", "@type": "@id" },
|
|
39
|
+
"evidence": { "@id": "cred:evidence", "@type": "@id" },
|
|
40
|
+
"expirationDate": { "@id": "cred:expirationDate", "@type": "xsd:dateTime" },
|
|
41
|
+
"holder": { "@id": "cred:holder", "@type": "@id" },
|
|
42
|
+
"issued": { "@id": "cred:issued", "@type": "xsd:dateTime" },
|
|
43
|
+
"issuer": { "@id": "cred:issuer", "@type": "@id" },
|
|
44
|
+
"issuanceDate": { "@id": "cred:issuanceDate", "@type": "xsd:dateTime" },
|
|
45
|
+
"proof": { "@id": "sec:proof", "@type": "@id", "@container": "@graph" },
|
|
46
|
+
"refreshService": {
|
|
47
|
+
"@id": "cred:refreshService",
|
|
48
|
+
"@type": "@id",
|
|
49
|
+
"@context": {
|
|
50
|
+
"@version": 1.1,
|
|
51
|
+
"@protected": true,
|
|
52
|
+
|
|
53
|
+
"id": "@id",
|
|
54
|
+
"type": "@type",
|
|
55
|
+
|
|
56
|
+
"cred": "https://www.w3.org/2018/credentials#",
|
|
57
|
+
|
|
58
|
+
"ManualRefreshService2018": "cred:ManualRefreshService2018"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"termsOfUse": { "@id": "cred:termsOfUse", "@type": "@id" },
|
|
62
|
+
"validFrom": { "@id": "cred:validFrom", "@type": "xsd:dateTime" },
|
|
63
|
+
"validUntil": { "@id": "cred:validUntil", "@type": "xsd:dateTime" }
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
"VerifiablePresentation": {
|
|
68
|
+
"@id": "https://www.w3.org/2018/credentials#VerifiablePresentation",
|
|
69
|
+
"@context": {
|
|
70
|
+
"@version": 1.1,
|
|
71
|
+
"@protected": true,
|
|
72
|
+
|
|
73
|
+
"id": "@id",
|
|
74
|
+
"type": "@type",
|
|
75
|
+
|
|
76
|
+
"cred": "https://www.w3.org/2018/credentials#",
|
|
77
|
+
"sec": "https://w3id.org/security#",
|
|
78
|
+
|
|
79
|
+
"holder": { "@id": "cred:holder", "@type": "@id" },
|
|
80
|
+
"proof": { "@id": "sec:proof", "@type": "@id", "@container": "@graph" },
|
|
81
|
+
"verifiableCredential": {
|
|
82
|
+
"@id": "cred:verifiableCredential",
|
|
83
|
+
"@type": "@id",
|
|
84
|
+
"@container": "@graph"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
"EcdsaSecp256k1Signature2019": {
|
|
90
|
+
"@id": "https://w3id.org/security#EcdsaSecp256k1Signature2019",
|
|
91
|
+
"@context": {
|
|
92
|
+
"@version": 1.1,
|
|
93
|
+
"@protected": true,
|
|
94
|
+
|
|
95
|
+
"id": "@id",
|
|
96
|
+
"type": "@type",
|
|
97
|
+
|
|
98
|
+
"sec": "https://w3id.org/security#",
|
|
99
|
+
"xsd": "http://www.w3.org/2001/XMLSchema#",
|
|
100
|
+
|
|
101
|
+
"challenge": "sec:challenge",
|
|
102
|
+
"created": { "@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime" },
|
|
103
|
+
"domain": "sec:domain",
|
|
104
|
+
"expires": { "@id": "sec:expiration", "@type": "xsd:dateTime" },
|
|
105
|
+
"jws": "sec:jws",
|
|
106
|
+
"nonce": "sec:nonce",
|
|
107
|
+
"proofPurpose": {
|
|
108
|
+
"@id": "sec:proofPurpose",
|
|
109
|
+
"@type": "@vocab",
|
|
110
|
+
"@context": {
|
|
111
|
+
"@version": 1.1,
|
|
112
|
+
"@protected": true,
|
|
113
|
+
|
|
114
|
+
"id": "@id",
|
|
115
|
+
"type": "@type",
|
|
116
|
+
|
|
117
|
+
"sec": "https://w3id.org/security#",
|
|
118
|
+
|
|
119
|
+
"assertionMethod": {
|
|
120
|
+
"@id": "sec:assertionMethod",
|
|
121
|
+
"@type": "@id",
|
|
122
|
+
"@container": "@set"
|
|
123
|
+
},
|
|
124
|
+
"authentication": {
|
|
125
|
+
"@id": "sec:authenticationMethod",
|
|
126
|
+
"@type": "@id",
|
|
127
|
+
"@container": "@set"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"proofValue": "sec:proofValue",
|
|
132
|
+
"verificationMethod": { "@id": "sec:verificationMethod", "@type": "@id" }
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
"EcdsaSecp256r1Signature2019": {
|
|
137
|
+
"@id": "https://w3id.org/security#EcdsaSecp256r1Signature2019",
|
|
138
|
+
"@context": {
|
|
139
|
+
"@version": 1.1,
|
|
140
|
+
"@protected": true,
|
|
141
|
+
|
|
142
|
+
"id": "@id",
|
|
143
|
+
"type": "@type",
|
|
144
|
+
|
|
145
|
+
"sec": "https://w3id.org/security#",
|
|
146
|
+
"xsd": "http://www.w3.org/2001/XMLSchema#",
|
|
147
|
+
|
|
148
|
+
"challenge": "sec:challenge",
|
|
149
|
+
"created": { "@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime" },
|
|
150
|
+
"domain": "sec:domain",
|
|
151
|
+
"expires": { "@id": "sec:expiration", "@type": "xsd:dateTime" },
|
|
152
|
+
"jws": "sec:jws",
|
|
153
|
+
"nonce": "sec:nonce",
|
|
154
|
+
"proofPurpose": {
|
|
155
|
+
"@id": "sec:proofPurpose",
|
|
156
|
+
"@type": "@vocab",
|
|
157
|
+
"@context": {
|
|
158
|
+
"@version": 1.1,
|
|
159
|
+
"@protected": true,
|
|
160
|
+
|
|
161
|
+
"id": "@id",
|
|
162
|
+
"type": "@type",
|
|
163
|
+
|
|
164
|
+
"sec": "https://w3id.org/security#",
|
|
165
|
+
|
|
166
|
+
"assertionMethod": {
|
|
167
|
+
"@id": "sec:assertionMethod",
|
|
168
|
+
"@type": "@id",
|
|
169
|
+
"@container": "@set"
|
|
170
|
+
},
|
|
171
|
+
"authentication": {
|
|
172
|
+
"@id": "sec:authenticationMethod",
|
|
173
|
+
"@type": "@id",
|
|
174
|
+
"@container": "@set"
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
"proofValue": "sec:proofValue",
|
|
179
|
+
"verificationMethod": { "@id": "sec:verificationMethod", "@type": "@id" }
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
"Ed25519Signature2018": {
|
|
184
|
+
"@id": "https://w3id.org/security#Ed25519Signature2018",
|
|
185
|
+
"@context": {
|
|
186
|
+
"@version": 1.1,
|
|
187
|
+
"@protected": true,
|
|
188
|
+
|
|
189
|
+
"id": "@id",
|
|
190
|
+
"type": "@type",
|
|
191
|
+
|
|
192
|
+
"sec": "https://w3id.org/security#",
|
|
193
|
+
"xsd": "http://www.w3.org/2001/XMLSchema#",
|
|
194
|
+
|
|
195
|
+
"challenge": "sec:challenge",
|
|
196
|
+
"created": { "@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime" },
|
|
197
|
+
"domain": "sec:domain",
|
|
198
|
+
"expires": { "@id": "sec:expiration", "@type": "xsd:dateTime" },
|
|
199
|
+
"jws": "sec:jws",
|
|
200
|
+
"nonce": "sec:nonce",
|
|
201
|
+
"proofPurpose": {
|
|
202
|
+
"@id": "sec:proofPurpose",
|
|
203
|
+
"@type": "@vocab",
|
|
204
|
+
"@context": {
|
|
205
|
+
"@version": 1.1,
|
|
206
|
+
"@protected": true,
|
|
207
|
+
|
|
208
|
+
"id": "@id",
|
|
209
|
+
"type": "@type",
|
|
210
|
+
|
|
211
|
+
"sec": "https://w3id.org/security#",
|
|
212
|
+
|
|
213
|
+
"assertionMethod": {
|
|
214
|
+
"@id": "sec:assertionMethod",
|
|
215
|
+
"@type": "@id",
|
|
216
|
+
"@container": "@set"
|
|
217
|
+
},
|
|
218
|
+
"authentication": {
|
|
219
|
+
"@id": "sec:authenticationMethod",
|
|
220
|
+
"@type": "@id",
|
|
221
|
+
"@container": "@set"
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
"proofValue": "sec:proofValue",
|
|
226
|
+
"verificationMethod": { "@id": "sec:verificationMethod", "@type": "@id" }
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
"RsaSignature2018": {
|
|
231
|
+
"@id": "https://w3id.org/security#RsaSignature2018",
|
|
232
|
+
"@context": {
|
|
233
|
+
"@version": 1.1,
|
|
234
|
+
"@protected": true,
|
|
235
|
+
|
|
236
|
+
"challenge": "sec:challenge",
|
|
237
|
+
"created": { "@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime" },
|
|
238
|
+
"domain": "sec:domain",
|
|
239
|
+
"expires": { "@id": "sec:expiration", "@type": "xsd:dateTime" },
|
|
240
|
+
"jws": "sec:jws",
|
|
241
|
+
"nonce": "sec:nonce",
|
|
242
|
+
"proofPurpose": {
|
|
243
|
+
"@id": "sec:proofPurpose",
|
|
244
|
+
"@type": "@vocab",
|
|
245
|
+
"@context": {
|
|
246
|
+
"@version": 1.1,
|
|
247
|
+
"@protected": true,
|
|
248
|
+
|
|
249
|
+
"id": "@id",
|
|
250
|
+
"type": "@type",
|
|
251
|
+
|
|
252
|
+
"sec": "https://w3id.org/security#",
|
|
253
|
+
|
|
254
|
+
"assertionMethod": {
|
|
255
|
+
"@id": "sec:assertionMethod",
|
|
256
|
+
"@type": "@id",
|
|
257
|
+
"@container": "@set"
|
|
258
|
+
},
|
|
259
|
+
"authentication": {
|
|
260
|
+
"@id": "sec:authenticationMethod",
|
|
261
|
+
"@type": "@id",
|
|
262
|
+
"@container": "@set"
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
"proofValue": "sec:proofValue",
|
|
267
|
+
"verificationMethod": { "@id": "sec:verificationMethod", "@type": "@id" }
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
"proof": { "@id": "https://w3id.org/security#proof", "@type": "@id", "@container": "@graph" }
|
|
272
|
+
}
|
|
273
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type EditCredentialAsIssuerParams,
|
|
3
|
+
createNodeKwilClient,
|
|
4
|
+
createServerKwilSigner,
|
|
5
|
+
} from "@idos-network/core";
|
|
6
|
+
import type {
|
|
7
|
+
idOSCredential,
|
|
8
|
+
idOSGrant,
|
|
9
|
+
idOSUser,
|
|
10
|
+
idOSUserAttribute,
|
|
11
|
+
idOSWallet,
|
|
12
|
+
} from "@idos-network/core/types";
|
|
13
|
+
import type { SignKeyPair } from "tweetnacl";
|
|
14
|
+
import {
|
|
15
|
+
CredentialService,
|
|
16
|
+
type DelegatedWriteGrantBaseParams,
|
|
17
|
+
type DelegatedWriteGrantParams,
|
|
18
|
+
} from "./services/credential.service";
|
|
19
|
+
import {
|
|
20
|
+
type CredentialFields,
|
|
21
|
+
type CredentialSubject,
|
|
22
|
+
CredentialsBuilderService,
|
|
23
|
+
type CredentialsIssuerConfig,
|
|
24
|
+
} from "./services/credentials-builder.service";
|
|
25
|
+
import { type CreateAccessGrantFromDAGParams, GrantService } from "./services/grant.service";
|
|
26
|
+
import {
|
|
27
|
+
type CreateProfileReqParams,
|
|
28
|
+
type CreateWalletReqParams,
|
|
29
|
+
type UpsertWalletReqParams,
|
|
30
|
+
UserService,
|
|
31
|
+
} from "./services/user.service";
|
|
32
|
+
|
|
33
|
+
type CreateIssuerParams = {
|
|
34
|
+
chainId?: string;
|
|
35
|
+
nodeUrl: string;
|
|
36
|
+
signingKeyPair: SignKeyPair;
|
|
37
|
+
encryptionSecretKey: Uint8Array;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export class idOSIssuer {
|
|
41
|
+
readonly #credentialService: CredentialService;
|
|
42
|
+
readonly #grantService: GrantService;
|
|
43
|
+
readonly #userService: UserService;
|
|
44
|
+
readonly #credentialsBuilderService: CredentialsBuilderService;
|
|
45
|
+
|
|
46
|
+
static async init(params: CreateIssuerParams): Promise<idOSIssuer> {
|
|
47
|
+
const kwilClient = await createNodeKwilClient({
|
|
48
|
+
nodeUrl: params.nodeUrl,
|
|
49
|
+
chainId: params.chainId,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const [signer] = createServerKwilSigner(params.signingKeyPair);
|
|
53
|
+
kwilClient.setSigner(signer);
|
|
54
|
+
|
|
55
|
+
const credentialService = new CredentialService(
|
|
56
|
+
kwilClient,
|
|
57
|
+
params.signingKeyPair,
|
|
58
|
+
params.encryptionSecretKey,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const grantService = new GrantService(kwilClient, params.encryptionSecretKey);
|
|
62
|
+
const userService = new UserService(kwilClient);
|
|
63
|
+
const credentialsBuilderService = new CredentialsBuilderService();
|
|
64
|
+
|
|
65
|
+
return new idOSIssuer(credentialService, grantService, userService, credentialsBuilderService);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private constructor(
|
|
69
|
+
credentialService: CredentialService,
|
|
70
|
+
grantService: GrantService,
|
|
71
|
+
userService: UserService,
|
|
72
|
+
credentialsBuilderService: CredentialsBuilderService,
|
|
73
|
+
) {
|
|
74
|
+
this.#credentialService = credentialService;
|
|
75
|
+
this.#grantService = grantService;
|
|
76
|
+
this.#userService = userService;
|
|
77
|
+
this.#credentialsBuilderService = credentialsBuilderService;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// User Service facade methods
|
|
81
|
+
async hasProfile(userAddress: string): Promise<boolean> {
|
|
82
|
+
return this.#userService.hasProfile(userAddress);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async createUserProfile(params: CreateProfileReqParams): Promise<idOSUser> {
|
|
86
|
+
return this.#userService.createUserProfile(params);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async upsertWalletAsInserter(params: UpsertWalletReqParams): Promise<idOSWallet> {
|
|
90
|
+
return this.#userService.upsertWalletAsInserter(params);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async createUser(
|
|
94
|
+
user: CreateProfileReqParams,
|
|
95
|
+
wallet: CreateWalletReqParams,
|
|
96
|
+
): Promise<[idOSUser, idOSWallet]> {
|
|
97
|
+
return this.#userService.createUser(user, wallet);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async createCredentialByDelegatedWriteGrant(
|
|
101
|
+
credentialParams: DelegatedWriteGrantBaseParams,
|
|
102
|
+
delegatedWriteGrant: DelegatedWriteGrantParams,
|
|
103
|
+
consumerEncryptionPublicKey?: Uint8Array,
|
|
104
|
+
): Promise<{
|
|
105
|
+
originalCredential: Omit<idOSCredential, "user_id">;
|
|
106
|
+
copyCredential: Omit<idOSCredential, "user_id">;
|
|
107
|
+
}> {
|
|
108
|
+
return this.#credentialService.createCredentialByDelegatedWriteGrant(
|
|
109
|
+
credentialParams,
|
|
110
|
+
delegatedWriteGrant,
|
|
111
|
+
consumerEncryptionPublicKey,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async editCredentialAsIssuer(
|
|
116
|
+
publicNotesId: string,
|
|
117
|
+
publicNotes: string,
|
|
118
|
+
): Promise<EditCredentialAsIssuerParams | null> {
|
|
119
|
+
return this.#credentialService.editCredentialAsIssuer(publicNotesId, publicNotes);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async getCredentialIdByContentHash(contentHash: string): Promise<string | null> {
|
|
123
|
+
return this.#credentialService.getCredentialIdByContentHash(contentHash);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async getSharedCredential(id: string): Promise<idOSCredential | null> {
|
|
127
|
+
return this.#credentialService.getSharedCredential(id);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async createAccessGrantFromDAG(
|
|
131
|
+
params: CreateAccessGrantFromDAGParams,
|
|
132
|
+
): Promise<CreateAccessGrantFromDAGParams | null> {
|
|
133
|
+
return this.#grantService.createAccessGrantFromDAG(
|
|
134
|
+
params,
|
|
135
|
+
(contentHash: string) => this.getCredentialIdByContentHash(contentHash),
|
|
136
|
+
(id: string) => this.getSharedCredential(id),
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async buildCredentials(
|
|
141
|
+
fields: CredentialFields,
|
|
142
|
+
subject: CredentialSubject,
|
|
143
|
+
issuer: CredentialsIssuerConfig,
|
|
144
|
+
// biome-ignore lint/suspicious/noExplicitAny: Using `any` to avoid type errors.
|
|
145
|
+
): Promise<any> {
|
|
146
|
+
return this.#credentialsBuilderService.buildCredentials(fields, subject, issuer);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// biome-ignore lint/suspicious/noExplicitAny: Using `any` to avoid type errors.
|
|
150
|
+
buildDocumentLoader(): any {
|
|
151
|
+
return this.#credentialsBuilderService.buildDocumentLoader();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export type { idOSCredential, idOSGrant, idOSUser, idOSUserAttribute, idOSWallet };
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type KwilActionClient,
|
|
3
|
+
base64Decode,
|
|
4
|
+
base64Encode,
|
|
5
|
+
encryptContent,
|
|
6
|
+
hexEncode,
|
|
7
|
+
hexEncodeSha256Hash,
|
|
8
|
+
utf8Encode,
|
|
9
|
+
} from "@idos-network/core";
|
|
10
|
+
import {
|
|
11
|
+
type CreateCredentialByDelegatedWriteGrantParams,
|
|
12
|
+
type EditCredentialAsIssuerParams,
|
|
13
|
+
createCredentialByDelegatedWriteGrant,
|
|
14
|
+
editCredentialAsIssuer,
|
|
15
|
+
getCredentialIdByContentHash,
|
|
16
|
+
getSharedCredential,
|
|
17
|
+
} from "@idos-network/core/kwil-actions";
|
|
18
|
+
import type { idOSCredential } from "@idos-network/core/types";
|
|
19
|
+
import invariant from "tiny-invariant";
|
|
20
|
+
import nacl from "tweetnacl";
|
|
21
|
+
|
|
22
|
+
type InsertableIDOSCredential = Omit<idOSCredential, "id" | "original_id"> & {
|
|
23
|
+
id?: idOSCredential["id"];
|
|
24
|
+
content_hash?: string;
|
|
25
|
+
public_notes_signature: string;
|
|
26
|
+
broader_signature: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
type BuildInsertableIDOSCredentialArgs = {
|
|
30
|
+
userId: string;
|
|
31
|
+
publicNotes: string;
|
|
32
|
+
plaintextContent: Uint8Array;
|
|
33
|
+
recipientEncryptionPublicKey: Uint8Array;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type BaseCredentialParams = {
|
|
37
|
+
id?: string;
|
|
38
|
+
userId: string;
|
|
39
|
+
publicNotes: string;
|
|
40
|
+
plaintextContent: Uint8Array;
|
|
41
|
+
recipientEncryptionPublicKey: Uint8Array;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type DelegatedWriteGrantBaseParams = Omit<BaseCredentialParams, "userId">;
|
|
45
|
+
|
|
46
|
+
export type DelegatedWriteGrantParams = {
|
|
47
|
+
id: string;
|
|
48
|
+
ownerWalletIdentifier: string;
|
|
49
|
+
consumerWalletIdentifier: string;
|
|
50
|
+
issuerPublicKey: string;
|
|
51
|
+
accessGrantTimelock: string;
|
|
52
|
+
notUsableBefore: string;
|
|
53
|
+
notUsableAfter: string;
|
|
54
|
+
signature: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export class CredentialService {
|
|
58
|
+
readonly #kwilClient: KwilActionClient;
|
|
59
|
+
readonly #signingKeyPair: nacl.SignKeyPair;
|
|
60
|
+
readonly #encryptionSecretKey: Uint8Array;
|
|
61
|
+
|
|
62
|
+
constructor(
|
|
63
|
+
kwilClient: KwilActionClient,
|
|
64
|
+
signingKeyPair: nacl.SignKeyPair,
|
|
65
|
+
encryptionSecretKey: Uint8Array,
|
|
66
|
+
) {
|
|
67
|
+
this.#kwilClient = kwilClient;
|
|
68
|
+
this.#signingKeyPair = signingKeyPair;
|
|
69
|
+
this.#encryptionSecretKey = encryptionSecretKey;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
#buildInsertableIDOSCredential(args: BuildInsertableIDOSCredentialArgs): InsertableIDOSCredential;
|
|
73
|
+
|
|
74
|
+
#buildInsertableIDOSCredential(
|
|
75
|
+
args: Omit<BuildInsertableIDOSCredentialArgs, "userId">,
|
|
76
|
+
): Omit<InsertableIDOSCredential, "user_id">;
|
|
77
|
+
|
|
78
|
+
#buildInsertableIDOSCredential({
|
|
79
|
+
userId,
|
|
80
|
+
publicNotes,
|
|
81
|
+
plaintextContent,
|
|
82
|
+
recipientEncryptionPublicKey,
|
|
83
|
+
}: Omit<BuildInsertableIDOSCredentialArgs, "userId"> & { userId?: string }):
|
|
84
|
+
| InsertableIDOSCredential
|
|
85
|
+
| Omit<InsertableIDOSCredential, "user_id"> {
|
|
86
|
+
const ephemeralKeyPair = nacl.box.keyPair();
|
|
87
|
+
const content = encryptContent(
|
|
88
|
+
plaintextContent,
|
|
89
|
+
recipientEncryptionPublicKey,
|
|
90
|
+
ephemeralKeyPair.secretKey,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const public_notes_signature = base64Encode(
|
|
94
|
+
nacl.sign.detached(utf8Encode(publicNotes), this.#signingKeyPair.secretKey),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
user_id: userId,
|
|
99
|
+
content: base64Encode(content),
|
|
100
|
+
public_notes: publicNotes,
|
|
101
|
+
public_notes_signature,
|
|
102
|
+
|
|
103
|
+
broader_signature: base64Encode(
|
|
104
|
+
nacl.sign.detached(
|
|
105
|
+
Uint8Array.from([...base64Decode(public_notes_signature), ...content]),
|
|
106
|
+
this.#signingKeyPair.secretKey,
|
|
107
|
+
),
|
|
108
|
+
),
|
|
109
|
+
|
|
110
|
+
issuer_auth_public_key: hexEncode(this.#signingKeyPair.publicKey, true),
|
|
111
|
+
encryptor_public_key: base64Encode(ephemeralKeyPair.publicKey),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
#ensureEntityId<T extends { id?: string }>(entity: T): T & { id: string } {
|
|
116
|
+
if (!entity.id) {
|
|
117
|
+
(entity as T & { id: string }).id = crypto.randomUUID();
|
|
118
|
+
}
|
|
119
|
+
return entity as T & { id: string };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async createCredentialByDelegatedWriteGrant(
|
|
123
|
+
credentialParams: DelegatedWriteGrantBaseParams,
|
|
124
|
+
delegatedWriteGrant: DelegatedWriteGrantParams,
|
|
125
|
+
consumerEncryptionPublicKey?: Uint8Array,
|
|
126
|
+
): Promise<{
|
|
127
|
+
originalCredential: Omit<idOSCredential, "user_id">;
|
|
128
|
+
copyCredential: Omit<idOSCredential, "user_id">;
|
|
129
|
+
}> {
|
|
130
|
+
let recipientPublicKey = consumerEncryptionPublicKey;
|
|
131
|
+
if (!recipientPublicKey) {
|
|
132
|
+
// If we're not explicitly given a consumer enc pub key, we're assuming that the issuer is creating a copy
|
|
133
|
+
// for themselves. So, we derive the recipient encryption public key from the issuer's encryption secret key.
|
|
134
|
+
recipientPublicKey = nacl.box.keyPair.fromSecretKey(this.#encryptionSecretKey).publicKey;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const originalCredential = this.#ensureEntityId(
|
|
138
|
+
this.#buildInsertableIDOSCredential(credentialParams),
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const contentHash = hexEncodeSha256Hash(credentialParams.plaintextContent);
|
|
142
|
+
|
|
143
|
+
const copyCredential = this.#ensureEntityId(
|
|
144
|
+
this.#buildInsertableIDOSCredential({
|
|
145
|
+
publicNotes: "",
|
|
146
|
+
plaintextContent: credentialParams.plaintextContent,
|
|
147
|
+
recipientEncryptionPublicKey: recipientPublicKey,
|
|
148
|
+
}),
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const payload: CreateCredentialByDelegatedWriteGrantParams = {
|
|
152
|
+
issuer_auth_public_key: originalCredential.issuer_auth_public_key,
|
|
153
|
+
original_encryptor_public_key: originalCredential.encryptor_public_key,
|
|
154
|
+
original_credential_id: originalCredential.id,
|
|
155
|
+
original_content: originalCredential.content,
|
|
156
|
+
original_public_notes: originalCredential.public_notes,
|
|
157
|
+
original_public_notes_signature: originalCredential.public_notes_signature,
|
|
158
|
+
original_broader_signature: originalCredential.broader_signature,
|
|
159
|
+
copy_encryptor_public_key: copyCredential.encryptor_public_key,
|
|
160
|
+
copy_credential_id: copyCredential.id,
|
|
161
|
+
copy_content: copyCredential.content,
|
|
162
|
+
copy_public_notes_signature: copyCredential.public_notes_signature,
|
|
163
|
+
copy_broader_signature: copyCredential.broader_signature,
|
|
164
|
+
content_hash: contentHash,
|
|
165
|
+
dwg_owner: delegatedWriteGrant.ownerWalletIdentifier,
|
|
166
|
+
dwg_grantee: delegatedWriteGrant.consumerWalletIdentifier,
|
|
167
|
+
dwg_issuer_public_key: delegatedWriteGrant.issuerPublicKey,
|
|
168
|
+
dwg_id: delegatedWriteGrant.id,
|
|
169
|
+
dwg_access_grant_timelock: delegatedWriteGrant.accessGrantTimelock,
|
|
170
|
+
dwg_not_before: delegatedWriteGrant.notUsableBefore,
|
|
171
|
+
dwg_not_after: delegatedWriteGrant.notUsableAfter,
|
|
172
|
+
dwg_signature: delegatedWriteGrant.signature,
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
await createCredentialByDelegatedWriteGrant(this.#kwilClient, payload);
|
|
176
|
+
|
|
177
|
+
return { originalCredential, copyCredential };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async editCredentialAsIssuer(
|
|
181
|
+
publicNotesId: string,
|
|
182
|
+
publicNotes: string,
|
|
183
|
+
): Promise<EditCredentialAsIssuerParams | null> {
|
|
184
|
+
const payload = {
|
|
185
|
+
public_notes_id: publicNotesId,
|
|
186
|
+
public_notes: publicNotes,
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const result = await editCredentialAsIssuer(this.#kwilClient, payload);
|
|
190
|
+
|
|
191
|
+
return result ?? null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async getCredentialIdByContentHash(contentHash: string): Promise<string | null> {
|
|
195
|
+
const id = await getCredentialIdByContentHash(this.#kwilClient, contentHash);
|
|
196
|
+
|
|
197
|
+
invariant(id, "Required `idOSCredential` id not found");
|
|
198
|
+
|
|
199
|
+
return id;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async getSharedCredential(id: string): Promise<idOSCredential | null> {
|
|
203
|
+
const result = await getSharedCredential(this.#kwilClient, id);
|
|
204
|
+
|
|
205
|
+
return result ?? null;
|
|
206
|
+
}
|
|
207
|
+
}
|