@prosopo/provider 3.2.0 → 3.2.2

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.
Files changed (233) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/api/admin/apiAdminRoutesProvider.js +26 -21
  3. package/dist/api/admin/apiRegisterSiteKeyEndpoint.js +21 -20
  4. package/dist/api/admin/apiRemoveDetectorKeyEndpoint.js +27 -27
  5. package/dist/api/admin/apiUpdateDetectorKeyEndpoint.js +33 -33
  6. package/dist/api/admin/createApiAdminRoutesProvider.js +8 -5
  7. package/dist/api/blacklistRequestInspector.js +115 -98
  8. package/dist/api/block.js +13 -8
  9. package/dist/api/captcha.js +519 -356
  10. package/dist/api/domainMiddleware.js +75 -68
  11. package/dist/api/headerCheckMiddleware.js +26 -25
  12. package/dist/api/ignoreMiddleware.js +12 -10
  13. package/dist/api/ja4Middleware.js +74 -82
  14. package/dist/api/public.js +26 -23
  15. package/dist/api/robotsMiddleware.js +11 -9
  16. package/dist/api/validateAddress.js +20 -18
  17. package/dist/api/verify.js +133 -95
  18. package/dist/cjs/api/captcha.cjs +45 -36
  19. package/dist/cjs/tasks/captchaManager.cjs +24 -10
  20. package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +12 -12
  21. package/dist/cjs/tasks/powCaptcha/powTasksUtils.cjs +1 -1
  22. package/dist/index.js +38 -15
  23. package/dist/rules/lang.js +14 -14
  24. package/dist/schedulers/captchaScheduler.js +28 -23
  25. package/dist/schedulers/getClientList.js +29 -24
  26. package/dist/tasks/captchaManager.js +109 -85
  27. package/dist/tasks/client/clientTasks.js +265 -204
  28. package/dist/tasks/dataset/datasetTasks.js +27 -16
  29. package/dist/tasks/dataset/datasetTasksUtils.js +31 -31
  30. package/dist/tasks/detection/decodePayload.js +378 -208
  31. package/dist/tasks/detection/getBotScore.js +12 -10
  32. package/dist/tasks/frictionless/frictionlessTasks.js +120 -119
  33. package/dist/tasks/frictionless/frictionlessTasksUtils.js +10 -5
  34. package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +360 -271
  35. package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.js +23 -16
  36. package/dist/tasks/index.js +4 -2
  37. package/dist/tasks/powCaptcha/powTasks.js +157 -97
  38. package/dist/tasks/powCaptcha/powTasksUtils.js +24 -20
  39. package/dist/tasks/tasks.js +53 -29
  40. package/dist/util.js +98 -88
  41. package/package.json +31 -27
  42. package/vite.cjs.config.ts +4 -1
  43. package/vite.esm.config.ts +20 -0
  44. package/vite.test.config.ts +15 -3
  45. package/dist/api/admin/apiAdminRoutesProvider.d.ts +0 -9
  46. package/dist/api/admin/apiAdminRoutesProvider.d.ts.map +0 -1
  47. package/dist/api/admin/apiAdminRoutesProvider.js.map +0 -1
  48. package/dist/api/admin/apiRegisterSiteKeyEndpoint.d.ts +0 -14
  49. package/dist/api/admin/apiRegisterSiteKeyEndpoint.d.ts.map +0 -1
  50. package/dist/api/admin/apiRegisterSiteKeyEndpoint.js.map +0 -1
  51. package/dist/api/admin/apiRemoveDetectorKeyEndpoint.d.ts +0 -14
  52. package/dist/api/admin/apiRemoveDetectorKeyEndpoint.d.ts.map +0 -1
  53. package/dist/api/admin/apiRemoveDetectorKeyEndpoint.js.map +0 -1
  54. package/dist/api/admin/apiUpdateDetectorKeyEndpoint.d.ts +0 -14
  55. package/dist/api/admin/apiUpdateDetectorKeyEndpoint.d.ts.map +0 -1
  56. package/dist/api/admin/apiUpdateDetectorKeyEndpoint.js.map +0 -1
  57. package/dist/api/admin/createApiAdminRoutesProvider.d.ts +0 -4
  58. package/dist/api/admin/createApiAdminRoutesProvider.d.ts.map +0 -1
  59. package/dist/api/admin/createApiAdminRoutesProvider.js.map +0 -1
  60. package/dist/api/blacklistRequestInspector.d.ts +0 -39
  61. package/dist/api/blacklistRequestInspector.d.ts.map +0 -1
  62. package/dist/api/blacklistRequestInspector.js.map +0 -1
  63. package/dist/api/block.d.ts +0 -3
  64. package/dist/api/block.d.ts.map +0 -1
  65. package/dist/api/block.js.map +0 -1
  66. package/dist/api/captcha.d.ts +0 -4
  67. package/dist/api/captcha.d.ts.map +0 -1
  68. package/dist/api/captcha.js.map +0 -1
  69. package/dist/api/domainMiddleware.d.ts +0 -4
  70. package/dist/api/domainMiddleware.d.ts.map +0 -1
  71. package/dist/api/domainMiddleware.js.map +0 -1
  72. package/dist/api/headerCheckMiddleware.d.ts +0 -4
  73. package/dist/api/headerCheckMiddleware.d.ts.map +0 -1
  74. package/dist/api/headerCheckMiddleware.js.map +0 -1
  75. package/dist/api/ignoreMiddleware.d.ts +0 -3
  76. package/dist/api/ignoreMiddleware.d.ts.map +0 -1
  77. package/dist/api/ignoreMiddleware.js.map +0 -1
  78. package/dist/api/ja4Middleware.d.ts +0 -10
  79. package/dist/api/ja4Middleware.d.ts.map +0 -1
  80. package/dist/api/ja4Middleware.js.map +0 -1
  81. package/dist/api/public.d.ts +0 -3
  82. package/dist/api/public.d.ts.map +0 -1
  83. package/dist/api/public.js.map +0 -1
  84. package/dist/api/robotsMiddleware.d.ts +0 -3
  85. package/dist/api/robotsMiddleware.d.ts.map +0 -1
  86. package/dist/api/robotsMiddleware.js.map +0 -1
  87. package/dist/api/validateAddress.d.ts +0 -5
  88. package/dist/api/validateAddress.d.ts.map +0 -1
  89. package/dist/api/validateAddress.js.map +0 -1
  90. package/dist/api/verify.d.ts +0 -4
  91. package/dist/api/verify.d.ts.map +0 -1
  92. package/dist/api/verify.js.map +0 -1
  93. package/dist/index.d.ts +0 -15
  94. package/dist/index.d.ts.map +0 -1
  95. package/dist/index.js.map +0 -1
  96. package/dist/rules/lang.d.ts +0 -3
  97. package/dist/rules/lang.d.ts.map +0 -1
  98. package/dist/rules/lang.js.map +0 -1
  99. package/dist/schedulers/captchaScheduler.d.ts +0 -4
  100. package/dist/schedulers/captchaScheduler.d.ts.map +0 -1
  101. package/dist/schedulers/captchaScheduler.js.map +0 -1
  102. package/dist/schedulers/getClientList.d.ts +0 -4
  103. package/dist/schedulers/getClientList.d.ts.map +0 -1
  104. package/dist/schedulers/getClientList.js.map +0 -1
  105. package/dist/tasks/captchaManager.d.ts +0 -48
  106. package/dist/tasks/captchaManager.d.ts.map +0 -1
  107. package/dist/tasks/captchaManager.js.map +0 -1
  108. package/dist/tasks/client/clientTasks.d.ts +0 -21
  109. package/dist/tasks/client/clientTasks.d.ts.map +0 -1
  110. package/dist/tasks/client/clientTasks.js.map +0 -1
  111. package/dist/tasks/dataset/datasetTasks.d.ts +0 -13
  112. package/dist/tasks/dataset/datasetTasks.d.ts.map +0 -1
  113. package/dist/tasks/dataset/datasetTasks.js.map +0 -1
  114. package/dist/tasks/dataset/datasetTasksUtils.d.ts +0 -3
  115. package/dist/tasks/dataset/datasetTasksUtils.d.ts.map +0 -1
  116. package/dist/tasks/dataset/datasetTasksUtils.js.map +0 -1
  117. package/dist/tasks/detection/decodePayload.d.ts +0 -5
  118. package/dist/tasks/detection/decodePayload.d.ts.map +0 -1
  119. package/dist/tasks/detection/decodePayload.js.map +0 -1
  120. package/dist/tasks/detection/getBotScore.d.ts +0 -5
  121. package/dist/tasks/detection/getBotScore.d.ts.map +0 -1
  122. package/dist/tasks/detection/getBotScore.js.map +0 -1
  123. package/dist/tasks/frictionless/frictionlessTasks.d.ts +0 -23
  124. package/dist/tasks/frictionless/frictionlessTasks.d.ts.map +0 -1
  125. package/dist/tasks/frictionless/frictionlessTasks.js.map +0 -1
  126. package/dist/tasks/frictionless/frictionlessTasksUtils.d.ts +0 -5
  127. package/dist/tasks/frictionless/frictionlessTasksUtils.d.ts.map +0 -1
  128. package/dist/tasks/frictionless/frictionlessTasksUtils.js.map +0 -1
  129. package/dist/tasks/imgCaptcha/imgCaptchaTasks.d.ts +0 -29
  130. package/dist/tasks/imgCaptcha/imgCaptchaTasks.d.ts.map +0 -1
  131. package/dist/tasks/imgCaptcha/imgCaptchaTasks.js.map +0 -1
  132. package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.d.ts +0 -7
  133. package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.d.ts.map +0 -1
  134. package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.js.map +0 -1
  135. package/dist/tasks/index.d.ts +0 -2
  136. package/dist/tasks/index.d.ts.map +0 -1
  137. package/dist/tasks/index.js.map +0 -1
  138. package/dist/tasks/powCaptcha/powTasks.d.ts +0 -16
  139. package/dist/tasks/powCaptcha/powTasks.d.ts.map +0 -1
  140. package/dist/tasks/powCaptcha/powTasks.js.map +0 -1
  141. package/dist/tasks/powCaptcha/powTasksUtils.d.ts +0 -3
  142. package/dist/tasks/powCaptcha/powTasksUtils.d.ts.map +0 -1
  143. package/dist/tasks/powCaptcha/powTasksUtils.js.map +0 -1
  144. package/dist/tasks/tasks.d.ts +0 -25
  145. package/dist/tasks/tasks.d.ts.map +0 -1
  146. package/dist/tasks/tasks.js.map +0 -1
  147. package/dist/tests/index.d.ts +0 -2
  148. package/dist/tests/index.d.ts.map +0 -1
  149. package/dist/tests/index.js +0 -2
  150. package/dist/tests/index.js.map +0 -1
  151. package/dist/tests/integration/accessRules.integration.test.d.ts +0 -2
  152. package/dist/tests/integration/accessRules.integration.test.d.ts.map +0 -1
  153. package/dist/tests/integration/accessRules.integration.test.js +0 -164
  154. package/dist/tests/integration/accessRules.integration.test.js.map +0 -1
  155. package/dist/tests/integration/imgCaptcha.integration.test.d.ts +0 -2
  156. package/dist/tests/integration/imgCaptcha.integration.test.d.ts.map +0 -1
  157. package/dist/tests/integration/imgCaptcha.integration.test.js +0 -261
  158. package/dist/tests/integration/imgCaptcha.integration.test.js.map +0 -1
  159. package/dist/tests/integration/mocks/solvedTestCaptchas.d.ts +0 -32
  160. package/dist/tests/integration/mocks/solvedTestCaptchas.d.ts.map +0 -1
  161. package/dist/tests/integration/mocks/solvedTestCaptchas.js +0 -1046
  162. package/dist/tests/integration/mocks/solvedTestCaptchas.js.map +0 -1
  163. package/dist/tests/integration/powCaptcha.integration.test.d.ts +0 -2
  164. package/dist/tests/integration/powCaptcha.integration.test.d.ts.map +0 -1
  165. package/dist/tests/integration/powCaptcha.integration.test.js +0 -306
  166. package/dist/tests/integration/powCaptcha.integration.test.js.map +0 -1
  167. package/dist/tests/integration/registerSitekey.d.ts +0 -3
  168. package/dist/tests/integration/registerSitekey.d.ts.map +0 -1
  169. package/dist/tests/integration/registerSitekey.js +0 -39
  170. package/dist/tests/integration/registerSitekey.js.map +0 -1
  171. package/dist/tests/integration/userAccessPolicy.d.ts +0 -16
  172. package/dist/tests/integration/userAccessPolicy.d.ts.map +0 -1
  173. package/dist/tests/integration/userAccessPolicy.js +0 -55
  174. package/dist/tests/integration/userAccessPolicy.js.map +0 -1
  175. package/dist/tests/unit/api/ignoreMiddleware.unit.test.d.ts +0 -2
  176. package/dist/tests/unit/api/ignoreMiddleware.unit.test.d.ts.map +0 -1
  177. package/dist/tests/unit/api/ignoreMiddleware.unit.test.js +0 -43
  178. package/dist/tests/unit/api/ignoreMiddleware.unit.test.js.map +0 -1
  179. package/dist/tests/unit/api/ja4Middleware.unit.test.d.ts +0 -2
  180. package/dist/tests/unit/api/ja4Middleware.unit.test.d.ts.map +0 -1
  181. package/dist/tests/unit/api/ja4Middleware.unit.test.js +0 -71
  182. package/dist/tests/unit/api/ja4Middleware.unit.test.js.map +0 -1
  183. package/dist/tests/unit/schedulers/captchaScheduler.unit.test.d.ts +0 -2
  184. package/dist/tests/unit/schedulers/captchaScheduler.unit.test.d.ts.map +0 -1
  185. package/dist/tests/unit/schedulers/captchaScheduler.unit.test.js +0 -75
  186. package/dist/tests/unit/schedulers/captchaScheduler.unit.test.js.map +0 -1
  187. package/dist/tests/unit/tasks/captchaManager.unit.test.d.ts +0 -2
  188. package/dist/tests/unit/tasks/captchaManager.unit.test.d.ts.map +0 -1
  189. package/dist/tests/unit/tasks/captchaManager.unit.test.js +0 -236
  190. package/dist/tests/unit/tasks/captchaManager.unit.test.js.map +0 -1
  191. package/dist/tests/unit/tasks/client/clientTasks.unit.test.d.ts +0 -2
  192. package/dist/tests/unit/tasks/client/clientTasks.unit.test.d.ts.map +0 -1
  193. package/dist/tests/unit/tasks/client/clientTasks.unit.test.js +0 -277
  194. package/dist/tests/unit/tasks/client/clientTasks.unit.test.js.map +0 -1
  195. package/dist/tests/unit/tasks/dataset/datasetTasks.unit.test.d.ts +0 -2
  196. package/dist/tests/unit/tasks/dataset/datasetTasks.unit.test.d.ts.map +0 -1
  197. package/dist/tests/unit/tasks/dataset/datasetTasks.unit.test.js +0 -93
  198. package/dist/tests/unit/tasks/dataset/datasetTasks.unit.test.js.map +0 -1
  199. package/dist/tests/unit/tasks/dataset/datasetTasksUtils.unit.test.d.ts +0 -2
  200. package/dist/tests/unit/tasks/dataset/datasetTasksUtils.unit.test.d.ts.map +0 -1
  201. package/dist/tests/unit/tasks/dataset/datasetTasksUtils.unit.test.js +0 -75
  202. package/dist/tests/unit/tasks/dataset/datasetTasksUtils.unit.test.js.map +0 -1
  203. package/dist/tests/unit/tasks/frictionless/frictionlessTasks.unit.test.d.ts +0 -2
  204. package/dist/tests/unit/tasks/frictionless/frictionlessTasks.unit.test.d.ts.map +0 -1
  205. package/dist/tests/unit/tasks/frictionless/frictionlessTasks.unit.test.js +0 -68
  206. package/dist/tests/unit/tasks/frictionless/frictionlessTasks.unit.test.js.map +0 -1
  207. package/dist/tests/unit/tasks/frictionless/frictionlessTasksUtils.unit.test.d.ts +0 -2
  208. package/dist/tests/unit/tasks/frictionless/frictionlessTasksUtils.unit.test.d.ts.map +0 -1
  209. package/dist/tests/unit/tasks/frictionless/frictionlessTasksUtils.unit.test.js +0 -37
  210. package/dist/tests/unit/tasks/frictionless/frictionlessTasksUtils.unit.test.js.map +0 -1
  211. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.d.ts +0 -2
  212. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.d.ts.map +0 -1
  213. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.js +0 -402
  214. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.unit.test.js.map +0 -1
  215. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.d.ts +0 -2
  216. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.d.ts.map +0 -1
  217. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.js +0 -46
  218. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.js.map +0 -1
  219. package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.d.ts +0 -2
  220. package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.d.ts.map +0 -1
  221. package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js +0 -228
  222. package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js.map +0 -1
  223. package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.unit.test.d.ts +0 -2
  224. package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.unit.test.d.ts.map +0 -1
  225. package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.unit.test.js +0 -68
  226. package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.unit.test.js.map +0 -1
  227. package/dist/tests/unit/util.unit.test.d.ts +0 -2
  228. package/dist/tests/unit/util.unit.test.d.ts.map +0 -1
  229. package/dist/tests/unit/util.unit.test.js +0 -148
  230. package/dist/tests/unit/util.unit.test.js.map +0 -1
  231. package/dist/util.d.ts +0 -13
  232. package/dist/util.d.ts.map +0 -1
  233. package/dist/util.js.map +0 -1
