@prosopo/procaptcha 0.2.32 → 0.2.36

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.
@@ -6,11 +6,13 @@ require("./types/index.cjs");
6
6
  require("./utils/index.cjs");
7
7
  const Manager = require("./modules/Manager.cjs");
8
8
  const ProsopoCaptchaApi = require("./modules/ProsopoCaptchaApi.cjs");
9
+ const collector = require("./modules/collector.cjs");
9
10
  const manager = require("./types/manager.cjs");
10
11
  const utils = require("./utils/utils.cjs");
11
12
  exports.Manager = Manager.Manager;
12
13
  exports.defaultState = Manager.defaultState;
13
14
  exports.getNetwork = Manager.getNetwork;
14
15
  exports.ProsopoCaptchaApi = ProsopoCaptchaApi.ProsopoCaptchaApi;
16
+ exports.startCollector = collector.startCollector;
15
17
  exports.ProcapchaEventNames = manager.ProcapchaEventNames;
16
18
  exports.sleep = utils.sleep;
@@ -5,10 +5,10 @@ const Api = require("@polkadot/api/promise/Api");
5
5
  const types = require("@prosopo/types");
6
6
  const api = require("@prosopo/api");
7
7
  const keyring = require("@polkadot/keyring");
8
- const contract = require("@prosopo/contract");
9
8
  const common = require("@prosopo/common");
9
+ const contract = require("@prosopo/contract");
10
10
  const ws = require("@polkadot/rpc-provider/ws");
11
- const captcha = require("../contracts/captcha/dist/contract-info/captcha.cjs");
11
+ const contractInfo = require("@prosopo/captcha-contract/contract-info");
12
12
  const util = require("@prosopo/util");
13
13
  const random = require("@polkadot/util-crypto/random");
14
14
  const utils = require("../utils/utils.cjs");
