@prosopo/procaptcha 2.10.57 → 2.10.58
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/.turbo/turbo-build$colon$cjs.log +13 -12
- package/.turbo/turbo-build$colon$tsc.log +20 -20
- package/.turbo/turbo-build.log +15 -12
- package/CHANGELOG.md +34 -0
- package/dist/_virtual/_rolldown/runtime.js +3 -0
- package/dist/cjs/index.cjs +6 -8
- package/dist/cjs/modules/Manager.cjs +286 -384
- package/dist/cjs/modules/ProsopoCaptchaApi.cjs +41 -74
- package/dist/cjs/modules/collector.cjs +43 -73
- package/dist/cjs/modules/index.cjs +6 -8
- package/dist/index.js +3 -7
- package/dist/modules/Manager.d.ts.map +1 -1
- package/dist/modules/Manager.js +283 -382
- package/dist/modules/Manager.js.map +1 -1
- package/dist/modules/ProsopoCaptchaApi.js +40 -74
- package/dist/modules/collector.js +44 -74
- package/dist/modules/index.js +2 -6
- package/package.json +11 -11
|
@@ -1,388 +1,290 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
account: void 0
|
|
22
|
-
// don't handle timeout here, this should be handled by the state management
|
|
23
|
-
};
|
|
1
|
+
const require_ProsopoCaptchaApi = require("./ProsopoCaptchaApi.cjs");
|
|
2
|
+
let _polkadot_util_string = require("@polkadot/util/string");
|
|
3
|
+
let _prosopo_api = require("@prosopo/api");
|
|
4
|
+
let _prosopo_common = require("@prosopo/common");
|
|
5
|
+
let _prosopo_procaptcha_common = require("@prosopo/procaptcha-common");
|
|
6
|
+
let _prosopo_types = require("@prosopo/types");
|
|
7
|
+
let _prosopo_util = require("@prosopo/util");
|
|
8
|
+
let _prosopo_util_crypto = require("@prosopo/util-crypto");
|
|
9
|
+
//#region src/modules/Manager.ts
|
|
10
|
+
var defaultState = () => {
|
|
11
|
+
return {
|
|
12
|
+
showModal: false,
|
|
13
|
+
loading: false,
|
|
14
|
+
index: 0,
|
|
15
|
+
challenge: void 0,
|
|
16
|
+
solutions: void 0,
|
|
17
|
+
isHuman: false,
|
|
18
|
+
captchaApi: void 0,
|
|
19
|
+
account: void 0
|
|
20
|
+
};
|
|
24
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* The state operator. This is used to mutate the state of Procaptcha during the captcha process. State updates are published via the onStateUpdate callback. This should be used by frontends, e.g. react, to maintain the state of Procaptcha across renders.
|
|
24
|
+
*/
|
|
25
25
|
function Manager(configOptional, state, onStateUpdate, callbacks, frictionlessState, getHoneypotValue) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
solutions[index] = newSolution;
|
|
289
|
-
} else {
|
|
290
|
-
solutions[index] = [...solution, [hash, x || 0, y || 0]];
|
|
291
|
-
}
|
|
292
|
-
updateState({ solutions });
|
|
293
|
-
};
|
|
294
|
-
const nextRound = () => {
|
|
295
|
-
if (!state.challenge) {
|
|
296
|
-
throw new common.ProsopoError("CAPTCHA.NO_CAPTCHA", {
|
|
297
|
-
context: { error: "Cannot select, no Captcha found in state" }
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
if (state.index + 1 >= state.challenge.captchas.length) {
|
|
301
|
-
throw new common.ProsopoError("CAPTCHA.NO_CAPTCHA", {
|
|
302
|
-
context: {
|
|
303
|
-
error: "Cannot select, index is out of range for this Captcha"
|
|
304
|
-
}
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
updateState({ index: state.index + 1 });
|
|
308
|
-
};
|
|
309
|
-
const loadProviderApi = async (providerUrl) => {
|
|
310
|
-
const config = getConfig();
|
|
311
|
-
if (!config.account.address) {
|
|
312
|
-
throw new common.ProsopoEnvError("GENERAL.SITE_KEY_MISSING");
|
|
313
|
-
}
|
|
314
|
-
return new api.ProviderApi(providerUrl, config.account.address);
|
|
315
|
-
};
|
|
316
|
-
const clearTimeout = () => {
|
|
317
|
-
window.clearTimeout(Number(state.timeout));
|
|
318
|
-
updateState({ timeout: void 0 });
|
|
319
|
-
};
|
|
320
|
-
const setValidChallengeTimeout = () => {
|
|
321
|
-
const timeMillis = configOptional.captchas.image.solutionTimeout;
|
|
322
|
-
const successfullChallengeTimeout = setTimeout(() => {
|
|
323
|
-
updateState({ isHuman: false });
|
|
324
|
-
events.onExpired();
|
|
325
|
-
}, timeMillis);
|
|
326
|
-
updateState({ successfullChallengeTimeout });
|
|
327
|
-
};
|
|
328
|
-
const resetState = (frictionlessRestart) => {
|
|
329
|
-
clearTimeout();
|
|
330
|
-
updateState(defaultState());
|
|
331
|
-
events.onReset();
|
|
332
|
-
if (frictionlessRestart) {
|
|
333
|
-
frictionlessRestart();
|
|
334
|
-
}
|
|
335
|
-
};
|
|
336
|
-
const loadAccount = async () => {
|
|
337
|
-
const config = getConfig();
|
|
338
|
-
if (!config.web2 && !config.userAccountAddress) {
|
|
339
|
-
throw new common.ProsopoEnvError("GENERAL.ACCOUNT_NOT_FOUND", {
|
|
340
|
-
context: { error: "Account address has not been set for web3 mode" }
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
const selectAccount = async () => {
|
|
344
|
-
const ext = new (await procaptchaCommon.ExtensionLoader(config.web2))();
|
|
345
|
-
if (frictionlessState) {
|
|
346
|
-
return frictionlessState.userAccount;
|
|
347
|
-
}
|
|
348
|
-
return await ext.getAccount(config);
|
|
349
|
-
};
|
|
350
|
-
const account = await selectAccount();
|
|
351
|
-
updateState({ account });
|
|
352
|
-
return getAccount();
|
|
353
|
-
};
|
|
354
|
-
const getAccount = () => {
|
|
355
|
-
if (!state.account) {
|
|
356
|
-
throw new common.ProsopoEnvError("GENERAL.ACCOUNT_NOT_FOUND", {
|
|
357
|
-
context: { error: "Account not loaded" }
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
const account = state.account;
|
|
361
|
-
return account;
|
|
362
|
-
};
|
|
363
|
-
const getDappAccount = () => {
|
|
364
|
-
if (!state.dappAccount) {
|
|
365
|
-
throw new common.ProsopoEnvError("GENERAL.SITE_KEY_MISSING");
|
|
366
|
-
}
|
|
367
|
-
const dappAccount = state.dappAccount;
|
|
368
|
-
return dappAccount;
|
|
369
|
-
};
|
|
370
|
-
const getExtension = (possiblyAccount) => {
|
|
371
|
-
const account = possiblyAccount || getAccount();
|
|
372
|
-
if (!account.extension) {
|
|
373
|
-
throw new common.ProsopoEnvError("ACCOUNT.NO_POLKADOT_EXTENSION", {
|
|
374
|
-
context: { error: "Extension not loaded" }
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
return account.extension;
|
|
378
|
-
};
|
|
379
|
-
return {
|
|
380
|
-
start,
|
|
381
|
-
cancel,
|
|
382
|
-
submit,
|
|
383
|
-
select,
|
|
384
|
-
nextRound,
|
|
385
|
-
reload
|
|
386
|
-
};
|
|
26
|
+
const events = (0, _prosopo_procaptcha_common.getDefaultEvents)(callbacks);
|
|
27
|
+
const updateState = (0, _prosopo_procaptcha_common.buildUpdateState)(state, onStateUpdate);
|
|
28
|
+
let checkboxClickX = 0;
|
|
29
|
+
let checkboxClickY = 0;
|
|
30
|
+
let previousProviderUrl;
|
|
31
|
+
/**
|
|
32
|
+
* Build the config on demand, using the optional config passed in from the outside. State may override various
|
|
33
|
+
* config values depending on the state of the captcha process. E.g. if the process has been started using account
|
|
34
|
+
* "ABC" and then the user changes account to "DEF" via the optional config prop, the account in use will not change.
|
|
35
|
+
* This is because the captcha process has already been started using account "ABC".
|
|
36
|
+
* @returns the config for procaptcha
|
|
37
|
+
*/
|
|
38
|
+
const getConfig = () => {
|
|
39
|
+
const config = {
|
|
40
|
+
userAccountAddress: "",
|
|
41
|
+
...configOptional
|
|
42
|
+
};
|
|
43
|
+
if (state.account) config.userAccountAddress = state.account.account.address;
|
|
44
|
+
return _prosopo_types.ProcaptchaConfigSchema.parse(config);
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Called on start of user verification. This is when the user ticks the box to claim they are human.
|
|
48
|
+
*/
|
|
49
|
+
const start = async (checkboxX = 0, checkboxY = 0) => {
|
|
50
|
+
checkboxClickX = checkboxX;
|
|
51
|
+
checkboxClickY = checkboxY;
|
|
52
|
+
events.onOpen();
|
|
53
|
+
await (0, _prosopo_procaptcha_common.providerRetry)(async () => {
|
|
54
|
+
if (state.loading) return;
|
|
55
|
+
if (state.isHuman) return;
|
|
56
|
+
updateState({ loading: true });
|
|
57
|
+
updateState({ attemptCount: state.attemptCount ? state.attemptCount + 1 : 1 });
|
|
58
|
+
updateState({ sessionId: frictionlessState?.sessionId });
|
|
59
|
+
const config = getConfig();
|
|
60
|
+
updateState({ dappAccount: config.account.address });
|
|
61
|
+
await (0, _prosopo_util.sleep)(100);
|
|
62
|
+
const account = await loadAccount();
|
|
63
|
+
let captchaApi = state.captchaApi;
|
|
64
|
+
if (!frictionlessState?.provider) {
|
|
65
|
+
const currentConfig = getConfig();
|
|
66
|
+
const getRandomProviderResponse = await (0, _prosopo_procaptcha_common.getProcaptchaRandomActiveProvider)(currentConfig.defaultEnvironment, (0, _prosopo_procaptcha_common.pickIpMode)(currentConfig), {
|
|
67
|
+
attempt: state.attemptCount,
|
|
68
|
+
excludeUrl: previousProviderUrl
|
|
69
|
+
});
|
|
70
|
+
const providerUrl = getRandomProviderResponse.provider.url;
|
|
71
|
+
previousProviderUrl = providerUrl;
|
|
72
|
+
const providerApi = await loadProviderApi(providerUrl);
|
|
73
|
+
captchaApi = new require_ProsopoCaptchaApi.ProsopoCaptchaApi(account.account.address, getRandomProviderResponse, providerApi, config.web2, config.account.address || "");
|
|
74
|
+
updateState({ captchaApi });
|
|
75
|
+
} else {
|
|
76
|
+
const providerUrl = frictionlessState.provider.provider.url;
|
|
77
|
+
const providerApi = await loadProviderApi(providerUrl);
|
|
78
|
+
captchaApi = new require_ProsopoCaptchaApi.ProsopoCaptchaApi(account.account.address, frictionlessState.provider, providerApi, config.web2, config.account.address || "");
|
|
79
|
+
updateState({ captchaApi });
|
|
80
|
+
}
|
|
81
|
+
const simdReadingsOnChallenge = frictionlessState?.getSimdReadings ? await frictionlessState.getSimdReadings(0) : void 0;
|
|
82
|
+
const challenge = await captchaApi?.getCaptchaChallenge(state.sessionId, simdReadingsOnChallenge);
|
|
83
|
+
if (challenge.error) {
|
|
84
|
+
updateState({
|
|
85
|
+
loading: false,
|
|
86
|
+
error: {
|
|
87
|
+
message: challenge.error.message,
|
|
88
|
+
key: challenge.error.key || "API.UNKNOWN_ERROR"
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
events.onError(new Error(challenge.error?.message));
|
|
92
|
+
} else {
|
|
93
|
+
if (challenge.captchas.length <= 0) throw new _prosopo_common.ProsopoDatasetError("DEVELOPER.PROVIDER_NO_CAPTCHA");
|
|
94
|
+
const timeMillis = challenge.captchas.map((captcha) => captcha.timeLimitMs || config.captchas.image.challengeTimeout).reduce((a, b) => a + b);
|
|
95
|
+
const timeout = setTimeout(() => {
|
|
96
|
+
events.onChallengeExpired();
|
|
97
|
+
updateState({
|
|
98
|
+
isHuman: false,
|
|
99
|
+
showModal: false,
|
|
100
|
+
loading: false
|
|
101
|
+
});
|
|
102
|
+
}, timeMillis);
|
|
103
|
+
updateState({
|
|
104
|
+
index: 0,
|
|
105
|
+
solutions: challenge.captchas.map(() => []),
|
|
106
|
+
challenge,
|
|
107
|
+
showModal: true,
|
|
108
|
+
timeout,
|
|
109
|
+
loading: false
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}, start, resetState, state.attemptCount, 10);
|
|
113
|
+
};
|
|
114
|
+
const submit = async () => {
|
|
115
|
+
await (0, _prosopo_procaptcha_common.providerRetry)(async () => {
|
|
116
|
+
clearTimeout();
|
|
117
|
+
if (!state.challenge) throw new _prosopo_common.ProsopoError("CAPTCHA.NO_CAPTCHA", { context: { error: "Cannot submit, no Captcha found in state" } });
|
|
118
|
+
updateState({ showModal: false });
|
|
119
|
+
const challenge = state.challenge;
|
|
120
|
+
const captchaSolution = state.challenge.captchas.map((captcha, index) => {
|
|
121
|
+
const solution = (0, _prosopo_util.at)(state.solutions, index);
|
|
122
|
+
const shapeCoords = solution.flatMap(([_, x, y]) => [x, y]);
|
|
123
|
+
const coords = index === 0 ? [
|
|
124
|
+
checkboxClickX,
|
|
125
|
+
checkboxClickY,
|
|
126
|
+
...shapeCoords
|
|
127
|
+
] : shapeCoords;
|
|
128
|
+
const salt = (0, _prosopo_util_crypto.randomAsHex)(coords.map((x) => x.toString(16).length + 4).reduce((acc, curr) => acc + curr, 0));
|
|
129
|
+
const saltCoord = (0, _prosopo_util.embedData)(salt, coords);
|
|
130
|
+
return {
|
|
131
|
+
captchaId: captcha.captchaId,
|
|
132
|
+
captchaContentId: captcha.captchaContentId,
|
|
133
|
+
salt: saltCoord,
|
|
134
|
+
solution: solution.flatMap((s) => s[0])
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
const account = getAccount();
|
|
138
|
+
const signer = getExtension(account).signer;
|
|
139
|
+
if (!(0, _prosopo_util.at)(challenge.captchas, 0).datasetId) throw new _prosopo_common.ProsopoDatasetError("CAPTCHA.INVALID_CAPTCHA_ID", { context: { error: "No datasetId set for challenge" } });
|
|
140
|
+
const captchaApi = state.captchaApi;
|
|
141
|
+
if (!captchaApi) throw new _prosopo_common.ProsopoError("CAPTCHA.INVALID_TOKEN", { context: { error: "No Captcha API found in state" } });
|
|
142
|
+
if (!signer || !signer.signRaw) throw new _prosopo_common.ProsopoEnvError("GENERAL.CANT_FIND_KEYRINGPAIR", { context: { error: "Signer is not defined, cannot sign message to prove account ownership" } });
|
|
143
|
+
const userTimestampSignature = await signer.signRaw({
|
|
144
|
+
address: account.account.address,
|
|
145
|
+
data: (0, _polkadot_util_string.stringToHex)(challenge[_prosopo_types.ApiParams.timestamp]),
|
|
146
|
+
type: "bytes"
|
|
147
|
+
});
|
|
148
|
+
let encryptedBehavioralData;
|
|
149
|
+
if (frictionlessState?.encryptBehavioralData && (frictionlessState?.behaviorCollector1 || frictionlessState?.behaviorCollector2 || frictionlessState?.behaviorCollector3)) try {
|
|
150
|
+
const behavioralData = {
|
|
151
|
+
collector1: frictionlessState.behaviorCollector1?.getData() || [],
|
|
152
|
+
collector2: frictionlessState.behaviorCollector2?.getData() || [],
|
|
153
|
+
collector3: frictionlessState.behaviorCollector3?.getData() || [],
|
|
154
|
+
deviceCapability: frictionlessState.deviceCapability || "unknown"
|
|
155
|
+
};
|
|
156
|
+
const dataToEncrypt = frictionlessState.packBehavioralData ? frictionlessState.packBehavioralData(behavioralData) : behavioralData;
|
|
157
|
+
encryptedBehavioralData = await frictionlessState.encryptBehavioralData(JSON.stringify(dataToEncrypt));
|
|
158
|
+
} catch {}
|
|
159
|
+
const simdReadings = await (0, _prosopo_procaptcha_common.getSimdReadingsForSubmit)(frictionlessState);
|
|
160
|
+
const hpValue = getHoneypotValue?.();
|
|
161
|
+
const clientMetaData = hpValue ? { hp: hpValue } : void 0;
|
|
162
|
+
const submission = await captchaApi.submitCaptchaSolution(userTimestampSignature.signature, challenge.requestHash, captchaSolution, challenge.timestamp, challenge.signature.provider.requestHash, encryptedBehavioralData, simdReadings, clientMetaData);
|
|
163
|
+
const isHuman = submission[0].verified;
|
|
164
|
+
updateState({
|
|
165
|
+
submission,
|
|
166
|
+
isHuman,
|
|
167
|
+
loading: false
|
|
168
|
+
});
|
|
169
|
+
if (state.isHuman) {
|
|
170
|
+
const providerUrl = captchaApi.provider.provider.url;
|
|
171
|
+
events.onHuman((0, _prosopo_types.encodeProcaptchaOutput)({
|
|
172
|
+
[_prosopo_types.ApiParams.providerUrl]: providerUrl,
|
|
173
|
+
[_prosopo_types.ApiParams.user]: account.account.address,
|
|
174
|
+
[_prosopo_types.ApiParams.dapp]: getDappAccount(),
|
|
175
|
+
[_prosopo_types.ApiParams.commitmentId]: (0, _prosopo_util.hashToHex)(submission[1]),
|
|
176
|
+
[_prosopo_types.ApiParams.timestamp]: challenge.timestamp,
|
|
177
|
+
[_prosopo_types.ApiParams.signature]: {
|
|
178
|
+
[_prosopo_types.ApiParams.provider]: { [_prosopo_types.ApiParams.requestHash]: challenge.signature.provider.requestHash },
|
|
179
|
+
[_prosopo_types.ApiParams.user]: { [_prosopo_types.ApiParams.timestamp]: userTimestampSignature.signature }
|
|
180
|
+
},
|
|
181
|
+
[_prosopo_types.ApiParams.captchaType]: _prosopo_types.CaptchaType.image
|
|
182
|
+
}));
|
|
183
|
+
setValidChallengeTimeout();
|
|
184
|
+
} else {
|
|
185
|
+
events.onFailed();
|
|
186
|
+
resetState(frictionlessState?.restart);
|
|
187
|
+
}
|
|
188
|
+
}, start, resetState, state.attemptCount, 10);
|
|
189
|
+
};
|
|
190
|
+
const cancel = async () => {
|
|
191
|
+
clearTimeout();
|
|
192
|
+
resetState(frictionlessState?.restart);
|
|
193
|
+
events.onClose();
|
|
194
|
+
};
|
|
195
|
+
const reload = async () => {
|
|
196
|
+
clearTimeout();
|
|
197
|
+
events.onReload();
|
|
198
|
+
resetState(frictionlessState?.restart);
|
|
199
|
+
if (!frictionlessState?.restart) await start();
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* (De)Select an image from the solution for the current round. If the hash is already in the solutions list, it will be removed (deselected) and if not it will be added (selected).
|
|
203
|
+
* @param hash the hash of the image
|
|
204
|
+
* @param x
|
|
205
|
+
* @param y
|
|
206
|
+
*/
|
|
207
|
+
const select = (hash, x, y) => {
|
|
208
|
+
if (!state.challenge) throw new _prosopo_common.ProsopoError("CAPTCHA.NO_CAPTCHA", { context: { error: "Cannot select, no Captcha found in state" } });
|
|
209
|
+
if (state.index >= state.challenge.captchas.length || state.index < 0) throw new _prosopo_common.ProsopoError("CAPTCHA.NO_CAPTCHA", { context: { error: "Cannot select, index is out of range for this Captcha" } });
|
|
210
|
+
const index = state.index;
|
|
211
|
+
const solutions = state.solutions;
|
|
212
|
+
const solution = (0, _prosopo_util.at)(solutions, index);
|
|
213
|
+
if (solution.some((s) => s[0] === hash)) solutions[index] = solution.filter((s) => s[0] !== hash);
|
|
214
|
+
else solutions[index] = [...solution, [
|
|
215
|
+
hash,
|
|
216
|
+
x || 0,
|
|
217
|
+
y || 0
|
|
218
|
+
]];
|
|
219
|
+
updateState({ solutions });
|
|
220
|
+
};
|
|
221
|
+
/**
|
|
222
|
+
* Proceed to the next round of the challenge.
|
|
223
|
+
*/
|
|
224
|
+
const nextRound = () => {
|
|
225
|
+
if (!state.challenge) throw new _prosopo_common.ProsopoError("CAPTCHA.NO_CAPTCHA", { context: { error: "Cannot select, no Captcha found in state" } });
|
|
226
|
+
if (state.index + 1 >= state.challenge.captchas.length) throw new _prosopo_common.ProsopoError("CAPTCHA.NO_CAPTCHA", { context: { error: "Cannot select, index is out of range for this Captcha" } });
|
|
227
|
+
updateState({ index: state.index + 1 });
|
|
228
|
+
};
|
|
229
|
+
const loadProviderApi = async (providerUrl) => {
|
|
230
|
+
const config = getConfig();
|
|
231
|
+
if (!config.account.address) throw new _prosopo_common.ProsopoEnvError("GENERAL.SITE_KEY_MISSING");
|
|
232
|
+
return new _prosopo_api.ProviderApi(providerUrl, config.account.address);
|
|
233
|
+
};
|
|
234
|
+
const clearTimeout = () => {
|
|
235
|
+
window.clearTimeout(Number(state.timeout));
|
|
236
|
+
updateState({ timeout: void 0 });
|
|
237
|
+
};
|
|
238
|
+
const setValidChallengeTimeout = () => {
|
|
239
|
+
const timeMillis = configOptional.captchas.image.solutionTimeout;
|
|
240
|
+
const successfullChallengeTimeout = setTimeout(() => {
|
|
241
|
+
updateState({ isHuman: false });
|
|
242
|
+
events.onExpired();
|
|
243
|
+
}, timeMillis);
|
|
244
|
+
updateState({ successfullChallengeTimeout });
|
|
245
|
+
};
|
|
246
|
+
const resetState = (frictionlessRestart) => {
|
|
247
|
+
clearTimeout();
|
|
248
|
+
updateState(defaultState());
|
|
249
|
+
events.onReset();
|
|
250
|
+
if (frictionlessRestart) frictionlessRestart();
|
|
251
|
+
};
|
|
252
|
+
/**
|
|
253
|
+
* Load the account using address specified in config, or generate new address if not found in local storage for web2 mode.
|
|
254
|
+
*/
|
|
255
|
+
const loadAccount = async () => {
|
|
256
|
+
const config = getConfig();
|
|
257
|
+
if (!config.web2 && !config.userAccountAddress) throw new _prosopo_common.ProsopoEnvError("GENERAL.ACCOUNT_NOT_FOUND", { context: { error: "Account address has not been set for web3 mode" } });
|
|
258
|
+
const selectAccount = async () => {
|
|
259
|
+
const ext = new (await ((0, _prosopo_procaptcha_common.ExtensionLoader)(config.web2)))();
|
|
260
|
+
if (frictionlessState) return frictionlessState.userAccount;
|
|
261
|
+
return await ext.getAccount(config);
|
|
262
|
+
};
|
|
263
|
+
const account = await selectAccount();
|
|
264
|
+
updateState({ account });
|
|
265
|
+
return getAccount();
|
|
266
|
+
};
|
|
267
|
+
const getAccount = () => {
|
|
268
|
+
if (!state.account) throw new _prosopo_common.ProsopoEnvError("GENERAL.ACCOUNT_NOT_FOUND", { context: { error: "Account not loaded" } });
|
|
269
|
+
return state.account;
|
|
270
|
+
};
|
|
271
|
+
const getDappAccount = () => {
|
|
272
|
+
if (!state.dappAccount) throw new _prosopo_common.ProsopoEnvError("GENERAL.SITE_KEY_MISSING");
|
|
273
|
+
return state.dappAccount;
|
|
274
|
+
};
|
|
275
|
+
const getExtension = (possiblyAccount) => {
|
|
276
|
+
const account = possiblyAccount || getAccount();
|
|
277
|
+
if (!account.extension) throw new _prosopo_common.ProsopoEnvError("ACCOUNT.NO_POLKADOT_EXTENSION", { context: { error: "Extension not loaded" } });
|
|
278
|
+
return account.extension;
|
|
279
|
+
};
|
|
280
|
+
return {
|
|
281
|
+
start,
|
|
282
|
+
cancel,
|
|
283
|
+
submit,
|
|
284
|
+
select,
|
|
285
|
+
nextRound,
|
|
286
|
+
reload
|
|
287
|
+
};
|
|
387
288
|
}
|
|
289
|
+
//#endregion
|
|
388
290
|
exports.Manager = Manager;
|