@@ -1,378 +1,541 @@
1
1
  import { handleErrors } from "@prosopo/api-express-router";
2
2
  import { ProsopoApiError } from "@prosopo/common";
3
3
  import { parseCaptchaAssets } from "@prosopo/datasets";
4
- import { ApiParams, CaptchaRequestBody, CaptchaSolutionBody, CaptchaType, ClientApiPaths, GetFrictionlessCaptchaChallengeRequestBody, GetPowCaptchaChallengeRequestBody, SubmitPowCaptchaSolutionBody, } from "@prosopo/types";
5
- import { flatten, getIPAddress } from "@prosopo/util";
4
+ import { ClientApiPaths, CaptchaRequestBody, CaptchaType, ApiParams, CaptchaSolutionBody, GetPowCaptchaChallengeRequestBody, SubmitPowCaptchaSolutionBody, GetFrictionlessCaptchaChallengeRequestBody } from "@prosopo/types";
5
+ import { getIPAddress, flatten } from "@prosopo/util";
6
6
  import express from "express";
7
7
  import { FrictionlessManager } from "../tasks/frictionless/frictionlessTasks.js";
8
8
  import { Tasks } from "../tasks/tasks.js";
9
9
  import { getRequestUserScope } from "./blacklistRequestInspector.js";