@@ -42,7 +42,9 @@ const buildUpdateState = (state, onStateUpdate) => {
42
42
  const getNetwork = (config) => {
43
43
  const network = config.networks[config.defaultNetwork];
44
44
  if (!network) {
45
- throw new Error(`No network found for environment ${config.defaultEnvironment}`);
45
+ throw new common.ProsopoEnvError("DEVELOPER.NETWORK_NOT_FOUND", {
46
+ context: { error: `No network found for environment ${config.defaultEnvironment}` }
47
+ });
46
48
  }
47
49
  return network;
48
50
  };
@@ -57,12 +59,14 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
57
59
  onError: alertError,
58
60
  onHuman: (output) => {
59
61
  console.log("onHuman event triggered", output);
62
+ updateState({ sendData: !state.sendData });
60
63
  },
61
64
  onExtensionNotFound: () => {
62
65
  alert("No extension found");
63
66
  },
64
67
  onFailed: () => {
65
68
  alert("Captcha challenge failed. Please try again");
69
+ updateState({ sendData: !state.sendData });
66
70
  },
67
71
  onExpired: () => {
68
72
  alert("Completed challenge has expired, please try again");
@@ -72,6 +76,7 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
72
76
  },
73
77
  onOpen: () => {
74
78
  console.log("onOpen event triggered");
79
+ updateState({ sendData: !state.sendData });
75
80
  },
76
81
  onClose: () => {
77
82
  console.log("onClose event triggered");
@@ -146,7 +151,12 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
146
151
  if (providerUrlFromStorage) {
147
152
  providerApi = await loadProviderApi(providerUrlFromStorage);
148
153
  try {
149
- const verifyDappUserResponse = await providerApi.verifyDappUser(account.account.address);
154
+ const verifyDappUserResponse = await providerApi.verifyDappUser(
155
+ getDappAccount(),
156
+ account.account.address,
157
+ void 0,
158
+ configOptional.challengeValidLength
159
+ );
150
160
  if (verifyDappUserResponse.solutionApproved) {
151
161
  updateState({ isHuman: true, loading: false });
152
162
  events.onHuman({
@@ -183,9 +193,9 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
183
193
  const challenge = await captchaApi.getCaptchaChallenge();
184
194
  console.log("challenge", challenge);
185
195
  if (challenge.captchas.length <= 0) {
186
- throw new Error("No captchas returned from provider");
196
+ throw new common.ProsopoApiError("DEVELOPER.PROVIDER_NO_CAPTCHA");
187
197
  }
188
- const timeMillis = challenge.captchas.map((captcha2) => captcha2.captcha.timeLimitMs || 30 * 1e3).reduce((a, b) => a + b);
198
+ const timeMillis = challenge.captchas.map((captcha) => captcha.captcha.timeLimitMs || 30 * 1e3).reduce((a, b) => a + b);
189
199
  const timeout = setTimeout(() => {
190
200
  console.log("challenge expired after " + timeMillis + "ms");
191
201
  events.onChallengeExpired();
@@ -206,16 +216,18 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
206
216
  console.log("submitting solutions");
207
217
  clearTimeout();
208
218
  if (!state.challenge) {
209
- throw new Error("cannot submit, no challenge found");
219
+ throw new common.ProsopoError("CAPTCHA.NO_CAPTCHA", {
220
+ context: { error: "Cannot submit, no Captcha found in state" }
221
+ });
210
222
  }
211
223
  updateState({ showModal: false });
212
224
  const challenge = state.challenge;
213
225
  const salt = random.randomAsHex();
214
- const captchaSolution = state.challenge.captchas.map((captcha2, index) => {
226
+ const captchaSolution = state.challenge.captchas.map((captcha, index) => {
215
227
  const solution = util.at(state.solutions, index);
216
228
  return {
217
- captchaId: captcha2.captcha.captchaId,
218
- captchaContentId: captcha2.captcha.captchaContentId,
229
+ captchaId: captcha.captcha.captchaId,
230
+ captchaContentId: captcha.captcha.captchaContentId,
219
231
  salt,
220
232
  solution
221
233
  };
@@ -225,7 +237,9 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
225
237
  const signer = account.extension.signer;
226
238
  const first = util.at(challenge.captchas, 0);
227
239
  if (!first.captcha.datasetId) {
228
- throw new Error("No datasetId set for challenge");
240
+ throw new common.ProsopoDatasetError("CAPTCHA.INVALID_CAPTCHA_ID", {
241
+ context: { error: "No datasetId set for challenge" }
242
+ });
229
243
  }
230
244
  const captchaApi = getCaptchaApi();
231
245
  const submission = await captchaApi.submitCaptchaSolution(
@@ -266,10 +280,14 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
266
280
  };
267
281
  const select = (hash) => {
268
282
  if (!state.challenge) {
269
- throw new Error("cannot select, no challenge found");
283
+ throw new common.ProsopoError("CAPTCHA.NO_CAPTCHA", {
284
+ context: { error: "Cannot select, no Captcha found in state" }
285
+ });
270
286
  }
271
287
  if (state.index >= state.challenge.captchas.length || state.index < 0) {
272
- throw new Error("cannot select, round index out of range");
288
+ throw new common.ProsopoError("CAPTCHA.NO_CAPTCHA", {
289
+ context: { error: "Cannot select, index is out of range for this Captcha" }
290
+ });
273
291
  }
274
292
  const index = state.index;
275
293
  const solutions = state.solutions;
@@ -285,10 +303,14 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
285
303
  };
286
304
  const nextRound = () => {
287
305
  if (!state.challenge) {
288
- throw new Error("cannot proceed to next round, no challenge found");
306
+ throw new common.ProsopoError("CAPTCHA.NO_CAPTCHA", {
307
+ context: { error: "Cannot select, no Captcha found in state" }
308
+ });
289
309
  }
290
310
  if (state.index + 1 >= state.challenge.captchas.length) {
291
- throw new Error("cannot proceed to next round, already at last round");
311
+ throw new common.ProsopoError("CAPTCHA.NO_CAPTCHA", {
312
+ context: { error: "Cannot select, index is out of range for this Captcha" }
313
+ });
292
314
  }
293
315
  console.log("proceeding to next round");
294
316
  updateState({ index: state.index + 1 });
@@ -334,14 +356,16 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
334
356
  };
335
357
  const getCaptchaApi = () => {
336
358
  if (!state.captchaApi) {
337
- throw new Error("Captcha api not set");
359
+ throw new common.ProsopoApiError("API.UNKNOWN", { context: { error: "Captcha api not set", state } });
338
360
  }
339
361
  return state.captchaApi;
340
362
  };
341
363
  const loadAccount = async () => {
342
364
  const config = getConfig();
343
365
  if (!config.web2 && !config.userAccountAddress) {
344
- throw new Error("Account address has not been set for web3 mode");
366
+ throw new common.ProsopoEnvError("GENERAL.ACCOUNT_NOT_FOUND", {
367
+ context: { error: "Account address has not been set for web3 mode" }
368
+ });
345
369
  }
346
370
  const ext = config.web2 ? new ExtensionWeb2() : new ExtensionWeb3();
347
371
  const account = await ext.getAccount(config);
@@ -352,7 +376,7 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
352
376
  };
353
377
  const getAccount = () => {
354
378
  if (!state.account) {
355
- throw new Error("Account not loaded");
379
+ throw new common.ProsopoEnvError("GENERAL.ACCOUNT_NOT_FOUND", { context: { error: "Account not loaded" } });
356
380
  }
357
381
  const account = state.account;
358
382
  return account;
@@ -366,7 +390,7 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
366
390
  };
367
391
  const getBlockNumber = () => {
368
392
  if (!state.blockNumber) {
369
- throw new Error("Account not loaded");
393
+ throw new common.ProsopoContractError("CAPTCHA.INVALID_BLOCK_NO", { context: { error: "Block number not found" } });
370
394
  }
371
395
  const blockNumber = state.blockNumber;
372
396
  return blockNumber;
@@ -379,19 +403,29 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
379
403
  const keyring$1 = new keyring.Keyring({ type, ss58Format: api2.registry.chainSS58 });
380
404
  return new contract.ProsopoCaptchaContract(
381
405
  api2,
382
- JSON.parse(captcha.ContractAbi),
406
+ JSON.parse(contractInfo.ContractAbi),
383
407
  network.contract.address,
384
408
  "prosopo",
385
409
  0,
386
410
  keyring$1.addFromAddress(getAccount().account.address)
387
411
  );
388
412
  };
413
+ const exportData = async (events2) => {
414
+ var _a;
415
+ const providerUrl = storage.getProviderUrl() || ((_a = state.captchaApi) == null ? void 0 : _a.provider.provider.url.toString());
416
+ if (!providerUrl) {
417
+ return;
418
+ }
419
+ const providerApi = await loadProviderApi(providerUrl);
420
+ await providerApi.submitUserEvents(events2, getAccount().account.address);
421
+ };
389
422
  return {
390
423
  start,
391
424
  cancel,
392
425
  submit,
393
426
  select,
394
- nextRound
427
+ nextRound,
428
+ exportData
395
429
  };
396
430
  }
397
431
  exports.Manager = Manager;
@@ -1,12 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
- require("../packages/datasets/dist/index.cjs");
4
- const handlers = require("../api/handlers.cjs");
3
+ const datasets = require("@prosopo/datasets");
5
4
  const common = require("@prosopo/common");
6
5
  const util = require("@prosopo/util");
7
6
  const string = require("@polkadot/util/string");
8
- const merkle = require("../packages/datasets/dist/captcha/merkle.cjs");
9
- const captcha = require("../packages/datasets/dist/captcha/captcha.cjs");
10
7
  class ProsopoCaptchaApi {
11
8
  constructor(userAccount, contract, provider, providerApi, web2, dappAccount) {
12
9
  this.userAccount = userAccount;
@@ -20,9 +17,16 @@ class ProsopoCaptchaApi {
20
17
  try {
21
18
  const captchaChallenge = await this.providerApi.getCaptchaChallenge(this.userAccount, this.provider);
22
19
  this.verifyCaptchaChallengeContent(this.provider, captchaChallenge);
20
+ captchaChallenge.captchas.forEach((captcha) => {
21
+ captcha.captcha.items.forEach((item) => {
22
+ if (item.data) {
23
+ item.data = item.data.replace(/^http(s)*:\/\//, "//");
24
+ }
25
+ });
26
+ });
23
27
  return captchaChallenge;
24
- } catch (e) {
25
- throw new common.ProsopoEnvError(e);
28
+ } catch (error) {
29
+ throw new common.ProsopoEnvError("CAPTCHA.INVALID_CAPTCHA_CHALLENGE", { context: { error } });
26
30
  }
27
31
  }
28
32
  verifyCaptchaChallengeContent(provider, captchaChallenge) {
@@ -37,7 +41,7 @@ class ProsopoCaptchaApi {
37
41
  if (!verifyCaptchaData(captchaWithProof)) {
38
42
  throw new common.ProsopoEnvError("CAPTCHA.INVALID_CAPTCHA_CHALLENGE");
39
43
  }
40
- if (!merkle.verifyProof(captchaWithProof.captcha.captchaContentId, captchaWithProof.proof)) {
44
+ if (!datasets.verifyProof(captchaWithProof.captcha.captchaContentId, captchaWithProof.proof)) {
41
45
  throw new common.ProsopoEnvError("CAPTCHA.INVALID_CAPTCHA_CHALLENGE");
42
46
  }
43
47
  }
@@ -45,8 +49,8 @@ class ProsopoCaptchaApi {
45
49
  return;
46
50
  }
47
51
  async submitCaptchaSolution(signer, requestHash, datasetId, solutions, salt) {
48
- const tree = new merkle.CaptchaMerkleTree();
49
- const captchasHashed = solutions.map((captcha$1) => captcha.computeCaptchaSolutionHash(captcha$1));
52
+ const tree = new datasets.CaptchaMerkleTree();
53
+ const captchasHashed = solutions.map((captcha) => datasets.computeCaptchaSolutionHash(captcha));
50
54
  tree.build(captchasHashed);
51
55
  const commitmentId = tree.root.hash;
52
56
  console.log("solveCaptchaChallenge commitmentId", commitmentId);
@@ -54,7 +58,9 @@ class ProsopoCaptchaApi {
54
58
  let signature = void 0;
55
59
  if (this.web2) {
56
60
  if (!signer || !signer.signRaw) {
57
- throw new Error("Signer is not defined, cannot sign message to prove account ownership");
61
+ throw new common.ProsopoEnvError("GENERAL.CANT_FIND_KEYRINGPAIR", {
62
+ context: { error: "Signer is not defined, cannot sign message to prove account ownership" }
63
+ });
58
64
  }
59
65
  const signed = await signer.signRaw({
60
66
  address: this.userAccount,
@@ -72,22 +78,22 @@ class ProsopoCaptchaApi {
72
78
  salt,
73
79
  signature
74
80
  );
75
- } catch (err) {
76
- throw new handlers.ProsopoApiError(err);
81
+ } catch (error) {
82
+ throw new common.ProsopoDatasetError("CAPTCHA.INVALID_CAPTCHA_CHALLENGE", { context: { error } });
77
83
  }
78
84
  return [result, commitmentId, tx];
79
85
  }
80
86
  }
81
87
  async function verifyCaptchaData(captchaWithProof) {
82
- const captcha$1 = captchaWithProof.captcha;
88
+ const captcha = captchaWithProof.captcha;
83
89
  const proof = captchaWithProof.proof;
84
- if (!(await Promise.all(captcha$1.items.map(async (item) => (await captcha.computeItemHash(item)).hash === item.hash))).every(
90
+ if (!(await Promise.all(captcha.items.map(async (item) => (await datasets.computeItemHash(item)).hash === item.hash))).every(
85
91
  (hash) => hash === true
86
92
  )) {
87
93
  return false;
88
94
  }
89
- const captchaHash = captcha.computeCaptchaHash(captcha$1, false, false, false);
90
- if (captchaHash !== captcha$1.captchaContentId) {
95
+ const captchaHash = datasets.computeCaptchaHash(captcha, false, false, false);
96
+ if (captchaHash !== captcha.captchaContentId) {
91
97
  return false;
92
98
  }
93
99
  return util.at(proof, 0).indexOf(captchaHash) !== -1;
@@ -366,9 +366,8 @@ function picassoCanvas(roundNumber, seed, params) {
366
366
  }
367
367
  }
368
368
  return x64hash128(canvasElt.toDataURL(), seed);
369
- } catch (e) {
370
- console.log(e);
371
- throw new common.ProsopoEnvError(e);
369
+ } catch (error) {
370
+ throw new common.ProsopoError("WIDGET.CANVAS", { context: { error } });
372
371
  }
373
372
  }
374
373
  exports.picassoCanvas = picassoCanvas;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const COLLECTOR_LIMIT = 1e3;
4
+ const storeLog = (event, setEvents) => {
5
+ setEvents((currentEvents) => {
6
+ let newEvents = [...currentEvents, event];
7
+ if (newEvents.length > COLLECTOR_LIMIT) {
8
+ newEvents = newEvents.slice(1);
9
+ }
10
+ return newEvents;
11
+ });
12
+ };
13
+ const logMouseEvent = (event, setMouseEvent) => {
14
+ const storedEvent = {
15
+ x: event.x,
16
+ y: event.y,
17
+ timestamp: event.timeStamp
18
+ };
19
+ storeLog(storedEvent, setMouseEvent);
20
+ };
21
+ const logKeyboardEvent = (event, setKeyboardEvent) => {
22
+ const storedEvent = {
23
+ key: event.key,
24
+ timestamp: event.timeStamp,
25
+ isShiftKey: event.shiftKey,
26
+ isCtrlKey: event.ctrlKey
27
+ };
28
+ storeLog(storedEvent, setKeyboardEvent);
29
+ };
30
+ const logTouchEvent = (event, setTouchEvent) => {
31
+ for (let i = 0; i < event.touches.length; i++) {
32
+ const touch = event.touches[i];
33
+ if (!touch) {
34
+ continue;
35
+ }
36
+ storeLog({ x: touch.clientX, y: touch.clientY, timestamp: event.timeStamp }, setTouchEvent);
37
+ }
38
+ };
39
+ const startCollector = (setStoredMouseEvents, setStoredTouchEvents, setStoredKeyboardEvents, rootElement) => {
40
+ const form = findContainingForm(rootElement);
41
+ if (form) {
42
+ form.addEventListener("mousemove", (e) => logMouseEvent(e, setStoredMouseEvents));
43
+ form.addEventListener("keydown", (e) => logKeyboardEvent(e, setStoredKeyboardEvents));
44
+ form.addEventListener("keyup", (e) => logKeyboardEvent(e, setStoredKeyboardEvents));
45
+ form.addEventListener("touchstart", (e) => logTouchEvent(e, setStoredTouchEvents));
46
+ form.addEventListener("touchend", (e) => logTouchEvent(e, setStoredTouchEvents));
47
+ form.addEventListener("touchcancel", (e) => logTouchEvent(e, setStoredTouchEvents));
48
+ form.addEventListener("touchmove", (e) => logTouchEvent(e, setStoredTouchEvents));
49
+ }
50
+ };
51
+ const findContainingForm = (element) => {
52
+ if (element.tagName === "FORM") {
53
+ return element;
54
+ }
55
+ if (element.parentElement) {
56
+ return findContainingForm(element.parentElement);
57
+ }
58
+ return null;
59
+ };
60
+ exports.startCollector = startCollector;
@@ -2,7 +2,9 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const Manager = require("./Manager.cjs");
4
4
  const ProsopoCaptchaApi = require("./ProsopoCaptchaApi.cjs");
5
+ const collector = require("./collector.cjs");
5
6
  exports.Manager = Manager.Manager;
6
7
  exports.defaultState = Manager.defaultState;
7
8
  exports.getNetwork = Manager.getNetwork;
8
9
  exports.ProsopoCaptchaApi = ProsopoCaptchaApi.ProsopoCaptchaApi;
10
+ exports.startCollector = collector.startCollector;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prosopo/procaptcha",
3
- "version": "0.2.32",
3
+ "version": "0.2.36",
4
4
  "author": "PROSOPO LIMITED <info@prosopo.io>",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./dist/index.js",
@@ -45,12 +45,12 @@
45
45
  "@polkadot/types": "10.11.1",
46
46
  "@polkadot/util": "12.6.1",
47
47
  "@polkadot/util-crypto": "12.6.1",
48
- "@prosopo/api": "0.2.32",
49
- "@prosopo/common": "0.2.32",
50
- "@prosopo/contract": "0.2.32",
51
- "@prosopo/datasets": "0.2.32",
52
- "@prosopo/types": "0.2.32",
53
- "@prosopo/util": "0.2.32",
48
+ "@prosopo/api": "0.2.36",
49
+ "@prosopo/common": "0.2.36",
50
+ "@prosopo/contract": "0.2.36",
51
+ "@prosopo/datasets": "0.2.36",
52
+ "@prosopo/types": "0.2.36",
53
+ "@prosopo/util": "0.2.36",
54
54
  "rxjs": "7.8.1"
55
55
  },
56
56
  "devDependencies": {
@@ -1,20 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- class ProsopoApiError extends Error {
4
- constructor(error, context, ...params) {
5
- var __super = (...args) => {
6
- super(...args);
7
- };
8
- if (error instanceof Response) {
9
- __super(`HTTP Error: ${error.status} ${error.statusText}`);
10
- this.cause = error;
11
- } else {
12
- __super(error.message || "Unknown API Error");
13
- this.cause = error;
14
- }
15
- this.name = context && `${ProsopoApiError.name}@${context}` || ProsopoApiError.name;
16
- console.error("\n********************* ERROR *********************\n");
17
- console.error(this.cause, this.stack, ...params);
18
- }
19
- }
20
- exports.ProsopoApiError = ProsopoApiError;