@prosopo/procaptcha-pow 2.5.5 → 2.6.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/CHANGELOG.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# @prosopo/procaptcha-pow
|
|
2
|
+
|
|
3
|
+
## 2.6.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [52feffc]
|
|
8
|
+
- @prosopo/types@2.6.1
|
|
9
|
+
- @prosopo/procaptcha@2.6.1
|
|
10
|
+
- @prosopo/account@2.6.1
|
|
11
|
+
- @prosopo/api@2.6.1
|
|
12
|
+
- @prosopo/procaptcha-common@2.6.1
|
|
13
|
+
|
|
14
|
+
## 2.6.0
|
|
15
|
+
|
|
16
|
+
### Minor Changes
|
|
17
|
+
|
|
18
|
+
- a0bfc8a: bump all pkg versions since independent versioning applied
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [a0bfc8a]
|
|
23
|
+
- @prosopo/account@2.6.0
|
|
24
|
+
- @prosopo/api@2.6.0
|
|
25
|
+
- @prosopo/common@2.6.0
|
|
26
|
+
- @prosopo/locale@2.6.0
|
|
27
|
+
- @prosopo/procaptcha@2.6.0
|
|
28
|
+
- @prosopo/procaptcha-common@2.6.0
|
|
29
|
+
- @prosopo/types@2.6.0
|
|
30
|
+
- @prosopo/util@2.6.0
|
|
31
|
+
- @prosopo/widget-skeleton@2.6.0
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("@emotion/react/jsx-runtime");
|
|
4
|
+
const react = require("react");
|
|
5
|
+
const _interopNamespaceDefaultOnly = (e) => Object.freeze(Object.defineProperty({ __proto__: null, default: e }, Symbol.toStringTag, { value: "Module" }));
|
|
6
|
+
const ProcaptchaWidget = react.lazy(
|
|
7
|
+
async () => Promise.resolve().then(() => /* @__PURE__ */ _interopNamespaceDefaultOnly(require("./ProcaptchaWidget.cjs")))
|
|
8
|
+
);
|
|
9
|
+
const ProcaptchaPow = (props) => /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
10
|
+
ProcaptchaWidget,
|
|
11
|
+
{
|
|
12
|
+
config: props.config,
|
|
13
|
+
callbacks: props.callbacks,
|
|
14
|
+
frictionlessState: props.frictionlessState,
|
|
15
|
+
i18n: props.i18n
|
|
16
|
+
}
|
|
17
|
+
) });
|
|
18
|
+
exports.ProcaptchaPow = ProcaptchaPow;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const jsxRuntime = require("@emotion/react/jsx-runtime");
|
|
3
|
+
const locale = require("@prosopo/locale");
|
|
4
|
+
const procaptchaCommon = require("@prosopo/procaptcha-common");
|
|
5
|
+
const types = require("@prosopo/types");
|
|
6
|
+
const widgetSkeleton = require("@prosopo/widget-skeleton");
|
|
7
|
+
const react = require("react");
|
|
8
|
+
const Manager = require("../services/Manager.cjs");
|
|
9
|
+
const PROCAPTCHA_EXECUTE_EVENT = "procaptcha:execute";
|
|
10
|
+
const Procaptcha = (props) => {
|
|
11
|
+
const { t } = locale.useTranslation();
|
|
12
|
+
const config = props.config;
|
|
13
|
+
const i18n = props.i18n;
|
|
14
|
+
const theme = "light" === config.theme ? widgetSkeleton.lightTheme : widgetSkeleton.darkTheme;
|
|
15
|
+
const frictionlessState = props.frictionlessState;
|
|
16
|
+
const callbacks = props.callbacks || {};
|
|
17
|
+
const [state, _updateState] = procaptchaCommon.useProcaptcha(react.useState, react.useRef);
|
|
18
|
+
const [loading, setLoading] = react.useState(false);
|
|
19
|
+
const updateState = procaptchaCommon.buildUpdateState(state, _updateState);
|
|
20
|
+
const manager = react.useRef(
|
|
21
|
+
Manager.Manager(config, state, updateState, callbacks, frictionlessState)
|
|
22
|
+
);
|
|
23
|
+
react.useEffect(() => {
|
|
24
|
+
if (config.language) {
|
|
25
|
+
if (i18n) {
|
|
26
|
+
if (i18n.language !== config.language) {
|
|
27
|
+
i18n.changeLanguage(config.language).then((r) => r);
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
locale.loadI18next(false).then((i18n2) => {
|
|
31
|
+
if (i18n2.language !== config.language)
|
|
32
|
+
i18n2.changeLanguage(config.language).then((r) => r);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}, [i18n, config.language]);
|
|
37
|
+
react.useEffect(() => {
|
|
38
|
+
if (state.error) {
|
|
39
|
+
setLoading(false);
|
|
40
|
+
if (state.error.key === "CAPTCHA.NO_SESSION_FOUND" && frictionlessState) {
|
|
41
|
+
setTimeout(() => {
|
|
42
|
+
frictionlessState.restart();
|
|
43
|
+
}, 3e3);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}, [state.error, frictionlessState]);
|
|
47
|
+
react.useEffect(() => {
|
|
48
|
+
if (config.mode === types.ModeEnum.invisible) {
|
|
49
|
+
const handleExecuteEvent = (event) => {
|
|
50
|
+
try {
|
|
51
|
+
manager.current.start();
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error("Error starting PoW verification:", error);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
document.addEventListener(PROCAPTCHA_EXECUTE_EVENT, handleExecuteEvent);
|
|
57
|
+
return () => {
|
|
58
|
+
document.removeEventListener(
|
|
59
|
+
PROCAPTCHA_EXECUTE_EVENT,
|
|
60
|
+
handleExecuteEvent
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return () => {
|
|
65
|
+
};
|
|
66
|
+
}, [config.mode]);
|
|
67
|
+
if (config.mode === types.ModeEnum.invisible) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
71
|
+
procaptchaCommon.Checkbox,
|
|
72
|
+
{
|
|
73
|
+
checked: state.isHuman,
|
|
74
|
+
onChange: async () => {
|
|
75
|
+
if (loading) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
setLoading(true);
|
|
79
|
+
await manager.current.start();
|
|
80
|
+
setLoading(false);
|
|
81
|
+
},
|
|
82
|
+
theme,
|
|
83
|
+
labelText: t("WIDGET.I_AM_HUMAN"),
|
|
84
|
+
error: state.error?.message,
|
|
85
|
+
"aria-label": "human checkbox",
|
|
86
|
+
loading
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
module.exports = Procaptcha;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const string = require("@polkadot/util/string");
|
|
4
|
+
const api = require("@prosopo/api");
|
|
5
|
+
const common = require("@prosopo/common");
|
|
6
|
+
const procaptchaCommon = require("@prosopo/procaptcha-common");
|
|
7
|
+
const types = require("@prosopo/types");
|
|
8
|
+
const util = require("@prosopo/util");
|
|
9
|
+
const Manager = (configInput, state, onStateUpdate, callbacks, frictionlessState) => {
|
|
10
|
+
const events = procaptchaCommon.getDefaultEvents(callbacks);
|
|
11
|
+
const defaultState = () => {
|
|
12
|
+
return {
|
|
13
|
+
// note order matters! see buildUpdateState. These fields are set in order, so disable modal first, then set loading to false, etc.
|
|
14
|
+
showModal: false,
|
|
15
|
+
loading: false,
|
|
16
|
+
index: 0,
|
|
17
|
+
challenge: void 0,
|
|
18
|
+
solutions: void 0,
|
|
19
|
+
isHuman: false,
|
|
20
|
+
captchaApi: void 0,
|
|
21
|
+
account: void 0
|
|
22
|
+
// don't handle timeout here, this should be handled by the state management
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
const clearTimeout = () => {
|
|
26
|
+
window.clearTimeout(Number(state.timeout));
|
|
27
|
+
updateState({ timeout: void 0 });
|
|
28
|
+
};
|
|
29
|
+
const onFailed = () => {
|
|
30
|
+
updateState({
|
|
31
|
+
isHuman: false,
|
|
32
|
+
loading: false
|
|
33
|
+
});
|
|
34
|
+
events.onFailed();
|
|
35
|
+
resetState(frictionlessState?.restart);
|
|
36
|
+
};
|
|
37
|
+
const clearSuccessfulChallengeTimeout = () => {
|
|
38
|
+
window.clearTimeout(Number(state.successfullChallengeTimeout));
|
|
39
|
+
updateState({ successfullChallengeTimeout: void 0 });
|
|
40
|
+
};
|
|
41
|
+
const getConfig = () => {
|
|
42
|
+
const config = {
|
|
43
|
+
userAccountAddress: "",
|
|
44
|
+
...configInput
|
|
45
|
+
};
|
|
46
|
+
if (state.account) {
|
|
47
|
+
config.userAccountAddress = state.account.account.address;
|
|
48
|
+
}
|
|
49
|
+
return types.ProcaptchaConfigSchema.parse(config);
|
|
50
|
+
};
|
|
51
|
+
const getAccount = () => {
|
|
52
|
+
if (!state.account) {
|
|
53
|
+
throw new common.ProsopoEnvError("GENERAL.ACCOUNT_NOT_FOUND", {
|
|
54
|
+
context: { error: "Account not loaded" }
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
const account = state.account;
|
|
58
|
+
return { account };
|
|
59
|
+
};
|
|
60
|
+
const getDappAccount = () => {
|
|
61
|
+
if (!state.dappAccount) {
|
|
62
|
+
throw new common.ProsopoEnvError("GENERAL.SITE_KEY_MISSING");
|
|
63
|
+
}
|
|
64
|
+
const dappAccount = state.dappAccount;
|
|
65
|
+
return dappAccount;
|
|
66
|
+
};
|
|
67
|
+
const updateState = procaptchaCommon.buildUpdateState(state, onStateUpdate);
|
|
68
|
+
const resetState = (frictionlessRestart) => {
|
|
69
|
+
clearTimeout();
|
|
70
|
+
clearSuccessfulChallengeTimeout();
|
|
71
|
+
updateState(defaultState());
|
|
72
|
+
events.onReset();
|
|
73
|
+
if (frictionlessRestart) {
|
|
74
|
+
frictionlessRestart();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const setValidChallengeTimeout = () => {
|
|
78
|
+
const timeMillis = getConfig().captchas.pow.solutionTimeout;
|
|
79
|
+
const successfullChallengeTimeout = setTimeout(() => {
|
|
80
|
+
updateState({ isHuman: false });
|
|
81
|
+
events.onExpired();
|
|
82
|
+
resetState(frictionlessState?.restart);
|
|
83
|
+
}, timeMillis);
|
|
84
|
+
updateState({ successfullChallengeTimeout });
|
|
85
|
+
};
|
|
86
|
+
const start = async () => {
|
|
87
|
+
await procaptchaCommon.providerRetry(
|
|
88
|
+
async () => {
|
|
89
|
+
if (state.loading) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (state.isHuman) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
resetState();
|
|
96
|
+
updateState({
|
|
97
|
+
loading: true
|
|
98
|
+
});
|
|
99
|
+
updateState({ attemptCount: state.attemptCount + 1 });
|
|
100
|
+
const config = getConfig();
|
|
101
|
+
const selectAccount = async () => {
|
|
102
|
+
if (frictionlessState) {
|
|
103
|
+
return frictionlessState.userAccount;
|
|
104
|
+
}
|
|
105
|
+
const ext = new (await procaptchaCommon.ExtensionLoader(config.web2))();
|
|
106
|
+
return ext.getAccount(config);
|
|
107
|
+
};
|
|
108
|
+
const user = await selectAccount();
|
|
109
|
+
const userAccount = user.account.address;
|
|
110
|
+
updateState({
|
|
111
|
+
account: { account: { address: userAccount } }
|
|
112
|
+
});
|
|
113
|
+
updateState({ dappAccount: config.account.address });
|
|
114
|
+
await util.sleep(100);
|
|
115
|
+
if (!config.web2 && !config.userAccountAddress) {
|
|
116
|
+
throw new common.ProsopoEnvError("GENERAL.ACCOUNT_NOT_FOUND", {
|
|
117
|
+
context: {
|
|
118
|
+
error: "Account address has not been set for web3 mode"
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
let getRandomProviderResponse = void 0;
|
|
123
|
+
if (frictionlessState?.provider) {
|
|
124
|
+
getRandomProviderResponse = frictionlessState.provider;
|
|
125
|
+
} else {
|
|
126
|
+
getRandomProviderResponse = await procaptchaCommon.getRandomActiveProvider(
|
|
127
|
+
getConfig()
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
const providerUrl = getRandomProviderResponse.provider.url;
|
|
131
|
+
const providerApi = new api.ProviderApi(providerUrl, getDappAccount());
|
|
132
|
+
const challenge = await providerApi.getPowCaptchaChallenge(
|
|
133
|
+
userAccount,
|
|
134
|
+
getDappAccount(),
|
|
135
|
+
frictionlessState?.sessionId
|
|
136
|
+
);
|
|
137
|
+
if (challenge.error) {
|
|
138
|
+
updateState({
|
|
139
|
+
loading: false,
|
|
140
|
+
error: {
|
|
141
|
+
message: challenge.error.message,
|
|
142
|
+
key: challenge.error.key || "API.UNKNOWN_ERROR"
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
} else {
|
|
146
|
+
const solution = util.solvePoW(challenge.challenge, challenge.difficulty);
|
|
147
|
+
const signer = user.extension?.signer;
|
|
148
|
+
if (!signer || !signer.signRaw) {
|
|
149
|
+
throw new common.ProsopoEnvError("GENERAL.CANT_FIND_KEYRINGPAIR", {
|
|
150
|
+
context: {
|
|
151
|
+
error: "Signer is not defined, cannot sign message to prove account ownership"
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
const userTimestampSignature = await signer.signRaw({
|
|
156
|
+
address: userAccount,
|
|
157
|
+
data: string.stringToHex(challenge[types.ApiParams.timestamp].toString()),
|
|
158
|
+
type: "bytes"
|
|
159
|
+
});
|
|
160
|
+
const verifiedSolution = await providerApi.submitPowCaptchaSolution(
|
|
161
|
+
challenge,
|
|
162
|
+
getAccount().account.account.address,
|
|
163
|
+
getDappAccount(),
|
|
164
|
+
solution,
|
|
165
|
+
userTimestampSignature.signature.toString(),
|
|
166
|
+
config.captchas.pow.verifiedTimeout
|
|
167
|
+
);
|
|
168
|
+
if (verifiedSolution[types.ApiParams.verified]) {
|
|
169
|
+
updateState({
|
|
170
|
+
isHuman: true,
|
|
171
|
+
loading: false
|
|
172
|
+
});
|
|
173
|
+
events.onHuman(
|
|
174
|
+
types.encodeProcaptchaOutput({
|
|
175
|
+
[types.ApiParams.providerUrl]: providerUrl,
|
|
176
|
+
[types.ApiParams.user]: getAccount().account.account.address,
|
|
177
|
+
[types.ApiParams.dapp]: getDappAccount(),
|
|
178
|
+
[types.ApiParams.challenge]: challenge.challenge,
|
|
179
|
+
[types.ApiParams.nonce]: solution,
|
|
180
|
+
[types.ApiParams.timestamp]: challenge.timestamp,
|
|
181
|
+
[types.ApiParams.signature]: {
|
|
182
|
+
[types.ApiParams.provider]: challenge.signature.provider,
|
|
183
|
+
[types.ApiParams.user]: {
|
|
184
|
+
[types.ApiParams.timestamp]: userTimestampSignature.signature.toString()
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
);
|
|
189
|
+
setValidChallengeTimeout();
|
|
190
|
+
} else {
|
|
191
|
+
onFailed();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
start,
|
|
196
|
+
() => {
|
|
197
|
+
resetState();
|
|
198
|
+
},
|
|
199
|
+
state.attemptCount,
|
|
200
|
+
3
|
|
201
|
+
);
|
|
202
|
+
};
|
|
203
|
+
return {
|
|
204
|
+
start,
|
|
205
|
+
resetState
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
exports.Manager = Manager;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prosopo/procaptcha-pow",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.1",
|
|
4
4
|
"author": "PROSOPO LIMITED <info@prosopo.io>",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -24,18 +24,20 @@
|
|
|
24
24
|
"build": "tsc --build --verbose",
|
|
25
25
|
"build:cjs": "npx vite --config vite.cjs.config.ts build"
|
|
26
26
|
},
|
|
27
|
-
"browserslist": [
|
|
27
|
+
"browserslist": [
|
|
28
|
+
"> 0.5%, last 2 versions, not dead"
|
|
29
|
+
],
|
|
28
30
|
"dependencies": {
|
|
29
31
|
"@polkadot/util": "12.6.2",
|
|
30
|
-
"@prosopo/account": "2.
|
|
31
|
-
"@prosopo/api": "2.
|
|
32
|
-
"@prosopo/common": "2.
|
|
33
|
-
"@prosopo/locale": "2.
|
|
34
|
-
"@prosopo/procaptcha": "2.
|
|
35
|
-
"@prosopo/procaptcha-common": "2.
|
|
36
|
-
"@prosopo/types": "2.
|
|
37
|
-
"@prosopo/util": "2.
|
|
38
|
-
"@prosopo/widget-skeleton": "2.
|
|
32
|
+
"@prosopo/account": "2.6.1",
|
|
33
|
+
"@prosopo/api": "2.6.1",
|
|
34
|
+
"@prosopo/common": "2.6.0",
|
|
35
|
+
"@prosopo/locale": "2.6.0",
|
|
36
|
+
"@prosopo/procaptcha": "2.6.1",
|
|
37
|
+
"@prosopo/procaptcha-common": "2.6.1",
|
|
38
|
+
"@prosopo/types": "2.6.1",
|
|
39
|
+
"@prosopo/util": "2.6.0",
|
|
40
|
+
"@prosopo/widget-skeleton": "2.6.0",
|
|
39
41
|
"express": "4.21.2",
|
|
40
42
|
"react": "18.3.1"
|
|
41
43
|
},
|
|
@@ -47,7 +49,7 @@
|
|
|
47
49
|
}
|
|
48
50
|
},
|
|
49
51
|
"devDependencies": {
|
|
50
|
-
"@prosopo/config": "2.
|
|
52
|
+
"@prosopo/config": "2.6.0",
|
|
51
53
|
"@vitest/coverage-v8": "3.0.9",
|
|
52
54
|
"concurrently": "9.0.1",
|
|
53
55
|
"del-cli": "6.0.0",
|
|
@@ -60,7 +62,8 @@
|
|
|
60
62
|
},
|
|
61
63
|
"repository": {
|
|
62
64
|
"type": "git",
|
|
63
|
-
"url": "git+https://github.com/prosopo/captcha.git"
|
|
65
|
+
"url": "git+https://github.com/prosopo/captcha.git",
|
|
66
|
+
"directory": "packages/procaptcha-pow"
|
|
64
67
|
},
|
|
65
68
|
"bugs": {
|
|
66
69
|
"url": "https://github.com/prosopo/captcha/issues"
|