10
- import { validateAddr, validateSiteKey } from "./validateAddress.js";
10
+ import { validateSiteKey, validateAddr } from "./validateAddress.js";
11
11
  const DEFAULT_FRICTIONLESS_THRESHOLD = 0.5;
12
- export function prosopoRouter(env) {
13
- const router = express.Router();
14
- const userAccessRulesStorage = env.getDb().getUserAccessRulesStorage();
15
- router.post(ClientApiPaths.GetImageCaptchaChallenge, async (req, res, next) => {
16
- const tasks = new Tasks(env, req.logger);
17
- let parsed;
18
- if (!req.ip) {
19
- return next(new ProsopoApiError("API.BAD_REQUEST", {
20
- context: { code: 400, error: "IP address not found" },
21
- i18n: req.i18n,
22
- logger: req.logger,
23
- }));
24
- }
25
- const ipAddress = getIPAddress(req.ip || "");
26
- try {
27
- parsed = CaptchaRequestBody.parse(req.body);
28
- }
29
- catch (err) {
30
- return next(new ProsopoApiError("CAPTCHA.PARSE_ERROR", {
31
- context: { code: 400, error: err },
32
- i18n: req.i18n,
33
- logger: req.logger,
34
- }));
35
- }
36
- const { datasetId, user, dapp, sessionId } = parsed;
37
- validateSiteKey(dapp);
38
- validateAddr(user);
39
- try {
40
- const clientRecord = await tasks.db.getClientRecord(dapp);
41
- if (!clientRecord) {
42
- return next(new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
43
- context: { code: 400, siteKey: dapp },
44
- i18n: req.i18n,
45
- logger: req.logger,
46
- }));
47
- }
48
- const { valid, reason, frictionlessTokenId } = await tasks.imgCaptchaManager.isValidRequest(clientRecord, CaptchaType.image, sessionId);
49
- if (!valid) {
50
- return next(new ProsopoApiError(reason || "API.BAD_REQUEST", {
51
- context: {
52
- code: 400,
53
- siteKey: dapp,
54
- user,
55
- },
56
- i18n: req.i18n,
57
- logger: req.logger,
58
- }));
59
- }
60
- const userScope = getRequestUserScope(flatten(req.headers), req.ja4, req.ip, user);
61
- const userAccessPolicy = (await tasks.imgCaptchaManager.getPrioritisedAccessPolicies(userAccessRulesStorage, dapp, userScope))[0];
62
- const captchaConfig = {
63
- solved: {
64
- count: userAccessPolicy?.solvedImagesCount ||
65
- env.config.captchas.solved.count,
66
- },
67
- unsolved: {
68
- count: userAccessPolicy?.unsolvedImagesCount ||
69
- env.config.captchas.unsolved.count,
70
- },
71
- };
72
- const taskData = await tasks.imgCaptchaManager.getRandomCaptchasAndRequestHash(datasetId, user, ipAddress, captchaConfig, clientRecord.settings.imageThreshold ?? 0.8, frictionlessTokenId);
73
- const captchaResponse = {
74
- [ApiParams.status]: "ok",
75
- [ApiParams.captchas]: taskData.captchas.map((captcha) => ({
76
- ...captcha,
77
- target: req.t(`TARGET.${captcha.target}`),
78
- items: captcha.items.map((item) => parseCaptchaAssets(item, env.assetsResolver)),
79
- })),
80
- [ApiParams.requestHash]: taskData.requestHash,
81
- [ApiParams.timestamp]: taskData.timestamp.toString(),
82
- [ApiParams.signature]: {
83
- [ApiParams.provider]: {
84
- [ApiParams.requestHash]: taskData.signedRequestHash,
85
- },
86
- },
87
- };
88
- return res.json(captchaResponse);
12
+ function prosopoRouter(env) {
13
+ const router = express.Router();
14
+ const userAccessRulesStorage = env.getDb().getUserAccessRulesStorage();
15
+ router.post(
16
+ ClientApiPaths.GetImageCaptchaChallenge,
17
+ async (req, res, next) => {
18
+ const tasks = new Tasks(env, req.logger);
19
+ let parsed;
20
+ if (!req.ip) {
21
+ return next(
22
+ new ProsopoApiError("API.BAD_REQUEST", {
23
+ context: { code: 400, error: "IP address not found" },
24
+ i18n: req.i18n,
25
+ logger: req.logger
26
+ })
27
+ );
28
+ }
29
+ const ipAddress = getIPAddress(req.ip || "");
30
+ try {
31
+ parsed = CaptchaRequestBody.parse(req.body);
32
+ } catch (err) {
33
+ return next(
34
+ new ProsopoApiError("CAPTCHA.PARSE_ERROR", {
35
+ context: { code: 400, error: err },
36
+ i18n: req.i18n,
37
+ logger: req.logger
38
+ })
39
+ );
40
+ }
41
+ const { datasetId, user, dapp, sessionId } = parsed;
42
+ validateSiteKey(dapp);
43
+ validateAddr(user);
44
+ try {
45
+ const clientRecord = await tasks.db.getClientRecord(dapp);
46
+ if (!clientRecord) {
47
+ return next(
48
+ new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
49
+ context: { code: 400, siteKey: dapp },
50
+ i18n: req.i18n,
51
+ logger: req.logger
52
+ })
53
+ );
89
54
  }
90
- catch (err) {
91
- req.logger.error(() => ({
92
- err,
93
- data: req.params,
94
- msg: "Error in PoW captcha solution submission",
95
- }));
96
- return next(new ProsopoApiError("API.BAD_REQUEST", {
97
- context: {
98
- error: err,
99
- code: 500,
100
- params: req.params,
101
- context: err,
102
- },
103
- i18n: req.i18n,
104
- logger: req.logger,
105
- }));
55
+ const userScope = getRequestUserScope(
56
+ flatten(req.headers),
57
+ req.ja4,
58
+ req.ip,
59
+ user
60
+ );
61
+ const userAccessPolicy = (await tasks.imgCaptchaManager.getPrioritisedAccessPolicies(
62
+ userAccessRulesStorage,
63
+ dapp,
64
+ userScope
65
+ ))[0];
66
+ const { valid, reason, frictionlessTokenId } = await tasks.imgCaptchaManager.isValidRequest(
67
+ clientRecord,
68
+ CaptchaType.image,
69
+ sessionId,
70
+ userAccessPolicy
71
+ );
72
+ if (!valid) {
73
+ return next(
74
+ new ProsopoApiError(reason || "API.BAD_REQUEST", {
75
+ context: {
76
+ code: 400,
77
+ siteKey: dapp,
78
+ user
79
+ },
80
+ i18n: req.i18n,
81
+ logger: req.logger
82
+ })
83
+ );
106
84
  }
107
- });
108
- router.post(ClientApiPaths.SubmitImageCaptchaSolution, async (req, res, next) => {
109
- const tasks = new Tasks(env, req.logger);
110
- let parsed;
111
- try {
112
- parsed = CaptchaSolutionBody.parse(req.body);
113
- }
114
- catch (err) {
115
- return next(new ProsopoApiError("CAPTCHA.PARSE_ERROR", {
116
- context: { code: 400, error: err, body: req.body },
117
- i18n: req.i18n,
118
- logger: req.logger,
119
- }));
120
- }
121
- const { user, dapp } = parsed;
122
- validateSiteKey(dapp);
123
- validateAddr(user);
124
- try {
125
- const clientRecord = await tasks.db.getClientRecord(parsed.dapp);
126
- if (!clientRecord) {
127
- return next(new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
128
- context: { code: 400, siteKey: dapp },
129
- i18n: req.i18n,
130
- logger: req.logger,
131
- }));
85
+ const captchaConfig = {
86
+ solved: {
87
+ count: userAccessPolicy?.solvedImagesCount || env.config.captchas.solved.count
88
+ },
89
+ unsolved: {
90
+ count: userAccessPolicy?.unsolvedImagesCount || env.config.captchas.unsolved.count
91
+ }
92
+ };
93
+ const taskData = await tasks.imgCaptchaManager.getRandomCaptchasAndRequestHash(
94
+ datasetId,
95
+ user,
96
+ ipAddress,
97
+ captchaConfig,
98
+ clientRecord.settings.imageThreshold ?? 0.8,
99
+ frictionlessTokenId
100
+ );
101
+ const captchaResponse = {
102
+ [ApiParams.status]: "ok",
103
+ [ApiParams.captchas]: taskData.captchas.map((captcha) => ({
104
+ ...captcha,
105
+ target: req.t(`TARGET.${captcha.target}`),
106
+ items: captcha.items.map(
107
+ (item) => parseCaptchaAssets(item, env.assetsResolver)
108
+ )
109
+ })),
110
+ [ApiParams.requestHash]: taskData.requestHash,
111
+ [ApiParams.timestamp]: taskData.timestamp.toString(),
112
+ [ApiParams.signature]: {
113
+ [ApiParams.provider]: {
114
+ [ApiParams.requestHash]: taskData.signedRequestHash
132
115
  }
133
- const result = await tasks.imgCaptchaManager.dappUserSolution(user, dapp, parsed[ApiParams.requestHash], parsed[ApiParams.captchas], parsed[ApiParams.signature].user.timestamp, Number.parseInt(parsed[ApiParams.timestamp]), parsed[ApiParams.signature].provider.requestHash, getIPAddress(req.ip || "").bigInt(), flatten(req.headers), req.ja4);
134
- const returnValue = {
135
- status: req.i18n.t(result.verified ? "API.CAPTCHA_PASSED" : "API.CAPTCHA_FAILED"),
136
- ...result,
137
- };
138
- return res.json(returnValue);
116
+ }
117
+ };
118
+ return res.json(captchaResponse);
119
+ } catch (err) {
120
+ req.logger.error(() => ({
121
+ err,
122
+ data: req.params,
123
+ msg: "Error in PoW captcha solution submission"
124
+ }));
125
+ return next(
126
+ new ProsopoApiError("API.BAD_REQUEST", {
127
+ context: {
128
+ error: err,
129
+ code: 500,
130
+ params: req.params,
131
+ context: err
132
+ },
133
+ i18n: req.i18n,
134
+ logger: req.logger
135
+ })
136
+ );
137
+ }
138
+ }
139
+ );
140
+ router.post(
141
+ ClientApiPaths.SubmitImageCaptchaSolution,
142
+ async (req, res, next) => {
143
+ const tasks = new Tasks(env, req.logger);
144
+ let parsed;
145
+ try {
146
+ parsed = CaptchaSolutionBody.parse(req.body);
147
+ } catch (err) {
148
+ return next(
149
+ new ProsopoApiError("CAPTCHA.PARSE_ERROR", {
150
+ context: { code: 400, error: err, body: req.body },
151
+ i18n: req.i18n,
152
+ logger: req.logger
153
+ })
154
+ );
155
+ }
156
+ const { user, dapp } = parsed;
157
+ validateSiteKey(dapp);
158
+ validateAddr(user);
159
+ try {
160
+ const clientRecord = await tasks.db.getClientRecord(parsed.dapp);
161
+ if (!clientRecord) {
162
+ return next(
163
+ new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
164
+ context: { code: 400, siteKey: dapp },
165
+ i18n: req.i18n,
166
+ logger: req.logger
167
+ })
168
+ );
139
169
  }
140
- catch (err) {
141
- req.logger.error(() => ({
142
- err,
143
- body: req.body,
144
- msg: "Error in PoW captcha solution submission",
145
- }));
146
- return next(new ProsopoApiError("API.BAD_REQUEST", {
147
- context: {
148
- code: 500,
149
- siteKey: req.body.dapp,
150
- error: err,
151
- },
152
- i18n: req.i18n,
153
- logger: req.logger,
154
- }));
170
+ const result = await tasks.imgCaptchaManager.dappUserSolution(
171
+ user,
172
+ dapp,
173
+ parsed[ApiParams.requestHash],
174
+ parsed[ApiParams.captchas],
175
+ parsed[ApiParams.signature].user.timestamp,
176
+ Number.parseInt(parsed[ApiParams.timestamp]),
177
+ parsed[ApiParams.signature].provider.requestHash,
178
+ getIPAddress(req.ip || "").bigInt(),
179
+ flatten(req.headers),
180
+ req.ja4
181
+ );
182
+ const returnValue = {
183
+ status: req.i18n.t(
184
+ result.verified ? "API.CAPTCHA_PASSED" : "API.CAPTCHA_FAILED"
185
+ ),
186
+ ...result
187
+ };
188
+ return res.json(returnValue);
189
+ } catch (err) {
190
+ req.logger.error(() => ({
191
+ err,
192
+ body: req.body,
193
+ msg: "Error in PoW captcha solution submission"
194
+ }));
195
+ return next(
196
+ new ProsopoApiError("API.BAD_REQUEST", {
197
+ context: {
198
+ code: 500,
199
+ siteKey: req.body.dapp,
200
+ error: err
201
+ },
202
+ i18n: req.i18n,
203
+ logger: req.logger
204
+ })
205
+ );
206
+ }
207
+ }
208
+ );
209
+ router.post(ClientApiPaths.GetPowCaptchaChallenge, async (req, res, next) => {
210
+ let parsed;
211
+ const tasks = new Tasks(env);
212
+ tasks.setLogger(req.logger);
213
+ try {
214
+ parsed = GetPowCaptchaChallengeRequestBody.parse(req.body);
215
+ } catch (err) {
216
+ return next(
217
+ new ProsopoApiError("CAPTCHA.PARSE_ERROR", {
218
+ context: { code: 400, error: err },
219
+ i18n: req.i18n,
220
+ logger: req.logger
221
+ })
222
+ );
223
+ }
224
+ const { user, dapp, sessionId } = parsed;
225
+ validateSiteKey(dapp);
226
+ validateAddr(user);
227
+ try {
228
+ const clientSettings = await tasks.db.getClientRecord(dapp);
229
+ if (!clientSettings) {
230
+ return next(
231
+ new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
232
+ context: { code: 400, siteKey: dapp },
233
+ i18n: req.i18n,
234
+ logger: req.logger
235
+ })
236
+ );
237
+ }
238
+ const userScope = getRequestUserScope(
239
+ flatten(req.headers),
240
+ req.ja4,
241
+ req.ip,
242
+ user
243
+ );
244
+ const userAccessPolicy = (await tasks.powCaptchaManager.getPrioritisedAccessPolicies(
245
+ userAccessRulesStorage,
246
+ dapp,
247
+ userScope
248
+ ))[0];
249
+ const { valid, reason, frictionlessTokenId } = await tasks.powCaptchaManager.isValidRequest(
250
+ clientSettings,
251
+ CaptchaType.pow,
252
+ sessionId,
253
+ userAccessPolicy
254
+ );
255
+ if (!valid) {
256
+ return next(
257
+ new ProsopoApiError(reason || "API.BAD_REQUEST", {
258
+ context: {
259
+ code: 400,
260
+ siteKey: dapp,
261
+ user
262
+ },
263
+ i18n: req.i18n,
264
+ logger: req.logger
265
+ })
266
+ );
267
+ }
268
+ const origin = req.headers.origin;
269
+ if (!origin) {
270
+ return next(
271
+ new ProsopoApiError("API.BAD_REQUEST", {
272
+ context: {
273
+ error: "Origin header not found",
274
+ code: 400,
275
+ siteKey: dapp,
276
+ user
277
+ },
278
+ i18n: req.i18n,
279
+ logger: req.logger
280
+ })
281
+ );
282
+ }
283
+ const challenge = await tasks.powCaptchaManager.getPowCaptchaChallenge(
284
+ user,
285
+ dapp,
286
+ origin,
287
+ userAccessPolicy?.powDifficulty || clientSettings?.settings?.powDifficulty
288
+ );
289
+ await tasks.db.storePowCaptchaRecord(
290
+ challenge.challenge,
291
+ {
292
+ requestedAtTimestamp: challenge.requestedAtTimestamp,
293
+ userAccount: user,
294
+ dappAccount: dapp
295
+ },
296
+ challenge.difficulty,
297
+ challenge.providerSignature,
298
+ getIPAddress(req.ip || "").bigInt(),
299
+ flatten(req.headers),
300
+ req.ja4,
301
+ frictionlessTokenId
302
+ );
303
+ const getPowCaptchaResponse = {
304
+ [ApiParams.status]: "ok",
305
+ [ApiParams.challenge]: challenge.challenge,
306
+ [ApiParams.difficulty]: challenge.difficulty,
307
+ [ApiParams.timestamp]: challenge.requestedAtTimestamp.toString(),
308
+ [ApiParams.signature]: {
309
+ [ApiParams.provider]: {
310
+ [ApiParams.challenge]: challenge.providerSignature
311
+ }
155
312
  }
156
- });
157
- router.post(ClientApiPaths.GetPowCaptchaChallenge, async (req, res, next) => {
158
- let parsed;
159
- const tasks = new Tasks(env);
160
- tasks.setLogger(req.logger);
161
- try {
162
- parsed = GetPowCaptchaChallengeRequestBody.parse(req.body);
313
+ };
314
+ return res.json(getPowCaptchaResponse);
315
+ } catch (err) {
316
+ req.logger.error(() => ({
317
+ err,
318
+ body: req.body,
319
+ msg: "Error in PoW captcha solution submission"
320
+ }));
321
+ return next(
322
+ new ProsopoApiError("API.BAD_REQUEST", {
323
+ context: {
324
+ code: 500,
325
+ siteKey: req.body.dapp,
326
+ user: req.body.user,
327
+ error: err
328
+ },
329
+ i18n: req.i18n,
330
+ logger: req.logger
331
+ })
332
+ );
333
+ }
334
+ });
335
+ router.post(
336
+ ClientApiPaths.SubmitPowCaptchaSolution,
337
+ async (req, res, next) => {
338
+ let parsed;
339
+ const tasks = new Tasks(env, req.logger);
340
+ try {
341
+ parsed = SubmitPowCaptchaSolutionBody.parse(req.body);
342
+ } catch (err) {
343
+ return next(
344
+ new ProsopoApiError("CAPTCHA.PARSE_ERROR", {
345
+ context: { code: 400, error: err, body: req.body },
346
+ i18n: req.i18n,
347
+ logger: req.logger
348
+ })
349
+ );
350
+ }
351
+ const {
352
+ challenge,
353
+ difficulty,
354
+ signature,
355
+ nonce,
356
+ verifiedTimeout,
357
+ dapp,
358
+ user
359
+ } = parsed;
360
+ validateSiteKey(dapp);
361
+ validateAddr(user);
362
+ try {
363
+ const clientRecord = await tasks.db.getClientRecord(dapp);
364
+ if (!clientRecord) {
365
+ return next(
366
+ new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
367
+ context: { code: 400, siteKey: dapp },
368
+ i18n: req.i18n,
369
+ logger: req.logger
370
+ })
371
+ );
163
372
  }
164
- catch (err) {
165
- return next(new ProsopoApiError("CAPTCHA.PARSE_ERROR", {
166
- context: { code: 400, error: err },
167
- i18n: req.i18n,
168
- logger: req.logger,
169
- }));
170
- }
171
- const { user, dapp, sessionId } = parsed;
172
- validateSiteKey(dapp);
173
- validateAddr(user);
174
- try {
175
- const clientSettings = await tasks.db.getClientRecord(dapp);
176
- if (!clientSettings) {
177
- return next(new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
178
- context: { code: 400, siteKey: dapp },
179
- i18n: req.i18n,
180
- logger: req.logger,
181
- }));
182
- }
183
- const { valid, reason, frictionlessTokenId } = await tasks.powCaptchaManager.isValidRequest(clientSettings, CaptchaType.pow, sessionId);
184
- if (!valid) {
185
- return next(new ProsopoApiError(reason || "API.BAD_REQUEST", {
186
- context: {
187
- code: 400,
188
- siteKey: dapp,
189
- user,
190
- },
191
- i18n: req.i18n,
192
- logger: req.logger,
193
- }));
194
- }
195
- const origin = req.headers.origin;
196
- if (!origin) {
197
- return next(new ProsopoApiError("API.BAD_REQUEST", {
198
- context: {
199
- error: "Origin header not found",
200
- code: 400,
201
- siteKey: dapp,
202
- user,
203
- },
204
- i18n: req.i18n,
205
- logger: req.logger,
206
- }));
207
- }
208
- const userScope = getRequestUserScope(flatten(req.headers), req.ja4, req.ip, user);
209
- const userAccessPolicy = (await tasks.powCaptchaManager.getPrioritisedAccessPolicies(userAccessRulesStorage, dapp, userScope))[0];
210
- const challenge = await tasks.powCaptchaManager.getPowCaptchaChallenge(user, dapp, origin, userAccessPolicy?.powDifficulty ||
211
- clientSettings?.settings?.powDifficulty);
212
- await tasks.db.storePowCaptchaRecord(challenge.challenge, {
213
- requestedAtTimestamp: challenge.requestedAtTimestamp,
214
- userAccount: user,
215
- dappAccount: dapp,
216
- }, challenge.difficulty, challenge.providerSignature, getIPAddress(req.ip || "").bigInt(), flatten(req.headers), req.ja4, frictionlessTokenId);
217
- const getPowCaptchaResponse = {
218
- [ApiParams.status]: "ok",
219
- [ApiParams.challenge]: challenge.challenge,
220
- [ApiParams.difficulty]: challenge.difficulty,
221
- [ApiParams.timestamp]: challenge.requestedAtTimestamp.toString(),
222
- [ApiParams.signature]: {
223
- [ApiParams.provider]: {
224
- [ApiParams.challenge]: challenge.providerSignature,
225
- },
226
- },
227
- };
228
- return res.json(getPowCaptchaResponse);
229
- }
230
- catch (err) {
231
- req.logger.error(() => ({
232
- err,
233
- body: req.body,
234
- msg: "Error in PoW captcha solution submission",
235
- }));
236
- return next(new ProsopoApiError("API.BAD_REQUEST", {
237
- context: {
238
- code: 500,
239
- siteKey: req.body.dapp,
240
- user: req.body.user,
241
- error: err,
242
- },
243
- i18n: req.i18n,
244
- logger: req.logger,
245
- }));
246
- }
247
- });
248
- router.post(ClientApiPaths.SubmitPowCaptchaSolution, async (req, res, next) => {
249
- let parsed;
373
+ const verified = await tasks.powCaptchaManager.verifyPowCaptchaSolution(
374
+ challenge,
375
+ difficulty,
376
+ signature.provider.challenge,
377
+ nonce,
378
+ verifiedTimeout,
379
+ signature.user.timestamp,
380
+ getIPAddress(req.ip || ""),
381
+ flatten(req.headers)
382
+ );
383
+ const response = { status: "ok", verified };
384
+ return res.json(response);
385
+ } catch (err) {
386
+ req.logger.error(() => ({
387
+ err,
388
+ body: req.body,
389
+ msg: "Error in PoW captcha solution submission"
390
+ }));
391
+ return next(
392
+ new ProsopoApiError("API.BAD_REQUEST", {
393
+ context: {
394
+ code: 500,
395
+ siteKey: req.body.dapp,
396
+ error: err
397
+ },
398
+ i18n: req.i18n,
399
+ logger: req.logger
400
+ })
401
+ );
402
+ }
403
+ }
404
+ );
405
+ router.post(
406
+ ClientApiPaths.GetFrictionlessCaptchaChallenge,
407
+ async (req, res, next) => {
408
+ try {
250
409
  const tasks = new Tasks(env, req.logger);
251
- try {
252
- parsed = SubmitPowCaptchaSolutionBody.parse(req.body);
410
+ const { token, dapp, user } = GetFrictionlessCaptchaChallengeRequestBody.parse(req.body);
411
+ const existingToken = await tasks.db.getFrictionlessTokenRecordByToken(token);
412
+ if (existingToken) {
413
+ req.logger.info(() => ({
414
+ token: existingToken,
415
+ msg: "Token has already been used"
416
+ }));
417
+ return res.json(
418
+ await tasks.frictionlessManager.sendImageCaptcha(
419
+ existingToken._id
420
+ )
421
+ );
253
422
  }
254
- catch (err) {
255
- return next(new ProsopoApiError("CAPTCHA.PARSE_ERROR", {
256
- context: { code: 400, error: err, body: req.body },
257
- i18n: req.i18n,
258
- logger: req.logger,
259
- }));
423
+ const lScore = tasks.frictionlessManager.checkLangRules(
424
+ req.headers["accept-language"] || ""
425
+ );
426
+ const { baseBotScore, timestamp } = await tasks.frictionlessManager.decryptPayload(token);
427
+ const botScore = baseBotScore + lScore;
428
+ const clientRecord = await tasks.db.getClientRecord(dapp);
429
+ if (!clientRecord) {
430
+ return next(
431
+ new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
432
+ context: { code: 400, siteKey: dapp },
433
+ i18n: req.i18n,
434
+ logger: req.logger
435
+ })
436
+ );
260
437
  }
261
- const { challenge, difficulty, signature, nonce, verifiedTimeout, dapp, user, } = parsed;
262
- validateSiteKey(dapp);
263
- validateAddr(user);
264
- try {
265
- const clientRecord = await tasks.db.getClientRecord(dapp);
266
- if (!clientRecord) {
267
- return next(new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
268
- context: { code: 400, siteKey: dapp },
269
- i18n: req.i18n,
270
- logger: req.logger,
271
- }));
272
- }
273
- const verified = await tasks.powCaptchaManager.verifyPowCaptchaSolution(challenge, difficulty, signature.provider.challenge, nonce, verifiedTimeout, signature.user.timestamp, getIPAddress(req.ip || ""), flatten(req.headers));
274
- const response = { status: "ok", verified };
275
- return res.json(response);
438
+ const { valid, reason } = await tasks.frictionlessManager.isValidRequest(
439
+ clientRecord,
440
+ CaptchaType.frictionless
441
+ );
442
+ if (!valid) {
443
+ return next(
444
+ new ProsopoApiError(reason || "API.BAD_REQUEST", {
445
+ context: {
446
+ code: 400,
447
+ siteKey: dapp,
448
+ user
449
+ },
450
+ i18n: req.i18n,
451
+ logger: req.logger
452
+ })
453
+ );
276
454
  }
277
- catch (err) {
278
- req.logger.error(() => ({
279
- err,
280
- body: req.body,
281
- msg: "Error in PoW captcha solution submission",
282
- }));
283
- return next(new ProsopoApiError("API.BAD_REQUEST", {
284
- context: {
285
- code: 500,
286
- siteKey: req.body.dapp,
287
- error: err,
288
- },
289
- i18n: req.i18n,
290
- logger: req.logger,
291
- }));
455
+ const botThreshold = clientRecord.settings?.frictionlessThreshold || DEFAULT_FRICTIONLESS_THRESHOLD;
456
+ const tokenId = await tasks.db.storeFrictionlessTokenRecord({
457
+ token,
458
+ score: botScore,
459
+ threshold: botThreshold,
460
+ scoreComponents: {
461
+ baseScore: baseBotScore,
462
+ ...lScore && { lScore }
463
+ }
464
+ });
465
+ const userScope = getRequestUserScope(
466
+ flatten(req.headers),
467
+ req.ja4,
468
+ req.ip,
469
+ user
470
+ );
471
+ const userAccessPolicy = (await tasks.frictionlessManager.getPrioritisedAccessPolicies(
472
+ userAccessRulesStorage,
473
+ dapp,
474
+ userScope
475
+ ))[0];
476
+ if (userAccessPolicy) {
477
+ await tasks.frictionlessManager.scoreIncreaseAccessPolicy(
478
+ userAccessPolicy,
479
+ baseBotScore,
480
+ botScore,
481
+ tokenId
482
+ );
483
+ if (userAccessPolicy.captchaType === CaptchaType.image) {
484
+ return res.json(
485
+ await tasks.frictionlessManager.sendImageCaptcha(tokenId)
486
+ );
487
+ }
488
+ if (userAccessPolicy.captchaType === CaptchaType.pow) {
489
+ return res.json(
490
+ await tasks.frictionlessManager.sendPowCaptcha(tokenId)
491
+ );
492
+ }
292
493
  }
293
- });
294
- router.post(ClientApiPaths.GetFrictionlessCaptchaChallenge, async (req, res, next) => {
295
- try {
296
- const tasks = new Tasks(env, req.logger);
297
- const { token, dapp, user } = GetFrictionlessCaptchaChallengeRequestBody.parse(req.body);
298
- const existingToken = await tasks.db.getFrictionlessTokenRecordByToken(token);
299
- if (existingToken) {
300
- req.logger.info(() => ({
301
- token: existingToken,
302
- msg: "Token has already been used",
303
- }));
304
- return res.json(await tasks.frictionlessManager.sendImageCaptcha(existingToken._id));
305
- }
306
- const lScore = tasks.frictionlessManager.checkLangRules(req.headers["accept-language"] || "");
307
- const { baseBotScore, timestamp } = await tasks.frictionlessManager.decryptPayload(token);
308
- const botScore = baseBotScore + lScore;
309
- const clientRecord = await tasks.db.getClientRecord(dapp);
310
- if (!clientRecord) {
311
- return next(new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
312
- context: { code: 400, siteKey: dapp },
313
- i18n: req.i18n,
314
- logger: req.logger,
315
- }));
316
- }
317
- const { valid, reason } = await tasks.frictionlessManager.isValidRequest(clientRecord, CaptchaType.frictionless);
318
- if (!valid) {
319
- return next(new ProsopoApiError(reason || "API.BAD_REQUEST", {
320
- context: {
321
- code: 400,
322
- siteKey: dapp,
323
- user,
324
- },
325
- i18n: req.i18n,
326
- logger: req.logger,
327
- }));
328
- }
329
- const botThreshold = clientRecord.settings?.frictionlessThreshold ||
330
- DEFAULT_FRICTIONLESS_THRESHOLD;
331
- const tokenId = await tasks.db.storeFrictionlessTokenRecord({
332
- token,
333
- score: botScore,
334
- threshold: botThreshold,
335
- scoreComponents: {
336
- baseScore: baseBotScore,
337
- ...(lScore && { lScore }),
338
- },
339
- });
340
- if (FrictionlessManager.timestampTooOld(timestamp)) {
341
- await tasks.frictionlessManager.scoreIncreaseTimestamp(timestamp, baseBotScore, botScore, tokenId);
342
- return res.json(await tasks.frictionlessManager.sendImageCaptcha(tokenId));
343
- }
344
- const userScope = getRequestUserScope(flatten(req.headers), req.ja4, req.ip, user);
345
- const userAccessPolicy = (await tasks.frictionlessManager.getPrioritisedAccessPolicies(userAccessRulesStorage, dapp, userScope))[0];
346
- if (userAccessPolicy?.captchaType === CaptchaType.image) {
347
- await tasks.frictionlessManager.scoreIncreaseAccessPolicy(userAccessPolicy, baseBotScore, botScore, tokenId);
348
- return res.json(await tasks.frictionlessManager.sendImageCaptcha(tokenId));
349
- }
350
- if (Number(botScore) > botThreshold) {
351
- req.logger.info(() => ({
352
- message: "Bot score is greater than threshold",
353
- data: {
354
- botScore,
355
- botThreshold,
356
- tokenId,
357
- },
358
- }));
359
- return res.json(await tasks.frictionlessManager.sendImageCaptcha(tokenId));
360
- }
361
- return res.json(await tasks.frictionlessManager.sendPowCaptcha(tokenId));
494
+ if (FrictionlessManager.timestampTooOld(timestamp)) {
495
+ await tasks.frictionlessManager.scoreIncreaseTimestamp(
496
+ timestamp,
497
+ baseBotScore,
498
+ botScore,
499
+ tokenId
500
+ );
501
+ return res.json(
502
+ await tasks.frictionlessManager.sendImageCaptcha(tokenId)
503
+ );
362
504
  }
363
- catch (err) {
364
- req.logger.error(() => ({
365
- err,
366
- msg: "Error in frictionless captcha challenge",
367
- }));
368
- return next(new ProsopoApiError("API.BAD_REQUEST", {
369
- context: { code: 400, error: err },
370
- i18n: req.i18n,
371
- logger: req.logger,
372
- }));
505
+ if (Number(botScore) > botThreshold) {
506
+ req.logger.info(() => ({
507
+ message: "Bot score is greater than threshold",
508
+ data: {
509
+ botScore,
510
+ botThreshold,
511
+ tokenId
512
+ }
513
+ }));
514
+ return res.json(
515
+ await tasks.frictionlessManager.sendImageCaptcha(tokenId)
516
+ );
373
517
  }
374
- });
375
- router.use(handleErrors);
376
- return router;
518
+ return res.json(
519
+ await tasks.frictionlessManager.sendPowCaptcha(tokenId)
520
+ );
521
+ } catch (err) {
522
+ req.logger.error(() => ({
523
+ err,
524
+ msg: "Error in frictionless captcha challenge"
525
+ }));
526
+ return next(
527
+ new ProsopoApiError("API.BAD_REQUEST", {
528
+ context: { code: 400, error: err },
529
+ i18n: req.i18n,
530
+ logger: req.logger
531
+ })
532
+ );
533
+ }
534
+ }
535
+ );
536
+ router.use(handleErrors);
537
+ return router;
377
538
  }
378
- //# sourceMappingURL=captcha.js.map
539
+ export {
540
+ prosopoRouter
541
+ };