@idos-network/client 1.2.0 → 2.2.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/README.md +15 -9
- package/dist/index.d.ts +48 -29
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -464
- package/dist/index.js.map +1 -1
- package/package.json +15 -9
- package/src/enclave/helpers.ts +10 -6
- package/src/enclave/iframe-enclave.test.ts +26 -0
- package/src/enclave/iframe-enclave.ts +6 -3
- package/src/index.test.ts +338 -0
- package/src/index.ts +277 -58
package/src/index.ts
CHANGED
|
@@ -3,28 +3,38 @@ import type {
|
|
|
3
3
|
EncryptionPasswordStore,
|
|
4
4
|
PublicEncryptionProfile,
|
|
5
5
|
} from "@idos-network/enclave";
|
|
6
|
-
import type { KwilSigner } from "@idos-network/kwil-js";
|
|
7
6
|
|
|
8
7
|
import {
|
|
9
|
-
|
|
8
|
+
buildEphemeralSignedCredentialContentReference,
|
|
9
|
+
buildPreliminaryIDOSCredential,
|
|
10
|
+
mapPreliminaryToIDOSCredential,
|
|
10
11
|
matchLevelOrHigher,
|
|
12
|
+
type PreliminaryIDOSCredential,
|
|
11
13
|
recordFilter,
|
|
14
|
+
type BaseLevel,
|
|
15
|
+
type Addon,
|
|
12
16
|
} from "@idos-network/credentials/utils";
|
|
13
17
|
import {
|
|
14
18
|
createClientKwilSigner,
|
|
19
|
+
createKgwAuthenticatedBlobGateway,
|
|
20
|
+
createNodeKwilClient,
|
|
15
21
|
createWebKwilClient,
|
|
22
|
+
isMmTokenAuth,
|
|
16
23
|
type KwilActionClient,
|
|
24
|
+
mmTokenCredentialContentUri,
|
|
17
25
|
signNearMessage,
|
|
18
26
|
type Wallet,
|
|
19
27
|
} from "@idos-network/kwil-infra";
|
|
20
28
|
import {
|
|
21
29
|
type AddAttributeInput,
|
|
22
30
|
type AddWalletInput,
|
|
31
|
+
type AddWalletMessageInput,
|
|
23
32
|
addWallet,
|
|
33
|
+
addWalletMessage as requestAddWalletMessage,
|
|
24
34
|
addWallets,
|
|
25
35
|
addAttribute as createAttribute,
|
|
26
|
-
|
|
27
|
-
|
|
36
|
+
createPreliminaryCredential,
|
|
37
|
+
type CreatePreliminaryCredentialInput,
|
|
28
38
|
dwgMessage,
|
|
29
39
|
type GetAccessGrantsGrantedInput,
|
|
30
40
|
type GetWalletsOutput,
|
|
@@ -41,6 +51,8 @@ import {
|
|
|
41
51
|
type idOSCredential,
|
|
42
52
|
type idOSCredentialListItem,
|
|
43
53
|
type idOSDelegatedWriteGrant,
|
|
54
|
+
type DelegatedWriteGrantBaseParams,
|
|
55
|
+
toDelegatedWriteGrantBaseParams,
|
|
44
56
|
type idOSGrant,
|
|
45
57
|
type idOSUser,
|
|
46
58
|
type idOSUserAttribute,
|
|
@@ -48,18 +60,24 @@ import {
|
|
|
48
60
|
removeCredential,
|
|
49
61
|
removeWallet,
|
|
50
62
|
revokeAccessGrant,
|
|
51
|
-
type
|
|
52
|
-
|
|
63
|
+
type SharePreliminaryCredentialInput,
|
|
64
|
+
sharePreliminaryCredential,
|
|
53
65
|
type WalletType,
|
|
54
66
|
} from "@idos-network/kwil-infra/actions";
|
|
67
|
+
import { NodeKwil, type KwilSigner } from "@idos-network/kwil-js";
|
|
68
|
+
import {
|
|
69
|
+
BlobGateway,
|
|
70
|
+
createBlobContentReference,
|
|
71
|
+
requireAccessTokenForUkycContent,
|
|
72
|
+
resolveCredentialEncryptedContent,
|
|
73
|
+
} from "@idos-network/utils/blob-gateway";
|
|
55
74
|
import {
|
|
56
75
|
base64Decode,
|
|
57
76
|
base64Encode,
|
|
58
77
|
hexEncodeSha256Hash,
|
|
59
78
|
utf8Decode,
|
|
60
|
-
utf8Encode,
|
|
61
79
|
} from "@idos-network/utils/codecs";
|
|
62
|
-
import { LocalStorageStore, type Store } from "@idos-network/utils/store";
|
|
80
|
+
import { LocalStorageStore, MemoryStore, type Store } from "@idos-network/utils/store";
|
|
63
81
|
import invariant from "tiny-invariant";
|
|
64
82
|
|
|
65
83
|
import { IframeEnclave } from "./enclave/iframe-enclave";
|
|
@@ -70,6 +88,52 @@ type Properties<T> = {
|
|
|
70
88
|
[K in keyof T as Exclude<T[K], Function> extends never ? never : K]: T[K];
|
|
71
89
|
};
|
|
72
90
|
|
|
91
|
+
function isBrowserRuntime(): boolean {
|
|
92
|
+
return typeof window !== "undefined";
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function usesNodeKwil(kwilClient: KwilActionClient): boolean {
|
|
96
|
+
return kwilClient.client instanceof NodeKwil;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function idleBlobGateway(url: string): BlobGateway {
|
|
100
|
+
if (!isBrowserRuntime()) {
|
|
101
|
+
return new BlobGateway({ url });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return new BlobGateway({
|
|
105
|
+
url,
|
|
106
|
+
// Browser blob reads need the KGW HttpOnly session cookie. That only works if the
|
|
107
|
+
// blob gateway is covered by the cookie's Domain (typically same host as nodeUrl/KGW).
|
|
108
|
+
fetchFn: (input, init) => fetch(input, { ...init, credentials: "include" }),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function blobGatewayForSigner(idle: idOSClientIdle, kwilSigner: KwilSigner): BlobGateway {
|
|
113
|
+
if (usesNodeKwil(idle.kwilClient)) {
|
|
114
|
+
return createKgwAuthenticatedBlobGateway({
|
|
115
|
+
url: idle.blobGateway.url,
|
|
116
|
+
kwilClient: idle.kwilClient,
|
|
117
|
+
signer: kwilSigner,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return isMmTokenAuth(kwilSigner)
|
|
122
|
+
? idle.blobGateway.withAccessToken(kwilSigner.accessToken)
|
|
123
|
+
: idle.blobGateway;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function contentUriForWallet(
|
|
127
|
+
walletType: WalletType,
|
|
128
|
+
kwilSigner: KwilSigner,
|
|
129
|
+
credentialId: string,
|
|
130
|
+
): string | undefined {
|
|
131
|
+
if (walletType !== "MM") {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
return mmTokenCredentialContentUri(kwilSigner, credentialId);
|
|
135
|
+
}
|
|
136
|
+
|
|
73
137
|
export type idOSClient =
|
|
74
138
|
| idOSClientConfiguration
|
|
75
139
|
| idOSClientIdle
|
|
@@ -80,6 +144,7 @@ export class idOSClientConfiguration<Provider extends BaseProvider = IframeEncla
|
|
|
80
144
|
readonly state: "configuration";
|
|
81
145
|
readonly chainId?: string;
|
|
82
146
|
readonly nodeUrl: string;
|
|
147
|
+
readonly blobGatewayUrl?: string;
|
|
83
148
|
readonly enclaveOptions: Omit<Provider["options"], "mode">;
|
|
84
149
|
readonly store: Store;
|
|
85
150
|
readonly enclaveProvider: BaseProvider;
|
|
@@ -87,6 +152,12 @@ export class idOSClientConfiguration<Provider extends BaseProvider = IframeEncla
|
|
|
87
152
|
constructor(params: {
|
|
88
153
|
chainId?: string;
|
|
89
154
|
nodeUrl: string;
|
|
155
|
+
/**
|
|
156
|
+
* Optional blob gateway base URL. Defaults to `nodeUrl`.
|
|
157
|
+
* In the browser this must share KGW's session-cookie Domain
|
|
158
|
+
* (typically the same host), because reads use `credentials: "include"`.
|
|
159
|
+
*/
|
|
160
|
+
blobGatewayUrl?: string;
|
|
90
161
|
enclaveOptions: Omit<Provider["options"], "mode">;
|
|
91
162
|
enclaveProvider?: new (options: Omit<Provider["options"], "mode">) => Provider;
|
|
92
163
|
store?: Store;
|
|
@@ -94,8 +165,9 @@ export class idOSClientConfiguration<Provider extends BaseProvider = IframeEncla
|
|
|
94
165
|
this.state = "configuration";
|
|
95
166
|
this.chainId = params.chainId;
|
|
96
167
|
this.nodeUrl = params.nodeUrl;
|
|
168
|
+
this.blobGatewayUrl = params.blobGatewayUrl;
|
|
97
169
|
this.enclaveOptions = params.enclaveOptions;
|
|
98
|
-
this.store = params.store ?? new LocalStorageStore();
|
|
170
|
+
this.store = params.store ?? (isBrowserRuntime() ? new LocalStorageStore() : new MemoryStore());
|
|
99
171
|
|
|
100
172
|
// TODO: This is a mess because of types...
|
|
101
173
|
if (params.enclaveProvider) {
|
|
@@ -120,23 +192,37 @@ export class idOSClientIdle {
|
|
|
120
192
|
readonly store: Store;
|
|
121
193
|
readonly kwilClient: KwilActionClient;
|
|
122
194
|
readonly enclaveProvider: BaseProvider;
|
|
195
|
+
readonly blobGateway: BlobGateway;
|
|
123
196
|
|
|
124
|
-
constructor(
|
|
197
|
+
constructor(
|
|
198
|
+
store: Store,
|
|
199
|
+
kwilClient: KwilActionClient,
|
|
200
|
+
enclaveProvider: BaseProvider,
|
|
201
|
+
blobGateway: BlobGateway,
|
|
202
|
+
) {
|
|
125
203
|
this.state = "idle";
|
|
126
204
|
this.store = store;
|
|
127
205
|
this.kwilClient = kwilClient;
|
|
128
206
|
this.enclaveProvider = enclaveProvider;
|
|
207
|
+
this.blobGateway = blobGateway;
|
|
129
208
|
}
|
|
130
209
|
|
|
131
210
|
static async fromConfig(params: idOSClientConfiguration<BaseProvider>): Promise<idOSClientIdle> {
|
|
132
|
-
const
|
|
211
|
+
const createKwilClient = isBrowserRuntime() ? createWebKwilClient : createNodeKwilClient;
|
|
212
|
+
const kwilClient = await createKwilClient({
|
|
133
213
|
nodeUrl: params.nodeUrl,
|
|
134
214
|
chainId: params.chainId,
|
|
135
215
|
});
|
|
136
216
|
|
|
137
217
|
await params.enclaveProvider.load();
|
|
138
218
|
|
|
139
|
-
|
|
219
|
+
const blobGatewayUrl = params.blobGatewayUrl ?? params.nodeUrl;
|
|
220
|
+
return new idOSClientIdle(
|
|
221
|
+
params.store,
|
|
222
|
+
kwilClient,
|
|
223
|
+
params.enclaveProvider,
|
|
224
|
+
idleBlobGateway(blobGatewayUrl),
|
|
225
|
+
);
|
|
140
226
|
}
|
|
141
227
|
|
|
142
228
|
async addressHasProfile(address: string): Promise<boolean> {
|
|
@@ -170,6 +256,7 @@ export class idOSClientIdle {
|
|
|
170
256
|
walletIdentifier,
|
|
171
257
|
walletPublicKey,
|
|
172
258
|
walletType,
|
|
259
|
+
blobGatewayForSigner(this, kwilSigner),
|
|
173
260
|
);
|
|
174
261
|
}
|
|
175
262
|
|
|
@@ -188,6 +275,7 @@ export class idOSClientWithUserSigner implements Omit<Properties<idOSClientIdle>
|
|
|
188
275
|
readonly walletIdentifier: string;
|
|
189
276
|
readonly walletPublicKey: string | undefined;
|
|
190
277
|
readonly walletType: WalletType;
|
|
278
|
+
readonly blobGateway: BlobGateway;
|
|
191
279
|
|
|
192
280
|
constructor(
|
|
193
281
|
idOSClientIdle: idOSClientIdle,
|
|
@@ -196,6 +284,7 @@ export class idOSClientWithUserSigner implements Omit<Properties<idOSClientIdle>
|
|
|
196
284
|
walletIdentifier: string,
|
|
197
285
|
walletPublicKey: string | undefined,
|
|
198
286
|
walletType: WalletType,
|
|
287
|
+
blobGateway: BlobGateway,
|
|
199
288
|
) {
|
|
200
289
|
this.state = "with-user-signer";
|
|
201
290
|
this.store = idOSClientIdle.store;
|
|
@@ -206,13 +295,20 @@ export class idOSClientWithUserSigner implements Omit<Properties<idOSClientIdle>
|
|
|
206
295
|
this.walletIdentifier = walletIdentifier;
|
|
207
296
|
this.walletPublicKey = walletPublicKey;
|
|
208
297
|
this.walletType = walletType;
|
|
298
|
+
this.blobGateway = blobGateway;
|
|
209
299
|
// @ts-expect-error - TODO: Fix this
|
|
210
300
|
this.enclaveProvider.setSigner(this.signer);
|
|
211
301
|
}
|
|
212
302
|
|
|
213
303
|
async logOut(): Promise<idOSClientIdle> {
|
|
214
304
|
this.kwilClient.setSigner(undefined);
|
|
215
|
-
|
|
305
|
+
await this.enclaveProvider.reset();
|
|
306
|
+
return new idOSClientIdle(
|
|
307
|
+
this.store,
|
|
308
|
+
this.kwilClient,
|
|
309
|
+
this.enclaveProvider,
|
|
310
|
+
idleBlobGateway(this.blobGateway.url),
|
|
311
|
+
);
|
|
216
312
|
}
|
|
217
313
|
|
|
218
314
|
async hasProfile(): Promise<boolean> {
|
|
@@ -268,6 +364,7 @@ export class idOSClientLoggedIn implements Omit<Properties<idOSClientWithUserSig
|
|
|
268
364
|
readonly walletPublicKey: string | undefined;
|
|
269
365
|
readonly walletType: WalletType;
|
|
270
366
|
readonly user: idOSUser;
|
|
367
|
+
readonly blobGateway: BlobGateway;
|
|
271
368
|
|
|
272
369
|
constructor(idOSClientWithUserSigner: idOSClientWithUserSigner, user: idOSUser) {
|
|
273
370
|
this.state = "logged-in";
|
|
@@ -280,19 +377,85 @@ export class idOSClientLoggedIn implements Omit<Properties<idOSClientWithUserSig
|
|
|
280
377
|
this.walletPublicKey = idOSClientWithUserSigner.walletPublicKey;
|
|
281
378
|
this.walletType = idOSClientWithUserSigner.walletType;
|
|
282
379
|
this.user = user;
|
|
380
|
+
this.blobGateway = idOSClientWithUserSigner.blobGateway;
|
|
283
381
|
}
|
|
284
382
|
|
|
285
383
|
async logOut(): Promise<idOSClientIdle> {
|
|
286
384
|
this.kwilClient.setSigner(undefined);
|
|
287
|
-
|
|
385
|
+
await this.enclaveProvider.reset();
|
|
386
|
+
return new idOSClientIdle(
|
|
387
|
+
this.store,
|
|
388
|
+
this.kwilClient,
|
|
389
|
+
this.enclaveProvider,
|
|
390
|
+
idleBlobGateway(this.blobGateway.url),
|
|
391
|
+
);
|
|
288
392
|
}
|
|
289
393
|
|
|
290
394
|
async requestDWGMessage(params: idOSDelegatedWriteGrant): Promise<string> {
|
|
291
395
|
return dwgMessage(this.kwilClient, params).then((res) => res.message);
|
|
292
396
|
}
|
|
293
397
|
|
|
398
|
+
async createCredential(
|
|
399
|
+
publicNotes: string,
|
|
400
|
+
plaintextContent: Uint8Array<ArrayBufferLike>,
|
|
401
|
+
// In case of credential creation on users behalf, this would be a issuer key
|
|
402
|
+
issuerSigningSecretKey?: Uint8Array,
|
|
403
|
+
): Promise<Omit<idOSCredential, "user_id" | "content" | "inserter_type" | "inserter_id">> {
|
|
404
|
+
const credentialId = crypto.randomUUID();
|
|
405
|
+
const preliminaryCredential: PreliminaryIDOSCredential = {
|
|
406
|
+
...(await buildPreliminaryIDOSCredential({
|
|
407
|
+
publicNotes,
|
|
408
|
+
plaintextContent,
|
|
409
|
+
recipientEncryptionPublicKey: base64Decode(this.user.recipient_encryption_public_key),
|
|
410
|
+
issuerSigningSecretKey,
|
|
411
|
+
contentUri: contentUriForWallet(this.walletType, this.kwilSigner, credentialId),
|
|
412
|
+
})),
|
|
413
|
+
id: credentialId,
|
|
414
|
+
};
|
|
415
|
+
requireAccessTokenForUkycContent(
|
|
416
|
+
preliminaryCredential.contentUri,
|
|
417
|
+
this.blobGateway.hasAccessToken,
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
const requestId = crypto.randomUUID();
|
|
421
|
+
|
|
422
|
+
const payload: CreatePreliminaryCredentialInput = {
|
|
423
|
+
request_id: requestId,
|
|
424
|
+
credential_id: preliminaryCredential.id,
|
|
425
|
+
issuer_auth_public_key: preliminaryCredential.issuerAuthPublicKey,
|
|
426
|
+
encryptor_public_key: preliminaryCredential.encryptorPublicKey,
|
|
427
|
+
content_uri: preliminaryCredential.contentUri,
|
|
428
|
+
content_size: preliminaryCredential.contentSize,
|
|
429
|
+
content_hash: hexEncodeSha256Hash(plaintextContent),
|
|
430
|
+
public_notes: preliminaryCredential.publicNotes,
|
|
431
|
+
public_notes_signature: preliminaryCredential.publicNotesSignature,
|
|
432
|
+
broader_signature: preliminaryCredential.broaderSignature,
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
await createPreliminaryCredential(this.kwilClient, payload);
|
|
436
|
+
|
|
437
|
+
await this.blobGateway.uploadCredentialBlobs({
|
|
438
|
+
requestId,
|
|
439
|
+
original: preliminaryCredential.encryptedContent,
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
return mapPreliminaryToIDOSCredential(preliminaryCredential);
|
|
443
|
+
}
|
|
444
|
+
|
|
294
445
|
async removeCredential(id: string): Promise<{ id: string }> {
|
|
446
|
+
const credential = await this.getCredentialById(id);
|
|
447
|
+
invariant(credential, `"idOSCredential" with id ${id} not found`);
|
|
448
|
+
|
|
449
|
+
const contentUri = credential.content_uri ?? null;
|
|
450
|
+
requireAccessTokenForUkycContent(contentUri, this.blobGateway.hasAccessToken);
|
|
451
|
+
|
|
295
452
|
await removeCredential(this.kwilClient, { id });
|
|
453
|
+
|
|
454
|
+
if (!contentUri) {
|
|
455
|
+
return { id };
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
await this.blobGateway.deleteCredentialBlob({ credentialId: id });
|
|
296
459
|
return { id };
|
|
297
460
|
}
|
|
298
461
|
|
|
@@ -300,8 +463,10 @@ export class idOSClientLoggedIn implements Omit<Properties<idOSClientWithUserSig
|
|
|
300
463
|
return getCredentialOwned(this.kwilClient, { id }).then((res) => res[0]);
|
|
301
464
|
}
|
|
302
465
|
|
|
303
|
-
async shareCredential(
|
|
304
|
-
|
|
466
|
+
async shareCredential(
|
|
467
|
+
credential: SharePreliminaryCredentialInput,
|
|
468
|
+
): Promise<SharePreliminaryCredentialInput> {
|
|
469
|
+
await sharePreliminaryCredential(this.kwilClient, credential);
|
|
305
470
|
return credential;
|
|
306
471
|
}
|
|
307
472
|
|
|
@@ -326,13 +491,7 @@ export class idOSClientLoggedIn implements Omit<Properties<idOSClientWithUserSig
|
|
|
326
491
|
const credential = await this.getCredentialById(id);
|
|
327
492
|
|
|
328
493
|
invariant(credential, `"idOSCredential" with id ${id} not found`);
|
|
329
|
-
|
|
330
|
-
await this.enclaveProvider.ensureUserEncryptionProfile();
|
|
331
|
-
|
|
332
|
-
const plaintext = await this.enclaveProvider.decrypt(
|
|
333
|
-
base64Decode(credential.content),
|
|
334
|
-
base64Decode(credential.encryptor_public_key),
|
|
335
|
-
);
|
|
494
|
+
const plaintext = await this.#decryptCredentialContent(credential);
|
|
336
495
|
|
|
337
496
|
return hexEncodeSha256Hash(plaintext);
|
|
338
497
|
}
|
|
@@ -341,19 +500,24 @@ export class idOSClientLoggedIn implements Omit<Properties<idOSClientWithUserSig
|
|
|
341
500
|
const credential = await this.getCredentialById(id);
|
|
342
501
|
|
|
343
502
|
invariant(credential, `"idOSCredential" with id ${id} not found`);
|
|
344
|
-
|
|
345
|
-
await this.enclaveProvider.ensureUserEncryptionProfile();
|
|
346
|
-
|
|
347
|
-
const plaintext = await this.enclaveProvider.decrypt(
|
|
348
|
-
base64Decode(credential.content),
|
|
349
|
-
base64Decode(credential.encryptor_public_key),
|
|
350
|
-
);
|
|
503
|
+
const plaintext = await this.#decryptCredentialContent(credential);
|
|
351
504
|
|
|
352
505
|
return utf8Decode(plaintext);
|
|
353
506
|
}
|
|
354
507
|
|
|
355
|
-
async
|
|
356
|
-
|
|
508
|
+
async getCredentialWithEncryptedContent(id: string): Promise<idOSCredential> {
|
|
509
|
+
const credential = await this.getCredentialById(id);
|
|
510
|
+
invariant(credential, `"idOSCredential" with id ${id} not found`);
|
|
511
|
+
return this.#credentialWithInlineContent(credential);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
async getCredentialSharedContent(id: string): Promise<string> {
|
|
515
|
+
const credential = await this.getCredentialShared(id);
|
|
516
|
+
|
|
517
|
+
invariant(credential, `Shared credential with id ${id} not found`);
|
|
518
|
+
const plaintext = await this.#decryptCredentialContent(credential);
|
|
519
|
+
|
|
520
|
+
return utf8Decode(plaintext);
|
|
357
521
|
}
|
|
358
522
|
|
|
359
523
|
async getGrants(
|
|
@@ -385,6 +549,11 @@ export class idOSClientLoggedIn implements Omit<Properties<idOSClientWithUserSig
|
|
|
385
549
|
return { id };
|
|
386
550
|
}
|
|
387
551
|
|
|
552
|
+
async addWalletMessage(params: AddWalletMessageInput): Promise<string> {
|
|
553
|
+
const result = await requestAddWalletMessage(this.kwilClient, params);
|
|
554
|
+
return result.message;
|
|
555
|
+
}
|
|
556
|
+
|
|
388
557
|
async addWallet(params: AddWalletInput): Promise<AddWalletInput> {
|
|
389
558
|
await addWallet(this.kwilClient, params);
|
|
390
559
|
|
|
@@ -485,8 +654,8 @@ export class idOSClientLoggedIn implements Omit<Properties<idOSClientWithUserSig
|
|
|
485
654
|
omit: Parameters<typeof recordFilter>[2];
|
|
486
655
|
};
|
|
487
656
|
credentialLevelOrHigherFilter?: {
|
|
488
|
-
userLevel:
|
|
489
|
-
requiredAddons:
|
|
657
|
+
userLevel: BaseLevel;
|
|
658
|
+
requiredAddons: Addon[];
|
|
490
659
|
};
|
|
491
660
|
}): Promise<idOSCredentialListItem[]> {
|
|
492
661
|
const credentials = await this.getAllCredentials();
|
|
@@ -563,7 +732,9 @@ export class idOSClientLoggedIn implements Omit<Properties<idOSClientWithUserSig
|
|
|
563
732
|
);
|
|
564
733
|
|
|
565
734
|
const matches = await this.enclaveProvider.filterCredentials(
|
|
566
|
-
|
|
735
|
+
await Promise.all(
|
|
736
|
+
fullCredentials.map((credential) => this.#credentialWithInlineContent(credential)),
|
|
737
|
+
),
|
|
567
738
|
privateFieldFilters,
|
|
568
739
|
);
|
|
569
740
|
|
|
@@ -586,49 +757,90 @@ export class idOSClientLoggedIn implements Omit<Properties<idOSClientWithUserSig
|
|
|
586
757
|
consumerAuthPublicKey: string;
|
|
587
758
|
lockedUntil?: number;
|
|
588
759
|
},
|
|
589
|
-
): Promise<
|
|
760
|
+
): Promise<SharePreliminaryCredentialInput> {
|
|
590
761
|
const credential = await this.getCredentialById(credentialId);
|
|
591
|
-
const contentHash = await this.getCredentialContentSha256Hash(credentialId);
|
|
592
762
|
|
|
593
763
|
invariant(credential, `"idOSCredential" with id ${credentialId} not found`);
|
|
594
764
|
|
|
595
|
-
const plaintextContent =
|
|
596
|
-
|
|
597
|
-
base64Decode(credential.content),
|
|
598
|
-
base64Decode(credential.encryptor_public_key),
|
|
599
|
-
),
|
|
600
|
-
);
|
|
765
|
+
const plaintextContent = await this.#decryptCredentialContent(credential);
|
|
766
|
+
const contentHash = hexEncodeSha256Hash(plaintextContent);
|
|
601
767
|
|
|
602
768
|
await this.enclaveProvider.ensureUserEncryptionProfile();
|
|
603
769
|
|
|
604
770
|
const { content, encryptorPublicKey } = await this.enclaveProvider.encrypt(
|
|
605
|
-
|
|
771
|
+
plaintextContent,
|
|
606
772
|
base64Decode(consumerEncryptionPublicKey),
|
|
607
773
|
);
|
|
608
774
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
)
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
775
|
+
// User issues the shared copy: sign content_uri with an ephemeral user-side key.
|
|
776
|
+
// Copy issuer_auth_public_key need not match the original credential's issuer.
|
|
777
|
+
const copyId = crypto.randomUUID();
|
|
778
|
+
const ukycContentUri = contentUriForWallet(this.walletType, this.kwilSigner, copyId);
|
|
779
|
+
requireAccessTokenForUkycContent(ukycContentUri, this.blobGateway.hasAccessToken);
|
|
780
|
+
const copyReference = ukycContentUri
|
|
781
|
+
? { uri: ukycContentUri, size: content.byteLength }
|
|
782
|
+
: await createBlobContentReference(content);
|
|
783
|
+
const signedReference = buildEphemeralSignedCredentialContentReference("", copyReference.uri);
|
|
784
|
+
|
|
785
|
+
// Integrator-supplied. ed25519 @caller is lowercase hex; after exact grantee
|
|
786
|
+
// matching, uppercase hex would store a grant the consumer cannot read. Other
|
|
787
|
+
// identifier encodings (NEAR, XRPL, Stellar, MM) are case-sensitive, so leave them.
|
|
788
|
+
const granteeWalletIdentifier = /^[0-9a-fA-F]{64}$/.test(consumerAuthPublicKey)
|
|
789
|
+
? consumerAuthPublicKey.toLowerCase()
|
|
790
|
+
: consumerAuthPublicKey;
|
|
791
|
+
|
|
792
|
+
const preliminaryCredential: SharePreliminaryCredentialInput = {
|
|
793
|
+
...signedReference,
|
|
794
|
+
request_id: crypto.randomUUID(),
|
|
795
|
+
copy_id: copyId,
|
|
796
|
+
original_id: credential.id,
|
|
797
|
+
content_uri: copyReference.uri,
|
|
798
|
+
content_size: copyReference.size,
|
|
799
|
+
encryptor_public_key: base64Encode(encryptorPublicKey),
|
|
800
|
+
grantee_wallet_identifier: granteeWalletIdentifier,
|
|
620
801
|
locked_until: lockedUntil,
|
|
621
802
|
content_hash: contentHash,
|
|
622
803
|
};
|
|
623
804
|
|
|
624
|
-
await this.shareCredential(
|
|
805
|
+
await this.shareCredential(preliminaryCredential);
|
|
806
|
+
await this.blobGateway.uploadCredentialBlobs({
|
|
807
|
+
requestId: preliminaryCredential.request_id,
|
|
808
|
+
copy: content,
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
return preliminaryCredential;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
async #credentialWithInlineContent(credential: idOSCredential): Promise<idOSCredential> {
|
|
815
|
+
if (credential.content) {
|
|
816
|
+
return credential;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
return {
|
|
820
|
+
...credential,
|
|
821
|
+
content: base64Encode(await this.#getCredentialEncryptedContent(credential)),
|
|
822
|
+
content_uri: null,
|
|
823
|
+
content_size: null,
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
async #decryptCredentialContent(credential: idOSCredential): Promise<Uint8Array> {
|
|
828
|
+
await this.enclaveProvider.ensureUserEncryptionProfile();
|
|
829
|
+
|
|
830
|
+
return this.enclaveProvider.decrypt(
|
|
831
|
+
await this.#getCredentialEncryptedContent(credential),
|
|
832
|
+
base64Decode(credential.encryptor_public_key),
|
|
833
|
+
);
|
|
834
|
+
}
|
|
625
835
|
|
|
626
|
-
|
|
836
|
+
async #getCredentialEncryptedContent(credential: idOSCredential): Promise<Uint8Array> {
|
|
837
|
+
return resolveCredentialEncryptedContent(credential, this.blobGateway);
|
|
627
838
|
}
|
|
628
839
|
}
|
|
629
840
|
|
|
630
841
|
export type {
|
|
631
842
|
idOSDelegatedWriteGrant,
|
|
843
|
+
DelegatedWriteGrantBaseParams,
|
|
632
844
|
idOSCredential,
|
|
633
845
|
idOSCredentialListItem,
|
|
634
846
|
idOSGrant,
|
|
@@ -637,14 +849,21 @@ export type {
|
|
|
637
849
|
idOSWallet,
|
|
638
850
|
};
|
|
639
851
|
|
|
640
|
-
export { signNearMessage };
|
|
852
|
+
export { signNearMessage, toDelegatedWriteGrantBaseParams };
|
|
641
853
|
|
|
642
854
|
export function createIDOSClient(params: {
|
|
643
855
|
nodeUrl: string;
|
|
856
|
+
/**
|
|
857
|
+
* Optional blob gateway base URL. Defaults to `nodeUrl`.
|
|
858
|
+
* In the browser this must share KGW's session-cookie Domain
|
|
859
|
+
* (typically the same host), because reads use `credentials: "include"`.
|
|
860
|
+
*/
|
|
861
|
+
blobGatewayUrl?: string;
|
|
644
862
|
enclaveOptions: Omit<IframeEnclave["options"], "mode">;
|
|
645
863
|
}): idOSClientConfiguration<IframeEnclave> {
|
|
646
864
|
return new idOSClientConfiguration({
|
|
647
865
|
nodeUrl: params.nodeUrl,
|
|
866
|
+
blobGatewayUrl: params.blobGatewayUrl,
|
|
648
867
|
enclaveOptions: params.enclaveOptions,
|
|
649
868
|
});
|
|
650
869
|
}
|