@idos-network/client 1.0.0-rc.1.0 → 1.0.0-rc.1.2
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/index.d.ts +206 -168
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32815 -4606
- package/dist/index.js.map +1 -1
- package/package.json +17 -14
- package/src/enclave/helpers.ts +25 -0
- package/src/enclave/iframe-enclave.ts +117 -128
- package/src/index.ts +152 -91
- package/src/enclave/index.ts +0 -3
- package/src/enclave/metamask-snap-enclave.ts +0 -91
- package/src/enclave/types.ts +0 -44
package/package.json
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idos-network/client",
|
|
3
3
|
"description": "idOS Client JavaScript SDK for browser environments",
|
|
4
|
-
"version": "1.0.0-rc.1.
|
|
4
|
+
"version": "1.0.0-rc.1.2",
|
|
5
5
|
"clean-package": {
|
|
6
6
|
"remove": [
|
|
7
|
-
"devDependencies.@idos-network/core"
|
|
7
|
+
"devDependencies.@idos-network/core",
|
|
8
|
+
"devDependencies.@idos-network/credentials"
|
|
8
9
|
]
|
|
9
10
|
},
|
|
10
11
|
"dependencies": {
|
|
11
|
-
"@
|
|
12
|
-
"
|
|
13
|
-
"
|
|
12
|
+
"@gemwallet/api": "^3.8.0",
|
|
13
|
+
"@idos-network/kwil-js": "0.1.1",
|
|
14
|
+
"es-toolkit": "^1.39.0",
|
|
15
|
+
"@idos-network/utils": "1.0.0-rc.1.0"
|
|
14
16
|
},
|
|
15
17
|
"devDependencies": {
|
|
16
18
|
"clean-package": "^2.2.0",
|
|
17
19
|
"release-it": "^19.0.2",
|
|
18
20
|
"release-it-pnpm": "^4.6.5",
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
+
"tiny-invariant": "^1.3.3",
|
|
22
|
+
"tsdown": "^0.13.0"
|
|
21
23
|
},
|
|
22
24
|
"engines": {
|
|
23
25
|
"node": ">=20.0.0"
|
|
@@ -35,19 +37,20 @@
|
|
|
35
37
|
"homepage": "https://idos.network",
|
|
36
38
|
"license": "MIT",
|
|
37
39
|
"module": "dist/index.js",
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"typescript": "^5.8.3"
|
|
42
|
+
},
|
|
38
43
|
"publishConfig": {
|
|
39
44
|
"access": "public"
|
|
40
45
|
},
|
|
41
46
|
"repository": "https://github.com/idos-network/idos-sdk-js",
|
|
47
|
+
"sideEffects": false,
|
|
48
|
+
"type": "module",
|
|
49
|
+
"types": "dist/index.d.ts",
|
|
42
50
|
"scripts": {
|
|
43
51
|
"build": "tsdown",
|
|
44
52
|
"dev": "tsdown --watch",
|
|
45
|
-
"postpack": "clean-package restore",
|
|
46
|
-
"prepack": "clean-package",
|
|
47
53
|
"publint": "pnpx publint --strict",
|
|
48
54
|
"release-it": "release-it"
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
"type": "module",
|
|
52
|
-
"types": "dist/index.d.ts"
|
|
53
|
-
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** biome-ignore-all lint/suspicious/noExplicitAny: any is fine here. */
|
|
2
|
+
import type { BaseProvider } from "@idos-network/utils/enclave";
|
|
3
|
+
|
|
4
|
+
type BaseProviderInstance = typeof BaseProvider.prototype & {
|
|
5
|
+
// Custom method from enclave to ensure <-> iframe communication
|
|
6
|
+
signTypedDataResponse: (signature: string) => Promise<void>;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
// Gets the method argument type
|
|
10
|
+
export type MethodArg<T> = T extends (arg: infer A) => any ? A : never;
|
|
11
|
+
|
|
12
|
+
// Gets the method return type
|
|
13
|
+
export type MethodReturn<T> = T extends (...args: any[]) => infer R ? R : never;
|
|
14
|
+
|
|
15
|
+
export type BaseProviderMethodArgs = {
|
|
16
|
+
[K in keyof BaseProviderInstance as BaseProviderInstance[K] extends (...args: any[]) => any
|
|
17
|
+
? K
|
|
18
|
+
: never]: Parameters<BaseProviderInstance[K]>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type BaseProviderMethodReturn = {
|
|
22
|
+
[K in keyof BaseProviderInstance as BaseProviderInstance[K] extends (...args: any[]) => any
|
|
23
|
+
? K
|
|
24
|
+
: never]: ReturnType<BaseProviderInstance[K]>;
|
|
25
|
+
};
|
|
@@ -1,103 +1,129 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { idOSCredential } from "@idos-network/credentials";
|
|
2
|
+
import {
|
|
3
|
+
BaseProvider,
|
|
4
|
+
type EnclaveOptions,
|
|
5
|
+
type PublicEncryptionProfile,
|
|
6
|
+
} from "@idos-network/utils/enclave";
|
|
7
|
+
import type { BaseProviderMethodArgs, BaseProviderMethodReturn } from "./helpers";
|
|
8
|
+
|
|
9
|
+
export interface IframeEnclaveOptions extends EnclaveOptions {
|
|
10
|
+
container: string;
|
|
11
|
+
url?: string;
|
|
12
|
+
throwOnUserCancelUnlock?: boolean;
|
|
13
|
+
}
|
|
2
14
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
StoredData,
|
|
8
|
-
} from "./types";
|
|
15
|
+
export class IframeEnclave extends BaseProvider<IframeEnclaveOptions> {
|
|
16
|
+
private container: string;
|
|
17
|
+
private iframe: HTMLIFrameElement;
|
|
18
|
+
private hostUrl: URL;
|
|
9
19
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
container: string;
|
|
13
|
-
iframe: HTMLIFrameElement;
|
|
14
|
-
hostUrl: URL;
|
|
20
|
+
constructor(options: IframeEnclaveOptions) {
|
|
21
|
+
super(options);
|
|
15
22
|
|
|
16
|
-
|
|
17
|
-
|
|
23
|
+
if (!this.options.container) {
|
|
24
|
+
throw new Error("container is required");
|
|
25
|
+
}
|
|
18
26
|
|
|
19
|
-
this.container = container;
|
|
20
|
-
this.
|
|
21
|
-
this.hostUrl = new URL(other.url ?? "https://enclave.idos.network");
|
|
27
|
+
this.container = this.options.container;
|
|
28
|
+
this.hostUrl = new URL(this.options.url ?? "https://enclave.idos.network");
|
|
22
29
|
this.iframe = document.createElement("iframe");
|
|
23
30
|
this.iframe.id = "idos-enclave-iframe";
|
|
24
31
|
}
|
|
25
32
|
|
|
33
|
+
/** @see parent method */
|
|
26
34
|
async load(): Promise<void> {
|
|
27
|
-
|
|
28
|
-
await this.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
async ready(userId: string, expectedUserEncryptionPublicKey?: string): Promise<Uint8Array> {
|
|
37
|
-
let { encryptionPublicKey: userEncryptionPublicKey } = (await this.requestToEnclave({
|
|
38
|
-
storage: {
|
|
39
|
-
userId,
|
|
40
|
-
expectedUserEncryptionPublicKey,
|
|
41
|
-
},
|
|
42
|
-
})) as StoredData;
|
|
43
|
-
|
|
44
|
-
while (!userEncryptionPublicKey) {
|
|
45
|
-
this.showEnclave();
|
|
46
|
-
try {
|
|
47
|
-
userEncryptionPublicKey = (await this.requestToEnclave({
|
|
48
|
-
keys: {},
|
|
49
|
-
})) as Uint8Array;
|
|
50
|
-
} catch (e) {
|
|
51
|
-
if (this.options.throwOnUserCancelUnlock) throw e;
|
|
52
|
-
} finally {
|
|
53
|
-
this.hideEnclave();
|
|
54
|
-
}
|
|
55
|
-
}
|
|
35
|
+
// First we have to create and load the iframe
|
|
36
|
+
await this.createAndLoadIframe();
|
|
37
|
+
|
|
38
|
+
// Load the enclave from the store (trigger load() method in the iframe)
|
|
39
|
+
await this.requestToEnclave("load");
|
|
40
|
+
|
|
41
|
+
// Pass current options
|
|
42
|
+
await this.reconfigure();
|
|
56
43
|
|
|
57
|
-
|
|
44
|
+
// Bind the message listener to the iframe (in case iframe -> enclave comm)
|
|
45
|
+
await this.bindMessageListener();
|
|
58
46
|
}
|
|
59
47
|
|
|
48
|
+
/** @override parent method to call iframe */
|
|
60
49
|
async reset(): Promise<void> {
|
|
61
|
-
this.requestToEnclave(
|
|
50
|
+
this.requestToEnclave("reset");
|
|
62
51
|
}
|
|
63
52
|
|
|
53
|
+
/** @override parent method to call iframe */
|
|
54
|
+
async reconfigure(options: Omit<IframeEnclaveOptions, "container" | "url"> = {}): Promise<void> {
|
|
55
|
+
super.reconfigure(options);
|
|
56
|
+
await this.requestToEnclave("reconfigure", this.options);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** @override parent method to call iframe */
|
|
64
60
|
async confirm(message: string): Promise<boolean> {
|
|
65
61
|
this.showEnclave();
|
|
66
62
|
|
|
67
|
-
|
|
63
|
+
try {
|
|
64
|
+
return await this.requestToEnclave("confirm", message);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error(error);
|
|
67
|
+
return false;
|
|
68
|
+
} finally {
|
|
68
69
|
this.hideEnclave();
|
|
69
|
-
|
|
70
|
-
});
|
|
70
|
+
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/** @override parent method to call iframe */
|
|
74
|
+
async filterCredentials(
|
|
75
|
+
credentials: idOSCredential[],
|
|
76
|
+
privateFieldFilters: { pick: Record<string, unknown[]>; omit: Record<string, unknown[]> },
|
|
77
|
+
): Promise<idOSCredential[]> {
|
|
78
|
+
return await this.requestToEnclave("filterCredentials", credentials, privateFieldFilters);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** @override parent method to call iframe */
|
|
73
82
|
async encrypt(
|
|
74
83
|
message: Uint8Array,
|
|
75
84
|
receiverPublicKey: Uint8Array,
|
|
76
85
|
): Promise<{ content: Uint8Array; encryptorPublicKey: Uint8Array }> {
|
|
77
|
-
return this.requestToEnclave(
|
|
78
|
-
encrypt: { message, receiverPublicKey },
|
|
79
|
-
}) as Promise<{ content: Uint8Array; encryptorPublicKey: Uint8Array }>;
|
|
86
|
+
return this.requestToEnclave("encrypt", message, receiverPublicKey);
|
|
80
87
|
}
|
|
81
88
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
/** @override parent method to call iframe */
|
|
90
|
+
async decrypt(
|
|
91
|
+
message: Uint8Array,
|
|
92
|
+
senderPublicKey: Uint8Array,
|
|
93
|
+
): Promise<Uint8Array<ArrayBufferLike>> {
|
|
94
|
+
return this.requestToEnclave("decrypt", message, senderPublicKey);
|
|
86
95
|
}
|
|
87
96
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
})
|
|
97
|
+
/** @override parent method to call iframe */
|
|
98
|
+
async backupUserEncryptionProfile(): Promise<void> {
|
|
99
|
+
this.showEnclave();
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
await this.requestToEnclave("backupUserEncryptionProfile");
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error(error);
|
|
105
|
+
} finally {
|
|
106
|
+
this.hideEnclave();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** @override parent method to call iframe */
|
|
111
|
+
async ensureUserEncryptionProfile(): Promise<PublicEncryptionProfile> {
|
|
112
|
+
this.showEnclave();
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
return await this.requestToEnclave("ensureUserEncryptionProfile");
|
|
116
|
+
} finally {
|
|
117
|
+
this.hideEnclave();
|
|
118
|
+
}
|
|
95
119
|
}
|
|
96
120
|
|
|
97
|
-
async
|
|
98
|
-
const container =
|
|
99
|
-
|
|
100
|
-
|
|
121
|
+
private async createAndLoadIframe(): Promise<void> {
|
|
122
|
+
const container = document.querySelector(this.container);
|
|
123
|
+
|
|
124
|
+
if (!container) {
|
|
125
|
+
throw new Error(`Can't find container with selector ${this.container}`);
|
|
126
|
+
}
|
|
101
127
|
|
|
102
128
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy#directives
|
|
103
129
|
const permissionsPolicies = ["publickey-credentials-get", "storage-access"];
|
|
@@ -150,18 +176,20 @@ export class IframeEnclave implements EnclaveProvider {
|
|
|
150
176
|
);
|
|
151
177
|
}
|
|
152
178
|
|
|
153
|
-
showEnclave(): void {
|
|
179
|
+
private showEnclave(): void {
|
|
154
180
|
// biome-ignore lint/style/noNonNullAssertion: Make the explosion visible.
|
|
155
181
|
this.iframe.parentElement!.classList.add("visible");
|
|
156
182
|
}
|
|
157
183
|
|
|
158
|
-
hideEnclave(): void {
|
|
184
|
+
private hideEnclave(): void {
|
|
159
185
|
// biome-ignore lint/style/noNonNullAssertion: Make the explosion visible.
|
|
160
186
|
this.iframe.parentElement!.classList.remove("visible");
|
|
161
187
|
}
|
|
162
188
|
|
|
163
|
-
|
|
164
|
-
|
|
189
|
+
private async requestToEnclave<TMethod extends keyof BaseProviderMethodArgs>(
|
|
190
|
+
method: TMethod,
|
|
191
|
+
...args: BaseProviderMethodArgs[TMethod]
|
|
192
|
+
): Promise<BaseProviderMethodReturn[TMethod]> {
|
|
165
193
|
return new Promise((resolve, reject) => {
|
|
166
194
|
const { port1, port2 } = new MessageChannel();
|
|
167
195
|
|
|
@@ -171,67 +199,28 @@ export class IframeEnclave implements EnclaveProvider {
|
|
|
171
199
|
};
|
|
172
200
|
|
|
173
201
|
// biome-ignore lint/style/noNonNullAssertion: Make the explosion visible.
|
|
174
|
-
this.iframe.contentWindow!.postMessage(
|
|
202
|
+
this.iframe.contentWindow!.postMessage(
|
|
203
|
+
{
|
|
204
|
+
method,
|
|
205
|
+
data: args,
|
|
206
|
+
},
|
|
207
|
+
this.hostUrl.origin,
|
|
208
|
+
[port2],
|
|
209
|
+
);
|
|
175
210
|
});
|
|
176
211
|
}
|
|
177
212
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
this.showEnclave();
|
|
181
|
-
|
|
182
|
-
window.addEventListener(
|
|
183
|
-
"message",
|
|
184
|
-
async (event) => {
|
|
185
|
-
if (event.data.type !== "idOS:store" || event.origin !== this.hostUrl.origin) return;
|
|
186
|
-
|
|
187
|
-
let status = "";
|
|
188
|
-
|
|
189
|
-
try {
|
|
190
|
-
status = "success";
|
|
191
|
-
this.hideEnclave();
|
|
192
|
-
} catch (_) {
|
|
193
|
-
status = "failure";
|
|
194
|
-
this.hideEnclave();
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
event.ports[0].postMessage({
|
|
198
|
-
result: {
|
|
199
|
-
type: "idOS:store",
|
|
200
|
-
status,
|
|
201
|
-
},
|
|
202
|
-
});
|
|
203
|
-
event.ports[0].close();
|
|
204
|
-
abortController.abort();
|
|
205
|
-
},
|
|
206
|
-
{ signal: abortController.signal },
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
try {
|
|
210
|
-
await this.requestToEnclave({
|
|
211
|
-
backupPasswordOrSecret: {},
|
|
212
|
-
});
|
|
213
|
-
} catch (error) {
|
|
214
|
-
console.error(error);
|
|
215
|
-
} finally {
|
|
216
|
-
this.hideEnclave();
|
|
217
|
-
}
|
|
213
|
+
private bindMessageListener(): void {
|
|
214
|
+
window.addEventListener("message", this.onMessage.bind(this));
|
|
218
215
|
}
|
|
219
216
|
|
|
220
|
-
async
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if (this.options.mode !== "new")
|
|
224
|
-
throw new Error("You can only call `discoverUserEncryptionPublicKey` when mode is `new`.");
|
|
217
|
+
private async onMessage(message: MessageEvent): Promise<void> {
|
|
218
|
+
if (message.data.type !== "idOS:signTypedData" || message.origin !== this.hostUrl.origin)
|
|
219
|
+
return;
|
|
225
220
|
|
|
226
|
-
const
|
|
221
|
+
const payload = message.data.payload;
|
|
222
|
+
const signature = await this.signTypedData(payload.domain, payload.types, payload.value);
|
|
227
223
|
|
|
228
|
-
|
|
229
|
-
userId,
|
|
230
|
-
userEncryptionPublicKey: base64Encode(userEncryptionPublicKey),
|
|
231
|
-
};
|
|
224
|
+
await this.requestToEnclave("signTypedDataResponse", signature);
|
|
232
225
|
}
|
|
233
226
|
}
|
|
234
|
-
|
|
235
|
-
function throwNewError(ErrorClass: ErrorConstructor, ...args: Parameters<ErrorConstructor>): never {
|
|
236
|
-
throw new ErrorClass(...args);
|
|
237
|
-
}
|