@prosopo/procaptcha 2.0.3 → 2.1.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/modules/Manager.cjs +163 -171
- package/dist/cjs/modules/ProsopoCaptchaApi.cjs +4 -0
- package/dist/modules/Manager.d.ts.map +1 -1
- package/dist/modules/Manager.js +302 -274
- package/dist/modules/Manager.js.map +1 -1
- package/dist/modules/ProsopoCaptchaApi.d.ts +2 -2
- package/dist/modules/ProsopoCaptchaApi.d.ts.map +1 -1
- package/dist/modules/ProsopoCaptchaApi.js +4 -0
- package/dist/modules/ProsopoCaptchaApi.js.map +1 -1
- package/dist/modules/collector.js.map +1 -1
- package/package.json +83 -77
- package/dist/cjs/account/dist/extension/Extension.cjs +0 -5
- package/dist/cjs/account/dist/extension/ExtensionWeb2.cjs +0 -78
- package/dist/cjs/account/dist/extension/ExtensionWeb3.cjs +0 -30
- package/dist/cjs/account/dist/index.cjs +0 -8
- package/dist/cjs/detector/src/index.cjs +0 -4586
|
@@ -2,19 +2,16 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const utilCrypto = require("@polkadot/util-crypto");
|
|
4
4
|
const random = require("@polkadot/util-crypto/random");
|
|
5
|
-
require("
|
|
5
|
+
const string = require("@polkadot/util/string");
|
|
6
|
+
const account = require("@prosopo/account");
|
|
6
7
|
const api = require("@prosopo/api");
|
|
7
8
|
const common = require("@prosopo/common");
|
|
8
|
-
const loadBalancer = require("@prosopo/load-balancer");
|
|
9
9
|
const procaptchaCommon = require("@prosopo/procaptcha-common");
|
|
10
10
|
const types = require("@prosopo/types");
|
|
11
11
|
const util = require("@prosopo/util");
|
|
12
12
|
const utils = require("../utils/utils.cjs");
|
|
13
13
|
const ProsopoCaptchaApi = require("./ProsopoCaptchaApi.cjs");
|
|
14
14
|
const storage = require("./storage.cjs");
|
|
15
|
-
const string = require("@polkadot/util/string");
|
|
16
|
-
const ExtensionWeb2 = require("../account/dist/extension/ExtensionWeb2.cjs");
|
|
17
|
-
const ExtensionWeb3 = require("../account/dist/extension/ExtensionWeb3.cjs");
|
|
18
15
|
const defaultState = () => {
|
|
19
16
|
return {
|
|
20
17
|
// note order matters! see buildUpdateState. These fields are set in order, so disable modal first, then set loading to false, etc.
|
|
@@ -29,28 +26,8 @@ const defaultState = () => {
|
|
|
29
26
|
// don't handle timeout here, this should be handled by the state management
|
|
30
27
|
};
|
|
31
28
|
};
|
|
32
|
-
const getRandomActiveProvider = (config) => {
|
|
33
|
-
const randomIntBetween = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
|
|
34
|
-
const PROVIDERS = loadBalancer.loadBalancer(config.defaultEnvironment);
|
|
35
|
-
const randomProvderObj = util.at(
|
|
36
|
-
PROVIDERS,
|
|
37
|
-
randomIntBetween(0, PROVIDERS.length - 1)
|
|
38
|
-
);
|
|
39
|
-
return {
|
|
40
|
-
providerAccount: randomProvderObj.address,
|
|
41
|
-
provider: {
|
|
42
|
-
url: randomProvderObj.url,
|
|
43
|
-
datasetId: randomProvderObj.datasetId,
|
|
44
|
-
datasetIdContent: randomProvderObj.datasetIdContent
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
29
|
function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
49
30
|
const events = procaptchaCommon.getDefaultEvents(onStateUpdate, state, callbacks);
|
|
50
|
-
const dispatchErrorEvent = (err) => {
|
|
51
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
52
|
-
events.onError(error);
|
|
53
|
-
};
|
|
54
31
|
const updateState = procaptchaCommon.buildUpdateState(state, onStateUpdate);
|
|
55
32
|
const getConfig = () => {
|
|
56
33
|
const config = {
|
|
@@ -62,152 +39,167 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
62
39
|
}
|
|
63
40
|
return types.ProcaptchaConfigSchema.parse(config);
|
|
64
41
|
};
|
|
65
|
-
const fallable = async (fn) => {
|
|
66
|
-
try {
|
|
67
|
-
await fn();
|
|
68
|
-
} catch (err) {
|
|
69
|
-
console.error(err);
|
|
70
|
-
dispatchErrorEvent(err);
|
|
71
|
-
updateState({ isHuman: false, showModal: false, loading: false });
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
42
|
const start = async () => {
|
|
75
43
|
events.onOpen();
|
|
76
|
-
await
|
|
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
|
-
|
|
44
|
+
await procaptchaCommon.providerRetry(
|
|
45
|
+
async () => {
|
|
46
|
+
if (state.loading) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (state.isHuman) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
await utilCrypto.cryptoWaitReady();
|
|
53
|
+
resetState();
|
|
54
|
+
updateState({ loading: true });
|
|
55
|
+
updateState({
|
|
56
|
+
attemptCount: state.attemptCount ? state.attemptCount + 1 : 1
|
|
57
|
+
});
|
|
58
|
+
const config = getConfig();
|
|
59
|
+
updateState({ dappAccount: config.account.address });
|
|
60
|
+
await utils.sleep(100);
|
|
61
|
+
const account2 = await loadAccount();
|
|
62
|
+
const getRandomProviderResponse = await procaptchaCommon.getRandomActiveProvider(
|
|
63
|
+
getConfig()
|
|
64
|
+
);
|
|
65
|
+
const providerUrl = getRandomProviderResponse.provider.url;
|
|
66
|
+
const providerApi = await loadProviderApi(providerUrl);
|
|
67
|
+
const captchaApi = new ProsopoCaptchaApi.ProsopoCaptchaApi(
|
|
68
|
+
account2.account.address,
|
|
69
|
+
getRandomProviderResponse,
|
|
70
|
+
providerApi,
|
|
71
|
+
config.web2,
|
|
72
|
+
config.account.address || ""
|
|
73
|
+
);
|
|
74
|
+
updateState({ captchaApi });
|
|
75
|
+
const challenge = await captchaApi.getCaptchaChallenge();
|
|
76
|
+
if (challenge.error) {
|
|
77
|
+
updateState({
|
|
78
|
+
loading: false,
|
|
79
|
+
error: challenge.error
|
|
80
|
+
});
|
|
81
|
+
} else {
|
|
82
|
+
if (challenge.captchas.length <= 0) {
|
|
83
|
+
throw new common.ProsopoDatasetError("DEVELOPER.PROVIDER_NO_CAPTCHA");
|
|
84
|
+
}
|
|
85
|
+
const timeMillis = challenge.captchas.map(
|
|
86
|
+
(captcha) => captcha.timeLimitMs || config.captchas.image.challengeTimeout
|
|
87
|
+
).reduce((a, b) => a + b);
|
|
88
|
+
const timeout = setTimeout(() => {
|
|
89
|
+
events.onChallengeExpired();
|
|
90
|
+
updateState({ isHuman: false, showModal: false, loading: false });
|
|
91
|
+
}, timeMillis);
|
|
92
|
+
updateState({
|
|
93
|
+
index: 0,
|
|
94
|
+
solutions: challenge.captchas.map(() => []),
|
|
95
|
+
challenge,
|
|
96
|
+
showModal: true,
|
|
97
|
+
timeout
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
start,
|
|
102
|
+
resetState,
|
|
103
|
+
state.attemptCount,
|
|
104
|
+
10
|
|
105
|
+
);
|
|
120
106
|
};
|
|
121
107
|
const submit = async () => {
|
|
122
|
-
await
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
updateState({ showModal: false });
|
|
130
|
-
const challenge = state.challenge;
|
|
131
|
-
const salt = random.randomAsHex();
|
|
132
|
-
const captchaSolution = state.challenge.captchas.map(
|
|
133
|
-
(captcha, index) => {
|
|
134
|
-
const solution = util.at(state.solutions, index);
|
|
135
|
-
return {
|
|
136
|
-
captchaId: captcha.captchaId,
|
|
137
|
-
captchaContentId: captcha.captchaContentId,
|
|
138
|
-
salt,
|
|
139
|
-
solution
|
|
140
|
-
};
|
|
108
|
+
await procaptchaCommon.providerRetry(
|
|
109
|
+
async () => {
|
|
110
|
+
clearTimeout();
|
|
111
|
+
if (!state.challenge) {
|
|
112
|
+
throw new common.ProsopoError("CAPTCHA.NO_CAPTCHA", {
|
|
113
|
+
context: { error: "Cannot submit, no Captcha found in state" }
|
|
114
|
+
});
|
|
141
115
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
context: { error: "No Captcha API found in state" }
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
if (!signer || !signer.signRaw) {
|
|
158
|
-
throw new common.ProsopoEnvError("GENERAL.CANT_FIND_KEYRINGPAIR", {
|
|
159
|
-
context: {
|
|
160
|
-
error: "Signer is not defined, cannot sign message to prove account ownership"
|
|
116
|
+
updateState({ showModal: false });
|
|
117
|
+
const challenge = state.challenge;
|
|
118
|
+
const salt = random.randomAsHex();
|
|
119
|
+
const captchaSolution = state.challenge.captchas.map(
|
|
120
|
+
(captcha, index) => {
|
|
121
|
+
const solution = util.at(state.solutions, index);
|
|
122
|
+
return {
|
|
123
|
+
captchaId: captcha.captchaId,
|
|
124
|
+
captchaContentId: captcha.captchaContentId,
|
|
125
|
+
salt,
|
|
126
|
+
solution
|
|
127
|
+
};
|
|
161
128
|
}
|
|
129
|
+
);
|
|
130
|
+
const account2 = getAccount();
|
|
131
|
+
const signer = getExtension(account2).signer;
|
|
132
|
+
const first = util.at(challenge.captchas, 0);
|
|
133
|
+
if (!first.datasetId) {
|
|
134
|
+
throw new common.ProsopoDatasetError("CAPTCHA.INVALID_CAPTCHA_ID", {
|
|
135
|
+
context: { error: "No datasetId set for challenge" }
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
const captchaApi = state.captchaApi;
|
|
139
|
+
if (!captchaApi) {
|
|
140
|
+
throw new common.ProsopoError("CAPTCHA.INVALID_TOKEN", {
|
|
141
|
+
context: { error: "No Captcha API found in state" }
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
if (!signer || !signer.signRaw) {
|
|
145
|
+
throw new common.ProsopoEnvError("GENERAL.CANT_FIND_KEYRINGPAIR", {
|
|
146
|
+
context: {
|
|
147
|
+
error: "Signer is not defined, cannot sign message to prove account ownership"
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
const userRequestHashSignature = await signer.signRaw({
|
|
152
|
+
address: account2.account.address,
|
|
153
|
+
data: string.stringToHex(challenge.requestHash),
|
|
154
|
+
type: "bytes"
|
|
162
155
|
});
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
events.onFailed();
|
|
179
|
-
}
|
|
180
|
-
updateState({
|
|
181
|
-
submission,
|
|
182
|
-
isHuman,
|
|
183
|
-
loading: false
|
|
184
|
-
});
|
|
185
|
-
if (state.isHuman) {
|
|
186
|
-
const providerUrl = captchaApi.provider.provider.url;
|
|
187
|
-
storage.setProcaptchaStorage({
|
|
188
|
-
...storage.getProcaptchaStorage(),
|
|
189
|
-
providerUrl
|
|
156
|
+
const submission = await captchaApi.submitCaptchaSolution(
|
|
157
|
+
userRequestHashSignature.signature,
|
|
158
|
+
challenge.requestHash,
|
|
159
|
+
captchaSolution,
|
|
160
|
+
challenge.timestamp,
|
|
161
|
+
challenge.signature.provider.requestHash
|
|
162
|
+
);
|
|
163
|
+
const isHuman = submission[0].verified;
|
|
164
|
+
if (!isHuman) {
|
|
165
|
+
events.onFailed();
|
|
166
|
+
}
|
|
167
|
+
updateState({
|
|
168
|
+
submission,
|
|
169
|
+
isHuman,
|
|
170
|
+
loading: false
|
|
190
171
|
});
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
[types.ApiParams.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
[types.ApiParams.
|
|
203
|
-
|
|
172
|
+
if (state.isHuman) {
|
|
173
|
+
const providerUrl = captchaApi.provider.provider.url;
|
|
174
|
+
storage.setProcaptchaStorage({
|
|
175
|
+
...storage.getProcaptchaStorage(),
|
|
176
|
+
providerUrl
|
|
177
|
+
});
|
|
178
|
+
events.onHuman(
|
|
179
|
+
types.encodeProcaptchaOutput({
|
|
180
|
+
[types.ApiParams.providerUrl]: providerUrl,
|
|
181
|
+
[types.ApiParams.user]: account2.account.address,
|
|
182
|
+
[types.ApiParams.dapp]: getDappAccount(),
|
|
183
|
+
[types.ApiParams.commitmentId]: util.hashToHex(submission[1]),
|
|
184
|
+
[types.ApiParams.timestamp]: challenge.timestamp,
|
|
185
|
+
[types.ApiParams.signature]: {
|
|
186
|
+
[types.ApiParams.provider]: {
|
|
187
|
+
[types.ApiParams.requestHash]: challenge.signature.provider.requestHash
|
|
188
|
+
},
|
|
189
|
+
[types.ApiParams.user]: {
|
|
190
|
+
[types.ApiParams.requestHash]: userRequestHashSignature.signature
|
|
191
|
+
}
|
|
204
192
|
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
|
|
193
|
+
})
|
|
194
|
+
);
|
|
195
|
+
setValidChallengeTimeout();
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
start,
|
|
199
|
+
resetState,
|
|
200
|
+
state.attemptCount,
|
|
201
|
+
10
|
|
202
|
+
);
|
|
211
203
|
};
|
|
212
204
|
const cancel = async () => {
|
|
213
205
|
clearTimeout();
|
|
@@ -282,10 +274,10 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
282
274
|
context: { error: "Account address has not been set for web3 mode" }
|
|
283
275
|
});
|
|
284
276
|
}
|
|
285
|
-
const ext = config.web2 ? new
|
|
286
|
-
const account = await ext.getAccount(config);
|
|
287
|
-
storage.setAccount(account.account.address);
|
|
288
|
-
updateState({ account });
|
|
277
|
+
const ext = config.web2 ? new account.ExtensionWeb2() : new account.ExtensionWeb3();
|
|
278
|
+
const account$1 = await ext.getAccount(config);
|
|
279
|
+
storage.setAccount(account$1.account.address);
|
|
280
|
+
updateState({ account: account$1 });
|
|
289
281
|
return getAccount();
|
|
290
282
|
};
|
|
291
283
|
const getAccount = () => {
|
|
@@ -294,8 +286,8 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
294
286
|
context: { error: "Account not loaded" }
|
|
295
287
|
});
|
|
296
288
|
}
|
|
297
|
-
const
|
|
298
|
-
return
|
|
289
|
+
const account2 = state.account;
|
|
290
|
+
return account2;
|
|
299
291
|
};
|
|
300
292
|
const getDappAccount = () => {
|
|
301
293
|
if (!state.dappAccount) {
|
|
@@ -305,13 +297,13 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
305
297
|
return dappAccount;
|
|
306
298
|
};
|
|
307
299
|
const getExtension = (possiblyAccount) => {
|
|
308
|
-
const
|
|
309
|
-
if (!
|
|
300
|
+
const account2 = possiblyAccount || getAccount();
|
|
301
|
+
if (!account2.extension) {
|
|
310
302
|
throw new common.ProsopoEnvError("ACCOUNT.NO_POLKADOT_EXTENSION", {
|
|
311
303
|
context: { error: "Extension not loaded" }
|
|
312
304
|
});
|
|
313
305
|
}
|
|
314
|
-
return
|
|
306
|
+
return account2.extension;
|
|
315
307
|
};
|
|
316
308
|
return {
|
|
317
309
|
start,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
|
|
3
3
|
const common = require("@prosopo/common");
|
|
4
4
|
const datasets = require("@prosopo/datasets");
|
|
5
|
+
const types = require("@prosopo/types");
|
|
5
6
|
class ProsopoCaptchaApi {
|
|
6
7
|
constructor(userAccount, provider, providerApi, web2, dappAccount) {
|
|
7
8
|
this.userAccount = userAccount;
|
|
@@ -19,6 +20,9 @@ class ProsopoCaptchaApi {
|
|
|
19
20
|
this.userAccount,
|
|
20
21
|
this.provider
|
|
21
22
|
);
|
|
23
|
+
if (captchaChallenge[types.ApiParams.error]) {
|
|
24
|
+
return captchaChallenge;
|
|
25
|
+
}
|
|
22
26
|
for (const captcha of captchaChallenge.captchas) {
|
|
23
27
|
for (const item of captcha.items) {
|
|
24
28
|
if (item.data) {
|
|
@@ -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":"AA8BA,OAAO,EAKN,KAAK,mBAAmB,EAExB,KAAK,4BAA4B,EAEjC,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAG5B,MAAM,gBAAgB,CAAC;AAwBxB,wBAAgB,OAAO,CACtB,cAAc,EAAE,4BAA4B,EAC5C,KAAK,EAAE,eAAe,EACtB,aAAa,EAAE,uBAAuB,EACtC,SAAS,EAAE,mBAAmB;;;;mBA8PR,MAAM;;EA2I5B"}
|