@prosopo/procaptcha-puzzle 2.10.8 → 2.10.10
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 +53 -0
- package/.turbo/turbo-build$colon$tsc.log +56 -0
- package/.turbo/turbo-build.log +57 -57
- package/CHANGELOG.md +17 -0
- package/dist/cjs/components/ProcaptchaPuzzle.cjs +18 -0
- package/dist/cjs/components/ProcaptchaWidget.cjs +191 -0
- package/dist/cjs/components/PuzzleCanvas.cjs +275 -0
- package/dist/cjs/index.cjs +5 -0
- package/dist/cjs/services/Manager.cjs +286 -0
- package/dist/components/ProcaptchaPuzzle.js +17 -5
- package/dist/components/ProcaptchaWidget.js +181 -144
- package/dist/components/PuzzleCanvas.js +258 -189
- package/dist/index.js +5 -3
- package/dist/services/Manager.js +270 -253
- package/package.json +4 -4
- package/src/components/ProcaptchaPuzzle.tsx +38 -0
- package/src/components/ProcaptchaWidget.tsx +261 -0
- package/src/components/PuzzleCanvas.tsx +336 -0
- package/src/index.ts +15 -0
- package/src/services/Manager.ts +454 -0
- package/tsconfig.cjs.json +44 -0
- package/tsconfig.json +45 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsconfig.types.json +9 -0
- package/.turbo/turbo-typecheck.log +0 -4
package/dist/services/Manager.js
CHANGED
|
@@ -1,269 +1,286 @@
|
|
|
1
1
|
import { stringToHex } from "@polkadot/util/string";
|
|
2
2
|
import { ProviderApi } from "@prosopo/api";
|
|
3
3
|
import { ProsopoEnvError } from "@prosopo/common";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { embedData, sleep } from "@prosopo/util";
|
|
4
|
+
import { getDefaultEvents, buildUpdateState, providerRetry, ExtensionLoader, getProcaptchaRandomActiveProvider } from "@prosopo/procaptcha-common";
|
|
5
|
+
import { ProcaptchaConfigSchema, ApiParams, encodeProcaptchaOutput } from "@prosopo/types";
|
|
6
|
+
import { sleep, embedData } from "@prosopo/util";
|
|
8
7
|
import { randomAsHex } from "@prosopo/util-crypto";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
8
|
+
const Manager = (configInput, state, onStateUpdate, callbacks, frictionlessState, getHoneypotValue) => {
|
|
9
|
+
const events = getDefaultEvents(callbacks);
|
|
10
|
+
let storedChallengeResponse;
|
|
11
|
+
let storedProviderApi;
|
|
12
|
+
let storedProviderUrl;
|
|
13
|
+
let storedUser;
|
|
14
|
+
let storedClickX;
|
|
15
|
+
let storedClickY;
|
|
16
|
+
const defaultState = () => {
|
|
17
|
+
return {
|
|
18
|
+
// note order matters! see buildUpdateState. These fields are set in order, so disable modal first, then set loading to false, etc.
|
|
19
|
+
showModal: false,
|
|
20
|
+
loading: false,
|
|
21
|
+
index: 0,
|
|
22
|
+
challenge: void 0,
|
|
23
|
+
solutions: void 0,
|
|
24
|
+
isHuman: false,
|
|
25
|
+
captchaApi: void 0,
|
|
26
|
+
account: void 0
|
|
27
|
+
// don't handle timeout here, this should be handled by the state management
|
|
28
28
|
};
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
};
|
|
30
|
+
const clearTimeout = () => {
|
|
31
|
+
window.clearTimeout(Number(state.timeout));
|
|
32
|
+
updateState({ timeout: void 0 });
|
|
33
|
+
};
|
|
34
|
+
const onFailed = () => {
|
|
35
|
+
updateState({
|
|
36
|
+
isHuman: false,
|
|
37
|
+
loading: false
|
|
38
|
+
});
|
|
39
|
+
events.onFailed();
|
|
40
|
+
resetState(frictionlessState?.restart);
|
|
41
|
+
};
|
|
42
|
+
const clearSuccessfulChallengeTimeout = () => {
|
|
43
|
+
window.clearTimeout(Number(state.successfullChallengeTimeout));
|
|
44
|
+
updateState({ successfullChallengeTimeout: void 0 });
|
|
45
|
+
};
|
|
46
|
+
const getConfig = () => {
|
|
47
|
+
const config = {
|
|
48
|
+
userAccountAddress: configInput.userAccountAddress || "",
|
|
49
|
+
...configInput
|
|
32
50
|
};
|
|
33
|
-
|
|
51
|
+
if (state.account) {
|
|
52
|
+
config.userAccountAddress = state.account.account.address;
|
|
53
|
+
}
|
|
54
|
+
return ProcaptchaConfigSchema.parse(config);
|
|
55
|
+
};
|
|
56
|
+
const getAccount = () => {
|
|
57
|
+
if (!state.account) {
|
|
58
|
+
throw new ProsopoEnvError("GENERAL.ACCOUNT_NOT_FOUND", {
|
|
59
|
+
context: { error: "Account not loaded" }
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
const account = state.account;
|
|
63
|
+
return { account };
|
|
64
|
+
};
|
|
65
|
+
const getDappAccount = () => {
|
|
66
|
+
if (!state.dappAccount) {
|
|
67
|
+
throw new ProsopoEnvError("GENERAL.SITE_KEY_MISSING");
|
|
68
|
+
}
|
|
69
|
+
const dappAccount = state.dappAccount;
|
|
70
|
+
return dappAccount;
|
|
71
|
+
};
|
|
72
|
+
const updateState = buildUpdateState(state, onStateUpdate);
|
|
73
|
+
const resetState = (frictionlessRestart) => {
|
|
74
|
+
clearTimeout();
|
|
75
|
+
clearSuccessfulChallengeTimeout();
|
|
76
|
+
updateState(defaultState());
|
|
77
|
+
events.onReset();
|
|
78
|
+
if (frictionlessRestart) {
|
|
79
|
+
frictionlessRestart();
|
|
80
|
+
}
|
|
81
|
+
storedChallengeResponse = void 0;
|
|
82
|
+
storedProviderApi = void 0;
|
|
83
|
+
storedProviderUrl = void 0;
|
|
84
|
+
storedUser = void 0;
|
|
85
|
+
storedClickX = void 0;
|
|
86
|
+
storedClickY = void 0;
|
|
87
|
+
};
|
|
88
|
+
const setValidChallengeTimeout = () => {
|
|
89
|
+
const timeMillis = getConfig().captchas.puzzle.solutionTimeout;
|
|
90
|
+
const successfullChallengeTimeout = setTimeout(() => {
|
|
91
|
+
updateState({ isHuman: false });
|
|
92
|
+
events.onExpired();
|
|
93
|
+
resetState(frictionlessState?.restart);
|
|
94
|
+
}, timeMillis);
|
|
95
|
+
updateState({ successfullChallengeTimeout });
|
|
96
|
+
};
|
|
97
|
+
const start = async (x = 0, y = 0) => {
|
|
98
|
+
storedClickX = x;
|
|
99
|
+
storedClickY = y;
|
|
100
|
+
await providerRetry(
|
|
101
|
+
async () => {
|
|
102
|
+
if (state.loading) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (state.isHuman) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
resetState();
|
|
34
109
|
updateState({
|
|
35
|
-
|
|
36
|
-
loading: false,
|
|
110
|
+
loading: true
|
|
37
111
|
});
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const config = {
|
|
47
|
-
userAccountAddress: configInput.userAccountAddress || "",
|
|
48
|
-
...configInput,
|
|
112
|
+
updateState({ attemptCount: state.attemptCount + 1 });
|
|
113
|
+
const config = getConfig();
|
|
114
|
+
const selectAccount = async () => {
|
|
115
|
+
if (frictionlessState) {
|
|
116
|
+
return frictionlessState.userAccount;
|
|
117
|
+
}
|
|
118
|
+
const ext = new (await ExtensionLoader(config.web2))();
|
|
119
|
+
return ext.getAccount(config);
|
|
49
120
|
};
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
};
|
|
64
|
-
const getDappAccount = () => {
|
|
65
|
-
if (!state.dappAccount) {
|
|
66
|
-
throw new ProsopoEnvError("GENERAL.SITE_KEY_MISSING");
|
|
121
|
+
const user = await selectAccount();
|
|
122
|
+
const userAccount = user.account.address;
|
|
123
|
+
updateState({
|
|
124
|
+
account: { account: { address: userAccount } }
|
|
125
|
+
});
|
|
126
|
+
updateState({ dappAccount: config.account.address });
|
|
127
|
+
await sleep(100);
|
|
128
|
+
if (!config.web2 && !config.userAccountAddress) {
|
|
129
|
+
throw new ProsopoEnvError("GENERAL.ACCOUNT_NOT_FOUND", {
|
|
130
|
+
context: {
|
|
131
|
+
error: "Account address has not been set for web3 mode"
|
|
132
|
+
}
|
|
133
|
+
});
|
|
67
134
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
updateState(defaultState());
|
|
76
|
-
events.onReset();
|
|
77
|
-
if (frictionlessRestart) {
|
|
78
|
-
frictionlessRestart();
|
|
135
|
+
let getRandomProviderResponse = void 0;
|
|
136
|
+
if (frictionlessState?.provider) {
|
|
137
|
+
getRandomProviderResponse = frictionlessState.provider;
|
|
138
|
+
} else {
|
|
139
|
+
getRandomProviderResponse = await getProcaptchaRandomActiveProvider(
|
|
140
|
+
getConfig().defaultEnvironment
|
|
141
|
+
);
|
|
79
142
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
};
|
|
96
|
-
const start = async (x = 0, y = 0) => {
|
|
97
|
-
storedClickX = x;
|
|
98
|
-
storedClickY = y;
|
|
99
|
-
await providerRetry(async () => {
|
|
100
|
-
if (state.loading) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
if (state.isHuman) {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
resetState();
|
|
107
|
-
updateState({
|
|
108
|
-
loading: true,
|
|
109
|
-
});
|
|
110
|
-
updateState({ attemptCount: state.attemptCount + 1 });
|
|
111
|
-
const config = getConfig();
|
|
112
|
-
const selectAccount = async () => {
|
|
113
|
-
if (frictionlessState) {
|
|
114
|
-
return frictionlessState.userAccount;
|
|
115
|
-
}
|
|
116
|
-
const ext = new (await ExtensionLoader(config.web2))();
|
|
117
|
-
return ext.getAccount(config);
|
|
118
|
-
};
|
|
119
|
-
const user = await selectAccount();
|
|
120
|
-
const userAccount = user.account.address;
|
|
121
|
-
updateState({
|
|
122
|
-
account: { account: { address: userAccount } },
|
|
123
|
-
});
|
|
124
|
-
updateState({ dappAccount: config.account.address });
|
|
125
|
-
await sleep(100);
|
|
126
|
-
if (!config.web2 && !config.userAccountAddress) {
|
|
127
|
-
throw new ProsopoEnvError("GENERAL.ACCOUNT_NOT_FOUND", {
|
|
128
|
-
context: {
|
|
129
|
-
error: "Account address has not been set for web3 mode",
|
|
130
|
-
},
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
let getRandomProviderResponse = undefined;
|
|
134
|
-
if (frictionlessState?.provider) {
|
|
135
|
-
getRandomProviderResponse = frictionlessState.provider;
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
getRandomProviderResponse = await getProcaptchaRandomActiveProvider(getConfig().defaultEnvironment);
|
|
139
|
-
}
|
|
140
|
-
const providerUrl = getRandomProviderResponse.provider.url;
|
|
141
|
-
const providerApi = new ProviderApi(providerUrl, getDappAccount());
|
|
142
|
-
const simdReadingsOnChallenge = frictionlessState?.getSimdReadings
|
|
143
|
-
? await frictionlessState.getSimdReadings(0)
|
|
144
|
-
: undefined;
|
|
145
|
-
const challenge = await providerApi.getPuzzleCaptchaChallenge(userAccount, getDappAccount(), frictionlessState?.sessionId, simdReadingsOnChallenge);
|
|
146
|
-
if (challenge.error) {
|
|
147
|
-
updateState({
|
|
148
|
-
loading: false,
|
|
149
|
-
error: {
|
|
150
|
-
message: challenge.error.message,
|
|
151
|
-
key: challenge.error.key || "API.UNKNOWN_ERROR",
|
|
152
|
-
},
|
|
153
|
-
});
|
|
154
|
-
return;
|
|
143
|
+
const providerUrl = getRandomProviderResponse.provider.url;
|
|
144
|
+
const providerApi = new ProviderApi(providerUrl, getDappAccount());
|
|
145
|
+
const simdReadingsOnChallenge = frictionlessState?.getSimdReadings ? await frictionlessState.getSimdReadings(0) : void 0;
|
|
146
|
+
const challenge = await providerApi.getPuzzleCaptchaChallenge(
|
|
147
|
+
userAccount,
|
|
148
|
+
getDappAccount(),
|
|
149
|
+
frictionlessState?.sessionId,
|
|
150
|
+
simdReadingsOnChallenge
|
|
151
|
+
);
|
|
152
|
+
if (challenge.error) {
|
|
153
|
+
updateState({
|
|
154
|
+
loading: false,
|
|
155
|
+
error: {
|
|
156
|
+
message: challenge.error.message,
|
|
157
|
+
key: challenge.error.key || "API.UNKNOWN_ERROR"
|
|
155
158
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
storedProviderUrl = providerUrl;
|
|
159
|
-
storedUser = user;
|
|
160
|
-
updateState({
|
|
161
|
-
loading: false,
|
|
162
|
-
});
|
|
163
|
-
}, async () => {
|
|
164
|
-
await start();
|
|
165
|
-
}, () => {
|
|
166
|
-
resetState();
|
|
167
|
-
}, state.attemptCount, 3);
|
|
168
|
-
return storedChallengeResponse;
|
|
169
|
-
};
|
|
170
|
-
const submitSolution = async (finalX, finalY, puzzleEvents) => {
|
|
171
|
-
if (!storedChallengeResponse ||
|
|
172
|
-
!storedProviderApi ||
|
|
173
|
-
!storedProviderUrl ||
|
|
174
|
-
!storedUser) {
|
|
175
|
-
throw new ProsopoEnvError("GENERAL.ACCOUNT_NOT_FOUND", {
|
|
176
|
-
context: { error: "No challenge data available. Call start() first." },
|
|
177
|
-
});
|
|
159
|
+
});
|
|
160
|
+
return;
|
|
178
161
|
}
|
|
179
|
-
|
|
162
|
+
storedChallengeResponse = challenge;
|
|
163
|
+
storedProviderApi = providerApi;
|
|
164
|
+
storedProviderUrl = providerUrl;
|
|
165
|
+
storedUser = user;
|
|
166
|
+
updateState({
|
|
167
|
+
loading: false
|
|
168
|
+
});
|
|
169
|
+
},
|
|
170
|
+
async () => {
|
|
171
|
+
await start();
|
|
172
|
+
},
|
|
173
|
+
() => {
|
|
174
|
+
resetState();
|
|
175
|
+
},
|
|
176
|
+
state.attemptCount,
|
|
177
|
+
3
|
|
178
|
+
);
|
|
179
|
+
return storedChallengeResponse;
|
|
180
|
+
};
|
|
181
|
+
const submitSolution = async (finalX, finalY, puzzleEvents) => {
|
|
182
|
+
if (!storedChallengeResponse || !storedProviderApi || !storedProviderUrl || !storedUser) {
|
|
183
|
+
throw new ProsopoEnvError("GENERAL.ACCOUNT_NOT_FOUND", {
|
|
184
|
+
context: { error: "No challenge data available. Call start() first." }
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
updateState({ loading: true });
|
|
188
|
+
try {
|
|
189
|
+
const challenge = storedChallengeResponse;
|
|
190
|
+
const providerApi = storedProviderApi;
|
|
191
|
+
const providerUrl = storedProviderUrl;
|
|
192
|
+
const user = storedUser;
|
|
193
|
+
const config = getConfig();
|
|
194
|
+
const signer = user.extension?.signer;
|
|
195
|
+
if (!signer || !signer.signRaw) {
|
|
196
|
+
throw new ProsopoEnvError("GENERAL.CANT_FIND_KEYRINGPAIR", {
|
|
197
|
+
context: {
|
|
198
|
+
error: "Signer is not defined, cannot sign message to prove account ownership"
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
const userTimestampSignature = await signer.signRaw({
|
|
203
|
+
address: user.account.address,
|
|
204
|
+
data: stringToHex(challenge[ApiParams.timestamp].toString()),
|
|
205
|
+
type: "bytes"
|
|
206
|
+
});
|
|
207
|
+
let encryptedBehavioralData;
|
|
208
|
+
if (frictionlessState?.encryptBehavioralData && (frictionlessState?.behaviorCollector1 || frictionlessState?.behaviorCollector2 || frictionlessState?.behaviorCollector3)) {
|
|
180
209
|
try {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
const userTimestampSignature = await signer.signRaw({
|
|
195
|
-
address: user.account.address,
|
|
196
|
-
data: stringToHex(challenge[ApiParams.timestamp].toString()),
|
|
197
|
-
type: "bytes",
|
|
198
|
-
});
|
|
199
|
-
let encryptedBehavioralData;
|
|
200
|
-
if (frictionlessState?.encryptBehavioralData &&
|
|
201
|
-
(frictionlessState?.behaviorCollector1 ||
|
|
202
|
-
frictionlessState?.behaviorCollector2 ||
|
|
203
|
-
frictionlessState?.behaviorCollector3)) {
|
|
204
|
-
try {
|
|
205
|
-
const behavioralData = {
|
|
206
|
-
collector1: frictionlessState.behaviorCollector1?.getData() || [],
|
|
207
|
-
collector2: frictionlessState.behaviorCollector2?.getData() || [],
|
|
208
|
-
collector3: frictionlessState.behaviorCollector3?.getData() || [],
|
|
209
|
-
deviceCapability: frictionlessState.deviceCapability || "unknown",
|
|
210
|
-
};
|
|
211
|
-
const dataToEncrypt = frictionlessState.packBehavioralData
|
|
212
|
-
? frictionlessState.packBehavioralData(behavioralData)
|
|
213
|
-
: behavioralData;
|
|
214
|
-
encryptedBehavioralData =
|
|
215
|
-
await frictionlessState.encryptBehavioralData(JSON.stringify(dataToEncrypt));
|
|
216
|
-
}
|
|
217
|
-
catch {
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
let salt;
|
|
221
|
-
if (storedClickX !== undefined && storedClickY !== undefined) {
|
|
222
|
-
const coords = [storedClickX, storedClickY];
|
|
223
|
-
const randomSalt = randomAsHex(coords
|
|
224
|
-
.map((coord) => coord.toString(16).length + 4)
|
|
225
|
-
.reduce((acc, curr) => acc + curr, 0));
|
|
226
|
-
salt = embedData(randomSalt, coords);
|
|
227
|
-
}
|
|
228
|
-
const simdReadings = frictionlessState?.getSimdReadings
|
|
229
|
-
? await frictionlessState.getSimdReadings()
|
|
230
|
-
: undefined;
|
|
231
|
-
const hpValue = getHoneypotValue?.();
|
|
232
|
-
const clientMetaData = hpValue ? { hp: hpValue } : undefined;
|
|
233
|
-
const verifiedSolution = await providerApi.submitPuzzleCaptchaSolution(challenge, getAccount().account.account.address, getDappAccount(), finalX, finalY, puzzleEvents, userTimestampSignature.signature.toString(), config.captchas.puzzle.verifiedTimeout, encryptedBehavioralData, salt, simdReadings, clientMetaData);
|
|
234
|
-
if (verifiedSolution[ApiParams.verified]) {
|
|
235
|
-
updateState({
|
|
236
|
-
isHuman: true,
|
|
237
|
-
loading: false,
|
|
238
|
-
});
|
|
239
|
-
events.onHuman(encodeProcaptchaOutput({
|
|
240
|
-
[ApiParams.providerUrl]: providerUrl,
|
|
241
|
-
[ApiParams.user]: getAccount().account.account.address,
|
|
242
|
-
[ApiParams.dapp]: getDappAccount(),
|
|
243
|
-
[ApiParams.challenge]: challenge.challenge,
|
|
244
|
-
[ApiParams.timestamp]: challenge.timestamp,
|
|
245
|
-
[ApiParams.signature]: {
|
|
246
|
-
[ApiParams.provider]: challenge.signature.provider,
|
|
247
|
-
[ApiParams.user]: {
|
|
248
|
-
[ApiParams.timestamp]: userTimestampSignature.signature.toString(),
|
|
249
|
-
},
|
|
250
|
-
},
|
|
251
|
-
}));
|
|
252
|
-
setValidChallengeTimeout();
|
|
253
|
-
return true;
|
|
254
|
-
}
|
|
255
|
-
onFailed();
|
|
256
|
-
return false;
|
|
257
|
-
}
|
|
258
|
-
catch (error) {
|
|
259
|
-
updateState({ loading: false });
|
|
260
|
-
throw error;
|
|
210
|
+
const behavioralData = {
|
|
211
|
+
collector1: frictionlessState.behaviorCollector1?.getData() || [],
|
|
212
|
+
collector2: frictionlessState.behaviorCollector2?.getData() || [],
|
|
213
|
+
collector3: frictionlessState.behaviorCollector3?.getData() || [],
|
|
214
|
+
deviceCapability: frictionlessState.deviceCapability || "unknown"
|
|
215
|
+
};
|
|
216
|
+
const dataToEncrypt = frictionlessState.packBehavioralData ? frictionlessState.packBehavioralData(behavioralData) : behavioralData;
|
|
217
|
+
encryptedBehavioralData = await frictionlessState.encryptBehavioralData(
|
|
218
|
+
JSON.stringify(dataToEncrypt)
|
|
219
|
+
);
|
|
220
|
+
} catch {
|
|
261
221
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
222
|
+
}
|
|
223
|
+
let salt;
|
|
224
|
+
if (storedClickX !== void 0 && storedClickY !== void 0) {
|
|
225
|
+
const coords = [storedClickX, storedClickY];
|
|
226
|
+
const randomSalt = randomAsHex(
|
|
227
|
+
coords.map((coord) => coord.toString(16).length + 4).reduce((acc, curr) => acc + curr, 0)
|
|
228
|
+
);
|
|
229
|
+
salt = embedData(randomSalt, coords);
|
|
230
|
+
}
|
|
231
|
+
const simdReadings = frictionlessState?.getSimdReadings ? await frictionlessState.getSimdReadings() : void 0;
|
|
232
|
+
const hpValue = getHoneypotValue?.();
|
|
233
|
+
const clientMetaData = hpValue ? { hp: hpValue } : void 0;
|
|
234
|
+
const verifiedSolution = await providerApi.submitPuzzleCaptchaSolution(
|
|
235
|
+
challenge,
|
|
236
|
+
getAccount().account.account.address,
|
|
237
|
+
getDappAccount(),
|
|
238
|
+
finalX,
|
|
239
|
+
finalY,
|
|
240
|
+
puzzleEvents,
|
|
241
|
+
userTimestampSignature.signature.toString(),
|
|
242
|
+
config.captchas.puzzle.verifiedTimeout,
|
|
243
|
+
encryptedBehavioralData,
|
|
244
|
+
salt,
|
|
245
|
+
simdReadings,
|
|
246
|
+
clientMetaData
|
|
247
|
+
);
|
|
248
|
+
if (verifiedSolution[ApiParams.verified]) {
|
|
249
|
+
updateState({
|
|
250
|
+
isHuman: true,
|
|
251
|
+
loading: false
|
|
252
|
+
});
|
|
253
|
+
events.onHuman(
|
|
254
|
+
encodeProcaptchaOutput({
|
|
255
|
+
[ApiParams.providerUrl]: providerUrl,
|
|
256
|
+
[ApiParams.user]: getAccount().account.account.address,
|
|
257
|
+
[ApiParams.dapp]: getDappAccount(),
|
|
258
|
+
[ApiParams.challenge]: challenge.challenge,
|
|
259
|
+
[ApiParams.timestamp]: challenge.timestamp,
|
|
260
|
+
[ApiParams.signature]: {
|
|
261
|
+
[ApiParams.provider]: challenge.signature.provider,
|
|
262
|
+
[ApiParams.user]: {
|
|
263
|
+
[ApiParams.timestamp]: userTimestampSignature.signature.toString()
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
})
|
|
267
|
+
);
|
|
268
|
+
setValidChallengeTimeout();
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
onFailed();
|
|
272
|
+
return false;
|
|
273
|
+
} catch (error) {
|
|
274
|
+
updateState({ loading: false });
|
|
275
|
+
throw error;
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
return {
|
|
279
|
+
start,
|
|
280
|
+
submitSolution,
|
|
281
|
+
resetState
|
|
282
|
+
};
|
|
283
|
+
};
|
|
284
|
+
export {
|
|
285
|
+
Manager
|
|
268
286
|
};
|
|
269
|
-
//# sourceMappingURL=Manager.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prosopo/procaptcha-puzzle",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.10",
|
|
4
4
|
"author": "PROSOPO LIMITED <info@prosopo.io>",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -30,11 +30,11 @@
|
|
|
30
30
|
"browserslist": ["> 0.5%, last 2 versions, not dead"],
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@polkadot/util": "13.5.7",
|
|
33
|
-
"@prosopo/api": "3.4.
|
|
33
|
+
"@prosopo/api": "3.4.10",
|
|
34
34
|
"@prosopo/common": "3.1.38",
|
|
35
35
|
"@prosopo/locale": "3.2.4",
|
|
36
|
-
"@prosopo/procaptcha-common": "2.10.
|
|
37
|
-
"@prosopo/types": "4.
|
|
36
|
+
"@prosopo/procaptcha-common": "2.10.19",
|
|
37
|
+
"@prosopo/types": "4.4.0",
|
|
38
38
|
"@prosopo/util": "3.2.15",
|
|
39
39
|
"@prosopo/util-crypto": "13.5.29",
|
|
40
40
|
"@prosopo/widget-skeleton": "2.8.3",
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import type { ProcaptchaProps } from "@prosopo/types";
|
|
16
|
+
import { type LazyExoticComponent, Suspense, lazy } from "react";
|
|
17
|
+
import type { ReactElement } from "react";
|
|
18
|
+
|
|
19
|
+
// Define the function signature more precisely
|
|
20
|
+
type ProcaptchaWidgetComponent = (
|
|
21
|
+
props: ProcaptchaProps,
|
|
22
|
+
) => ReactElement | null;
|
|
23
|
+
|
|
24
|
+
// Lazy load the widget with the correct type signature
|
|
25
|
+
const ProcaptchaWidget: LazyExoticComponent<ProcaptchaWidgetComponent> = lazy(
|
|
26
|
+
async () => import("./ProcaptchaWidget.js"),
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
export const ProcaptchaPuzzle = (props: ProcaptchaProps) => (
|
|
30
|
+
<Suspense>
|
|
31
|
+
<ProcaptchaWidget
|
|
32
|
+
config={props.config}
|
|
33
|
+
callbacks={props.callbacks}
|
|
34
|
+
frictionlessState={props.frictionlessState}
|
|
35
|
+
i18n={props.i18n}
|
|
36
|
+
/>
|
|
37
|
+
</Suspense>
|
|
38
|
+
);
|