@prosopo/procaptcha 1.0.2 → 2.0.1
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/cjs/account/dist/extension/ExtensionWeb2.cjs +21 -28
- package/dist/cjs/account/dist/extension/ExtensionWeb3.cjs +5 -3
- package/dist/cjs/detector/src/index.cjs +4586 -0
- package/dist/cjs/modules/Manager.cjs +83 -168
- package/dist/cjs/modules/ProsopoCaptchaApi.cjs +34 -49
- package/dist/cjs/modules/collector.cjs +32 -8
- package/dist/cjs/modules/storage.cjs +8 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/modules/Manager.d.ts +1 -2
- package/dist/modules/Manager.d.ts.map +1 -1
- package/dist/modules/Manager.js +92 -168
- package/dist/modules/Manager.js.map +1 -1
- package/dist/modules/ProsopoCaptchaApi.d.ts +7 -10
- package/dist/modules/ProsopoCaptchaApi.d.ts.map +1 -1
- package/dist/modules/ProsopoCaptchaApi.js +28 -48
- package/dist/modules/ProsopoCaptchaApi.js.map +1 -1
- package/dist/modules/collector.d.ts +1 -1
- package/dist/modules/collector.d.ts.map +1 -1
- package/dist/modules/collector.js +8 -8
- package/dist/modules/collector.js.map +1 -1
- package/dist/modules/index.d.ts +3 -3
- package/dist/modules/index.d.ts.map +1 -1
- package/dist/modules/index.js +3 -3
- package/dist/modules/index.js.map +1 -1
- package/dist/modules/storage.d.ts +1 -1
- package/dist/modules/storage.d.ts.map +1 -1
- package/dist/modules/storage.js +5 -5
- package/dist/modules/storage.js.map +1 -1
- package/dist/tests/modules/storage.test.js +15 -11
- package/dist/tests/modules/storage.test.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js.map +1 -1
- package/package.json +77 -85
- package/vite.cjs.config.ts +3 -3
- package/vite.test.config.ts +15 -15
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const
|
|
4
|
-
const
|
|
3
|
+
const utilCrypto = require("@polkadot/util-crypto");
|
|
4
|
+
const random = require("@polkadot/util-crypto/random");
|
|
5
5
|
require("../account/dist/index.cjs");
|
|
6
|
-
const keyring = require("@polkadot/keyring");
|
|
7
|
-
const contract = require("@prosopo/contract");
|
|
8
|
-
const common = require("@prosopo/common");
|
|
9
6
|
const api = require("@prosopo/api");
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const util = require("@prosopo/util");
|
|
7
|
+
const common = require("@prosopo/common");
|
|
8
|
+
const loadBalancer = require("@prosopo/load-balancer");
|
|
13
9
|
const procaptchaCommon = require("@prosopo/procaptcha-common");
|
|
14
|
-
const
|
|
10
|
+
const types = require("@prosopo/types");
|
|
11
|
+
const util = require("@prosopo/util");
|
|
15
12
|
const utils = require("../utils/utils.cjs");
|
|
16
13
|
const ProsopoCaptchaApi = require("./ProsopoCaptchaApi.cjs");
|
|
17
14
|
const storage = require("./storage.cjs");
|
|
@@ -35,11 +32,30 @@ const getNetwork = (config) => {
|
|
|
35
32
|
const network = config.networks[config.defaultNetwork];
|
|
36
33
|
if (!network) {
|
|
37
34
|
throw new common.ProsopoEnvError("DEVELOPER.NETWORK_NOT_FOUND", {
|
|
38
|
-
context: {
|
|
35
|
+
context: {
|
|
36
|
+
error: `No network found for environment ${config.defaultEnvironment}`
|
|
37
|
+
}
|
|
39
38
|
});
|
|
40
39
|
}
|
|
41
40
|
return network;
|
|
42
41
|
};
|
|
42
|
+
const getRandomActiveProvider = (config) => {
|
|
43
|
+
const randomIntBetween = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
|
|
44
|
+
const PROVIDERS = loadBalancer.loadBalancer(config.defaultEnvironment);
|
|
45
|
+
const randomProvderObj = util.at(
|
|
46
|
+
PROVIDERS,
|
|
47
|
+
randomIntBetween(0, PROVIDERS.length - 1)
|
|
48
|
+
);
|
|
49
|
+
return {
|
|
50
|
+
providerAccount: randomProvderObj.address,
|
|
51
|
+
provider: {
|
|
52
|
+
url: randomProvderObj.url,
|
|
53
|
+
datasetId: randomProvderObj.datasetId,
|
|
54
|
+
datasetIdContent: randomProvderObj.datasetIdContent
|
|
55
|
+
},
|
|
56
|
+
blockNumber: 0
|
|
57
|
+
};
|
|
58
|
+
};
|
|
43
59
|
function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
44
60
|
const events = procaptchaCommon.getDefaultEvents(onStateUpdate, state, callbacks);
|
|
45
61
|
const dispatchErrorEvent = (err) => {
|
|
@@ -75,87 +91,33 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
75
91
|
if (state.isHuman) {
|
|
76
92
|
return;
|
|
77
93
|
}
|
|
94
|
+
await utilCrypto.cryptoWaitReady();
|
|
78
95
|
resetState();
|
|
79
96
|
updateState({ loading: true });
|
|
80
97
|
const config = getConfig();
|
|
81
98
|
updateState({ dappAccount: config.account.address });
|
|
82
99
|
await utils.sleep(100);
|
|
83
100
|
const account = await loadAccount();
|
|
84
|
-
const contract
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
})
|
|
99
|
-
);
|
|
100
|
-
setValidChallengeTimeout();
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
const procaptchaStorage = storage.getProcaptchaStorage();
|
|
104
|
-
let providerApi;
|
|
105
|
-
if (procaptchaStorage.providerUrl && procaptchaStorage.blockNumber) {
|
|
106
|
-
providerApi = await loadProviderApi(procaptchaStorage.providerUrl);
|
|
107
|
-
try {
|
|
108
|
-
const extension = getExtension(account);
|
|
109
|
-
if (!extension || !extension.signer || !extension.signer.signRaw) {
|
|
110
|
-
throw new common.ProsopoEnvError("ACCOUNT.NO_POLKADOT_EXTENSION");
|
|
111
|
-
}
|
|
112
|
-
const signRaw = extension.signer.signRaw;
|
|
113
|
-
const { signature } = await signRaw({
|
|
114
|
-
address: account.account.address,
|
|
115
|
-
data: procaptchaStorage.blockNumber.toString(),
|
|
116
|
-
type: "bytes"
|
|
117
|
-
});
|
|
118
|
-
const token = types.encodeProcaptchaOutput({
|
|
119
|
-
[types.ApiParams.user]: account.account.address,
|
|
120
|
-
[types.ApiParams.dapp]: getDappAccount(),
|
|
121
|
-
[types.ApiParams.blockNumber]: procaptchaStorage.blockNumber
|
|
122
|
-
});
|
|
123
|
-
const verifyDappUserResponse = await providerApi.verifyUser(
|
|
124
|
-
token,
|
|
125
|
-
signature,
|
|
126
|
-
configOptional.captchas.image.cachedTimeout
|
|
127
|
-
);
|
|
128
|
-
if (verifyDappUserResponse.verified) {
|
|
129
|
-
updateState({ isHuman: true, loading: false });
|
|
130
|
-
const output = {
|
|
131
|
-
[types.ApiParams.providerUrl]: procaptchaStorage.providerUrl,
|
|
132
|
-
[types.ApiParams.user]: account.account.address,
|
|
133
|
-
[types.ApiParams.dapp]: getDappAccount(),
|
|
134
|
-
[types.ApiParams.commitmentId]: util.hashToHex(verifyDappUserResponse.commitmentId),
|
|
135
|
-
[types.ApiParams.blockNumber]: verifyDappUserResponse.blockNumber
|
|
136
|
-
};
|
|
137
|
-
events.onHuman(types.encodeProcaptchaOutput(output));
|
|
138
|
-
setValidChallengeTimeout();
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
} catch (err) {
|
|
142
|
-
console.error("Error contacting provider from storage", procaptchaStorage.providerUrl);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
const getRandomProviderResponse = await contract.wrapQuery(
|
|
146
|
-
contract$1.query.getRandomActiveProvider,
|
|
147
|
-
contract$1.query
|
|
148
|
-
)(account.account.address, getDappAccount());
|
|
149
|
-
const blockNumber = parseInt(getRandomProviderResponse.blockNumber.toString());
|
|
150
|
-
const providerUrl = common.trimProviderUrl(getRandomProviderResponse.provider.url.toString());
|
|
151
|
-
providerApi = await loadProviderApi(providerUrl);
|
|
152
|
-
const captchaApi = await loadCaptchaApi(contract$1, getRandomProviderResponse, providerApi);
|
|
101
|
+
const contract = getNetwork(config).contract.address;
|
|
102
|
+
const getRandomProviderResponse = getRandomActiveProvider(getConfig());
|
|
103
|
+
const blockNumber = getRandomProviderResponse.blockNumber;
|
|
104
|
+
const providerUrl = getRandomProviderResponse.provider.url;
|
|
105
|
+
const providerApi = await loadProviderApi(providerUrl);
|
|
106
|
+
const captchaApi = new ProsopoCaptchaApi.ProsopoCaptchaApi(
|
|
107
|
+
account.account.address,
|
|
108
|
+
contract,
|
|
109
|
+
getRandomProviderResponse,
|
|
110
|
+
providerApi,
|
|
111
|
+
config.web2,
|
|
112
|
+
config.account.address || ""
|
|
113
|
+
);
|
|
114
|
+
updateState({ captchaApi });
|
|
153
115
|
const challenge = await captchaApi.getCaptchaChallenge();
|
|
154
116
|
if (challenge.captchas.length <= 0) {
|
|
155
117
|
throw new common.ProsopoDatasetError("DEVELOPER.PROVIDER_NO_CAPTCHA");
|
|
156
118
|
}
|
|
157
119
|
const timeMillis = challenge.captchas.map(
|
|
158
|
-
(captcha) => captcha.
|
|
120
|
+
(captcha) => captcha.timeLimitMs || config.captchas.image.challengeTimeout
|
|
159
121
|
).reduce((a, b) => a + b);
|
|
160
122
|
const timeout = setTimeout(() => {
|
|
161
123
|
events.onChallengeExpired();
|
|
@@ -186,8 +148,8 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
186
148
|
(captcha, index) => {
|
|
187
149
|
const solution = util.at(state.solutions, index);
|
|
188
150
|
return {
|
|
189
|
-
captchaId: captcha.
|
|
190
|
-
captchaContentId: captcha.
|
|
151
|
+
captchaId: captcha.captchaId,
|
|
152
|
+
captchaContentId: captcha.captchaContentId,
|
|
191
153
|
salt,
|
|
192
154
|
solution
|
|
193
155
|
};
|
|
@@ -197,18 +159,24 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
197
159
|
const blockNumber = getBlockNumber();
|
|
198
160
|
const signer = getExtension(account).signer;
|
|
199
161
|
const first = util.at(challenge.captchas, 0);
|
|
200
|
-
if (!first.
|
|
162
|
+
if (!first.datasetId) {
|
|
201
163
|
throw new common.ProsopoDatasetError("CAPTCHA.INVALID_CAPTCHA_ID", {
|
|
202
164
|
context: { error: "No datasetId set for challenge" }
|
|
203
165
|
});
|
|
204
166
|
}
|
|
205
|
-
const captchaApi =
|
|
167
|
+
const captchaApi = state.captchaApi;
|
|
168
|
+
if (!captchaApi) {
|
|
169
|
+
throw new common.ProsopoError("CAPTCHA.INVALID_TOKEN", {
|
|
170
|
+
context: { error: "No Captcha API found in state" }
|
|
171
|
+
});
|
|
172
|
+
}
|
|
206
173
|
const submission = await captchaApi.submitCaptchaSolution(
|
|
207
174
|
signer,
|
|
208
175
|
challenge.requestHash,
|
|
209
|
-
first.captcha.datasetId,
|
|
210
176
|
captchaSolution,
|
|
211
|
-
salt
|
|
177
|
+
salt,
|
|
178
|
+
challenge.timestamp,
|
|
179
|
+
challenge.signature.provider.timestamp
|
|
212
180
|
);
|
|
213
181
|
const isHuman = submission[0].verified;
|
|
214
182
|
if (!isHuman) {
|
|
@@ -220,15 +188,25 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
220
188
|
loading: false
|
|
221
189
|
});
|
|
222
190
|
if (state.isHuman) {
|
|
223
|
-
const providerUrl =
|
|
224
|
-
storage.setProcaptchaStorage({
|
|
191
|
+
const providerUrl = captchaApi.provider.provider.url;
|
|
192
|
+
storage.setProcaptchaStorage({
|
|
193
|
+
...storage.getProcaptchaStorage(),
|
|
194
|
+
providerUrl,
|
|
195
|
+
blockNumber
|
|
196
|
+
});
|
|
225
197
|
events.onHuman(
|
|
226
198
|
types.encodeProcaptchaOutput({
|
|
227
199
|
[types.ApiParams.providerUrl]: providerUrl,
|
|
228
200
|
[types.ApiParams.user]: account.account.address,
|
|
229
201
|
[types.ApiParams.dapp]: getDappAccount(),
|
|
230
202
|
[types.ApiParams.commitmentId]: util.hashToHex(submission[1]),
|
|
231
|
-
[types.ApiParams.blockNumber]: blockNumber
|
|
203
|
+
[types.ApiParams.blockNumber]: blockNumber,
|
|
204
|
+
[types.ApiParams.timestamp]: challenge.timestamp,
|
|
205
|
+
[types.ApiParams.signature]: {
|
|
206
|
+
[types.ApiParams.provider]: {
|
|
207
|
+
[types.ApiParams.timestamp]: challenge.signature.provider.timestamp
|
|
208
|
+
}
|
|
209
|
+
}
|
|
232
210
|
})
|
|
233
211
|
);
|
|
234
212
|
setValidChallengeTimeout();
|
|
@@ -248,7 +226,9 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
248
226
|
}
|
|
249
227
|
if (state.index >= state.challenge.captchas.length || state.index < 0) {
|
|
250
228
|
throw new common.ProsopoError("CAPTCHA.NO_CAPTCHA", {
|
|
251
|
-
context: {
|
|
229
|
+
context: {
|
|
230
|
+
error: "Cannot select, index is out of range for this Captcha"
|
|
231
|
+
}
|
|
252
232
|
});
|
|
253
233
|
}
|
|
254
234
|
const index = state.index;
|
|
@@ -269,24 +249,13 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
269
249
|
}
|
|
270
250
|
if (state.index + 1 >= state.challenge.captchas.length) {
|
|
271
251
|
throw new common.ProsopoError("CAPTCHA.NO_CAPTCHA", {
|
|
272
|
-
context: {
|
|
252
|
+
context: {
|
|
253
|
+
error: "Cannot select, index is out of range for this Captcha"
|
|
254
|
+
}
|
|
273
255
|
});
|
|
274
256
|
}
|
|
275
257
|
updateState({ index: state.index + 1 });
|
|
276
258
|
};
|
|
277
|
-
const loadCaptchaApi = async (contract2, provider, providerApi) => {
|
|
278
|
-
const config = getConfig();
|
|
279
|
-
const captchaApi = new ProsopoCaptchaApi.ProsopoCaptchaApi(
|
|
280
|
-
getAccount().account.address,
|
|
281
|
-
contract2,
|
|
282
|
-
provider,
|
|
283
|
-
providerApi,
|
|
284
|
-
config.web2,
|
|
285
|
-
getDappAccount()
|
|
286
|
-
);
|
|
287
|
-
updateState({ captchaApi });
|
|
288
|
-
return getCaptchaApi();
|
|
289
|
-
};
|
|
290
259
|
const loadProviderApi = async (providerUrl) => {
|
|
291
260
|
const config = getConfig();
|
|
292
261
|
const network = getNetwork(config);
|
|
@@ -311,14 +280,6 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
311
280
|
clearTimeout();
|
|
312
281
|
updateState(defaultState());
|
|
313
282
|
};
|
|
314
|
-
const getCaptchaApi = () => {
|
|
315
|
-
if (!state.captchaApi) {
|
|
316
|
-
throw new common.ProsopoEnvError("API.UNKNOWN", {
|
|
317
|
-
context: { error: "Captcha api not set", state }
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
return state.captchaApi;
|
|
321
|
-
};
|
|
322
283
|
const loadAccount = async () => {
|
|
323
284
|
const config = getConfig();
|
|
324
285
|
if (!config.web2 && !config.userAccountAddress) {
|
|
@@ -334,7 +295,9 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
334
295
|
};
|
|
335
296
|
const getAccount = () => {
|
|
336
297
|
if (!state.account) {
|
|
337
|
-
throw new common.ProsopoEnvError("GENERAL.ACCOUNT_NOT_FOUND", {
|
|
298
|
+
throw new common.ProsopoEnvError("GENERAL.ACCOUNT_NOT_FOUND", {
|
|
299
|
+
context: { error: "Account not loaded" }
|
|
300
|
+
});
|
|
338
301
|
}
|
|
339
302
|
const account = state.account;
|
|
340
303
|
return account;
|
|
@@ -347,72 +310,24 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
347
310
|
return dappAccount;
|
|
348
311
|
};
|
|
349
312
|
const getBlockNumber = () => {
|
|
350
|
-
|
|
351
|
-
throw new common.ProsopoContractError("CAPTCHA.INVALID_BLOCK_NO", { context: { error: "Block number not found" } });
|
|
352
|
-
}
|
|
353
|
-
const blockNumber = state.blockNumber;
|
|
313
|
+
const blockNumber = state.blockNumber || 0;
|
|
354
314
|
return blockNumber;
|
|
355
315
|
};
|
|
356
|
-
const getExtension = (
|
|
357
|
-
account =
|
|
316
|
+
const getExtension = (possiblyAccount) => {
|
|
317
|
+
const account = possiblyAccount || getAccount();
|
|
358
318
|
if (!account.extension) {
|
|
359
|
-
throw new common.ProsopoEnvError("ACCOUNT.NO_POLKADOT_EXTENSION", {
|
|
319
|
+
throw new common.ProsopoEnvError("ACCOUNT.NO_POLKADOT_EXTENSION", {
|
|
320
|
+
context: { error: "Extension not loaded" }
|
|
321
|
+
});
|
|
360
322
|
}
|
|
361
323
|
return account.extension;
|
|
362
324
|
};
|
|
363
|
-
const loadContract = async () => {
|
|
364
|
-
const config = getConfig();
|
|
365
|
-
const network = getNetwork(config);
|
|
366
|
-
const api2 = await Api.ApiPromise.create({
|
|
367
|
-
provider: new ws.WsProvider(network.endpoint),
|
|
368
|
-
initWasm: false,
|
|
369
|
-
noInitWarn: true
|
|
370
|
-
});
|
|
371
|
-
const type = "sr25519";
|
|
372
|
-
const keyring$1 = new keyring.Keyring({ type, ss58Format: api2.registry.chainSS58 });
|
|
373
|
-
return new contract.ProsopoCaptchaContract(
|
|
374
|
-
api2,
|
|
375
|
-
JSON.parse(contractInfo.ContractAbi),
|
|
376
|
-
network.contract.address,
|
|
377
|
-
"prosopo",
|
|
378
|
-
0,
|
|
379
|
-
keyring$1.addFromAddress(getAccount().account.address)
|
|
380
|
-
);
|
|
381
|
-
};
|
|
382
|
-
const exportData = async (events2) => {
|
|
383
|
-
const procaptchaStorage = storage.getProcaptchaStorage();
|
|
384
|
-
const providerUrlFromStorage = procaptchaStorage.providerUrl;
|
|
385
|
-
let providerApi;
|
|
386
|
-
if (providerUrlFromStorage) {
|
|
387
|
-
providerApi = await loadProviderApi(providerUrlFromStorage);
|
|
388
|
-
} else {
|
|
389
|
-
const contract$1 = await loadContract();
|
|
390
|
-
const getRandomProviderResponse = await contract.wrapQuery(
|
|
391
|
-
contract$1.query.getRandomActiveProvider,
|
|
392
|
-
contract$1.query
|
|
393
|
-
)(getAccount().account.address, getDappAccount());
|
|
394
|
-
const providerUrl2 = common.trimProviderUrl(getRandomProviderResponse.provider.url.toString());
|
|
395
|
-
providerApi = await loadProviderApi(providerUrl2);
|
|
396
|
-
}
|
|
397
|
-
const providerUrl = storage.getProcaptchaStorage().providerUrl || state.captchaApi?.provider.provider.url.toString();
|
|
398
|
-
if (!providerUrl) {
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
let account = "";
|
|
402
|
-
try {
|
|
403
|
-
account = getAccount().account.address;
|
|
404
|
-
} catch (e) {
|
|
405
|
-
console.error(e);
|
|
406
|
-
}
|
|
407
|
-
await providerApi.submitUserEvents(events2, account);
|
|
408
|
-
};
|
|
409
325
|
return {
|
|
410
326
|
start,
|
|
411
327
|
cancel,
|
|
412
328
|
submit,
|
|
413
329
|
select,
|
|
414
|
-
nextRound
|
|
415
|
-
exportData
|
|
330
|
+
nextRound
|
|
416
331
|
};
|
|
417
332
|
}
|
|
418
333
|
exports.Manager = Manager;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
|
|
3
|
-
const datasets = require("@prosopo/datasets");
|
|
4
|
-
const common = require("@prosopo/common");
|
|
5
|
-
const util = require("@prosopo/util");
|
|
6
3
|
const string = require("@polkadot/util/string");
|
|
4
|
+
const common = require("@prosopo/common");
|
|
5
|
+
const datasets = require("@prosopo/datasets");
|
|
7
6
|
class ProsopoCaptchaApi {
|
|
8
7
|
constructor(userAccount, contract, provider, providerApi, web2, dappAccount) {
|
|
9
8
|
this.userAccount = userAccount;
|
|
@@ -18,47 +17,43 @@ class ProsopoCaptchaApi {
|
|
|
18
17
|
}
|
|
19
18
|
async getCaptchaChallenge() {
|
|
20
19
|
try {
|
|
21
|
-
const captchaChallenge = await this.providerApi.getCaptchaChallenge(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
const captchaChallenge = await this.providerApi.getCaptchaChallenge(
|
|
21
|
+
this.userAccount,
|
|
22
|
+
this.provider
|
|
23
|
+
);
|
|
24
|
+
for (const captcha of captchaChallenge.captchas) {
|
|
25
|
+
for (const item of captcha.items) {
|
|
25
26
|
if (item.data) {
|
|
26
27
|
item.data = item.data.replace(/^http(s)*:\/\//, "//");
|
|
27
28
|
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
30
31
|
return captchaChallenge;
|
|
31
32
|
} catch (error) {
|
|
32
|
-
throw new common.ProsopoEnvError("CAPTCHA.INVALID_CAPTCHA_CHALLENGE", {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
verifyCaptchaChallengeContent(provider, captchaChallenge) {
|
|
36
|
-
const first = util.at(captchaChallenge.captchas, 0);
|
|
37
|
-
const proofLength = first.proof.length;
|
|
38
|
-
const last = util.at(first.proof, proofLength - 1);
|
|
39
|
-
if (provider.provider.datasetIdContent.toString() !== util.at(last, 0)) {
|
|
40
|
-
throw new common.ProsopoEnvError("CAPTCHA.INVALID_DATASET_CONTENT_ID");
|
|
41
|
-
}
|
|
42
|
-
for (const captchaWithProof of captchaChallenge.captchas) {
|
|
43
|
-
if (!verifyCaptchaData(captchaWithProof)) {
|
|
44
|
-
throw new common.ProsopoEnvError("CAPTCHA.INVALID_CAPTCHA_CHALLENGE");
|
|
45
|
-
}
|
|
46
|
-
if (!datasets.verifyProof(captchaWithProof.captcha.captchaContentId, captchaWithProof.proof)) {
|
|
47
|
-
throw new common.ProsopoEnvError("CAPTCHA.INVALID_CAPTCHA_CHALLENGE");
|
|
48
|
-
}
|
|
33
|
+
throw new common.ProsopoEnvError("CAPTCHA.INVALID_CAPTCHA_CHALLENGE", {
|
|
34
|
+
context: { error }
|
|
35
|
+
});
|
|
49
36
|
}
|
|
50
|
-
return;
|
|
51
37
|
}
|
|
52
|
-
async submitCaptchaSolution(signer, requestHash,
|
|
38
|
+
async submitCaptchaSolution(signer, requestHash, solutions, salt, timestamp, providerTimestampSignature) {
|
|
53
39
|
const tree = new datasets.CaptchaMerkleTree();
|
|
54
|
-
const captchasHashed = solutions.map(
|
|
40
|
+
const captchasHashed = solutions.map(
|
|
41
|
+
(captcha) => datasets.computeCaptchaSolutionHash(captcha)
|
|
42
|
+
);
|
|
55
43
|
tree.build(captchasHashed);
|
|
44
|
+
if (!tree.root) {
|
|
45
|
+
throw new common.ProsopoDatasetError("CAPTCHA.INVALID_CAPTCHA_CHALLENGE", {
|
|
46
|
+
context: { error: "Merkle tree root is undefined" }
|
|
47
|
+
});
|
|
48
|
+
}
|
|
56
49
|
const commitmentId = tree.root.hash;
|
|
57
50
|
const tx = void 0;
|
|
58
|
-
let
|
|
51
|
+
let userRequestHashSignature = void 0;
|
|
59
52
|
if (!signer || !signer.signRaw) {
|
|
60
53
|
throw new common.ProsopoEnvError("GENERAL.CANT_FIND_KEYRINGPAIR", {
|
|
61
|
-
context: {
|
|
54
|
+
context: {
|
|
55
|
+
error: "Signer is not defined, cannot sign message to prove account ownership"
|
|
56
|
+
}
|
|
62
57
|
});
|
|
63
58
|
}
|
|
64
59
|
let result;
|
|
@@ -67,34 +62,24 @@ class ProsopoCaptchaApi {
|
|
|
67
62
|
data: string.stringToHex(requestHash),
|
|
68
63
|
type: "bytes"
|
|
69
64
|
});
|
|
70
|
-
|
|
65
|
+
userRequestHashSignature = signed.signature;
|
|
71
66
|
try {
|
|
72
67
|
result = await this.providerApi.submitCaptchaSolution(
|
|
73
68
|
solutions,
|
|
74
69
|
requestHash,
|
|
75
|
-
this.
|
|
70
|
+
this.userAccount,
|
|
76
71
|
salt,
|
|
77
|
-
|
|
72
|
+
timestamp,
|
|
73
|
+
providerTimestampSignature,
|
|
74
|
+
userRequestHashSignature
|
|
78
75
|
);
|
|
79
76
|
} catch (error) {
|
|
80
|
-
throw new common.ProsopoDatasetError("CAPTCHA.INVALID_CAPTCHA_CHALLENGE", {
|
|
77
|
+
throw new common.ProsopoDatasetError("CAPTCHA.INVALID_CAPTCHA_CHALLENGE", {
|
|
78
|
+
context: { error }
|
|
79
|
+
});
|
|
81
80
|
}
|
|
82
81
|
return [result, commitmentId, tx];
|
|
83
82
|
}
|
|
84
83
|
}
|
|
85
|
-
async function verifyCaptchaData(captchaWithProof) {
|
|
86
|
-
const captcha = captchaWithProof.captcha;
|
|
87
|
-
const proof = captchaWithProof.proof;
|
|
88
|
-
if (!(await Promise.all(captcha.items.map(async (item) => (await datasets.computeItemHash(item)).hash === item.hash))).every(
|
|
89
|
-
(hash) => hash === true
|
|
90
|
-
)) {
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
const captchaHash = datasets.computeCaptchaHash(captcha, false, false, false);
|
|
94
|
-
if (captchaHash !== captcha.captchaContentId) {
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
return util.at(proof, 0).indexOf(captchaHash) !== -1;
|
|
98
|
-
}
|
|
99
84
|
exports.ProsopoCaptchaApi = ProsopoCaptchaApi;
|
|
100
85
|
exports.default = ProsopoCaptchaApi;
|
|
@@ -29,19 +29,43 @@ const logKeyboardEvent = (event, setKeyboardEvent) => {
|
|
|
29
29
|
};
|
|
30
30
|
const logTouchEvent = (event, setTouchEvent) => {
|
|
31
31
|
for (const touch of Array.from(event.touches)) {
|
|
32
|
-
storeLog(
|
|
32
|
+
storeLog(
|
|
33
|
+
{ x: touch.clientX, y: touch.clientY, timestamp: event.timeStamp },
|
|
34
|
+
setTouchEvent
|
|
35
|
+
);
|
|
33
36
|
}
|
|
34
37
|
};
|
|
35
38
|
const startCollector = (setStoredMouseEvents, setStoredTouchEvents, setStoredKeyboardEvents, rootElement) => {
|
|
36
39
|
const form = findContainingForm(rootElement);
|
|
37
40
|
if (form) {
|
|
38
|
-
form.addEventListener(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
form.addEventListener(
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
form.addEventListener(
|
|
42
|
+
"mousemove",
|
|
43
|
+
(e) => logMouseEvent(e, setStoredMouseEvents)
|
|
44
|
+
);
|
|
45
|
+
form.addEventListener(
|
|
46
|
+
"keydown",
|
|
47
|
+
(e) => logKeyboardEvent(e, setStoredKeyboardEvents)
|
|
48
|
+
);
|
|
49
|
+
form.addEventListener(
|
|
50
|
+
"keyup",
|
|
51
|
+
(e) => logKeyboardEvent(e, setStoredKeyboardEvents)
|
|
52
|
+
);
|
|
53
|
+
form.addEventListener(
|
|
54
|
+
"touchstart",
|
|
55
|
+
(e) => logTouchEvent(e, setStoredTouchEvents)
|
|
56
|
+
);
|
|
57
|
+
form.addEventListener(
|
|
58
|
+
"touchend",
|
|
59
|
+
(e) => logTouchEvent(e, setStoredTouchEvents)
|
|
60
|
+
);
|
|
61
|
+
form.addEventListener(
|
|
62
|
+
"touchcancel",
|
|
63
|
+
(e) => logTouchEvent(e, setStoredTouchEvents)
|
|
64
|
+
);
|
|
65
|
+
form.addEventListener(
|
|
66
|
+
"touchmove",
|
|
67
|
+
(e) => logTouchEvent(e, setStoredTouchEvents)
|
|
68
|
+
);
|
|
45
69
|
}
|
|
46
70
|
};
|
|
47
71
|
const findContainingForm = (element) => {
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const types = require("@prosopo/types");
|
|
3
2
|
const util = require("@polkadot/util");
|
|
4
3
|
const string = require("@polkadot/util/string");
|
|
4
|
+
const types = require("@prosopo/types");
|
|
5
5
|
const PROCAPTCHA_STORAGE_KEY = "@prosopo/procaptcha";
|
|
6
6
|
function getProcaptchaStorage() {
|
|
7
7
|
return types.ProsopoLocalStorageSchema.parse(
|
|
8
|
-
JSON.parse(
|
|
8
|
+
JSON.parse(
|
|
9
|
+
util.hexToString(localStorage.getItem(PROCAPTCHA_STORAGE_KEY) || "0x7b7d")
|
|
10
|
+
)
|
|
9
11
|
);
|
|
10
12
|
}
|
|
11
13
|
function setProcaptchaStorage(storage2) {
|
|
12
|
-
localStorage.setItem(
|
|
14
|
+
localStorage.setItem(
|
|
15
|
+
PROCAPTCHA_STORAGE_KEY,
|
|
16
|
+
string.stringToHex(JSON.stringify(types.ProsopoLocalStorageSchema.parse(storage2)))
|
|
17
|
+
);
|
|
13
18
|
}
|
|
14
19
|
function setAccount(account) {
|
|
15
20
|
setProcaptchaStorage({ ...getProcaptchaStorage(), account });
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
1
|
+
export * from "./modules/index.js";
|
|
2
|
+
export * from "./utils/index.js";
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,cAAc,oBAAoB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
1
|
+
export * from "./modules/index.js";
|
|
2
|
+
export * from "./utils/index.js";
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,cAAc,oBAAoB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC"}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { ProcaptchaCallbacks, ProcaptchaClientConfigOutput, ProcaptchaState, ProcaptchaStateUpdateFn
|
|
1
|
+
import { type ProcaptchaCallbacks, type ProcaptchaClientConfigOutput, type ProcaptchaState, type ProcaptchaStateUpdateFn } from "@prosopo/types";
|
|
2
2
|
export declare function Manager(configOptional: ProcaptchaClientConfigOutput, state: ProcaptchaState, onStateUpdate: ProcaptchaStateUpdateFn, callbacks: ProcaptchaCallbacks): {
|
|
3
3
|
start: () => Promise<void>;
|
|
4
4
|
cancel: () => Promise<void>;
|
|
5
5
|
submit: () => Promise<void>;
|
|
6
6
|
select: (hash: string) => void;
|
|
7
7
|
nextRound: () => void;
|
|
8
|
-
exportData: (events: StoredEvents) => Promise<void>;
|
|
9
8
|
};
|
|
10
9
|
//# sourceMappingURL=Manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Manager.d.ts","sourceRoot":"","sources":["../../src/modules/Manager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Manager.d.ts","sourceRoot":"","sources":["../../src/modules/Manager.ts"],"names":[],"mappings":"AAyBA,OAAO,EAKL,KAAK,mBAAmB,EAExB,KAAK,4BAA4B,EAEjC,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAK7B,MAAM,gBAAgB,CAAC;AA8DxB,wBAAgB,OAAO,CACrB,cAAc,EAAE,4BAA4B,EAC5C,KAAK,EAAE,eAAe,EACtB,aAAa,EAAE,uBAAuB,EACtC,SAAS,EAAE,mBAAmB;;;;mBA4OR,MAAM;;EAiJ7B"}
|