@prosopo/provider 3.12.3 → 3.13.0

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 (49) hide show
  1. package/CHANGELOG.md +214 -0
  2. package/dist/api/admin/apiAdminRoutesProvider.js +13 -18
  3. package/dist/api/admin/apiToggleMaintenanceModeEndpoint.js +40 -0
  4. package/dist/api/blacklistRequestInspector.js +4 -4
  5. package/dist/api/captcha/getFrictionlessCaptchaChallenge.js +338 -0
  6. package/dist/api/captcha/getImageCaptchaChallenge.js +150 -0
  7. package/dist/api/captcha/getPoWCaptchaChallenge.js +156 -0
  8. package/dist/api/captcha/submitImageCaptchaSolution.js +87 -0
  9. package/dist/api/captcha/submitPoWCaptchaSolution.js +77 -0
  10. package/dist/api/captcha.js +18 -606
  11. package/dist/api/verify.js +24 -1
  12. package/dist/cjs/api/admin/apiAdminRoutesProvider.cjs +13 -18
  13. package/dist/cjs/api/admin/apiRegisterSiteKeyEndpoint.cjs +2 -1
  14. package/dist/cjs/api/admin/apiRemoveDetectorKeyEndpoint.cjs +3 -2
  15. package/dist/cjs/api/admin/apiToggleMaintenanceModeEndpoint.cjs +41 -0
  16. package/dist/cjs/api/blacklistRequestInspector.cjs +3 -3
  17. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge.cjs +337 -0
  18. package/dist/cjs/api/captcha/getImageCaptchaChallenge.cjs +149 -0
  19. package/dist/cjs/api/captcha/getPoWCaptchaChallenge.cjs +155 -0
  20. package/dist/cjs/api/captcha/submitImageCaptchaSolution.cjs +86 -0
  21. package/dist/cjs/api/captcha/submitPoWCaptchaSolution.cjs +76 -0
  22. package/dist/cjs/api/captcha.cjs +17 -605
  23. package/dist/cjs/api/ja4Middleware.cjs +2 -1
  24. package/dist/cjs/api/verify.cjs +24 -1
  25. package/dist/cjs/index.cjs +2 -0
  26. package/dist/cjs/schedulers/setClientEntropy.cjs +36 -0
  27. package/dist/cjs/tasks/captchaManager.cjs +7 -22
  28. package/dist/cjs/tasks/client/clientTasks.cjs +18 -36
  29. package/dist/cjs/tasks/detection/decodePayload.cjs +385 -714
  30. package/dist/cjs/tasks/detection/getBotScore.cjs +15 -2
  31. package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +136 -30
  32. package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +25 -13
  33. package/dist/cjs/tasks/powCaptcha/powTasks.cjs +8 -8
  34. package/dist/cjs/tasks/tasks.cjs +1 -0
  35. package/dist/cjs/util.cjs +14 -1
  36. package/dist/cjs/utils/hashUserIp.cjs +9 -0
  37. package/dist/index.js +2 -0
  38. package/dist/schedulers/setClientEntropy.js +36 -0
  39. package/dist/tasks/captchaManager.js +5 -21
  40. package/dist/tasks/client/clientTasks.js +19 -37
  41. package/dist/tasks/detection/decodePayload.js +385 -714
  42. package/dist/tasks/detection/getBotScore.js +17 -4
  43. package/dist/tasks/frictionless/frictionlessTasks.js +137 -31
  44. package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +25 -13
  45. package/dist/tasks/powCaptcha/powTasks.js +8 -8
  46. package/dist/tasks/tasks.js +1 -0
  47. package/dist/util.js +14 -1
  48. package/dist/utils/hashUserIp.js +9 -0
  49. package/package.json +24 -25
@@ -1,627 +1,39 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const apiExpressRouter = require("@prosopo/api-express-router");
4
- const common = require("@prosopo/common");
5
- const datasets = require("@prosopo/datasets");
6
4
  const types = require("@prosopo/types");
7
- const util = require("@prosopo/util");
8
5
  const express = require("express");
9
- const compositeIpAddress = require("../compositeIpAddress.cjs");
10
- const frictionlessTasks = require("../tasks/frictionless/frictionlessTasks.cjs");
11
- const frictionlessTasksUtils = require("../tasks/frictionless/frictionlessTasksUtils.cjs");
12
- const tasks = require("../tasks/tasks.cjs");
13
- const hashUserAgent = require("../utils/hashUserAgent.cjs");
14
- const blacklistRequestInspector = require("./blacklistRequestInspector.cjs");
15
- const validateAddress = require("./validateAddress.cjs");
16
- const DEFAULT_FRICTIONLESS_THRESHOLD = 0.5;
6
+ const getFrictionlessCaptchaChallenge = require("./captcha/getFrictionlessCaptchaChallenge.cjs");
7
+ const getImageCaptchaChallenge = require("./captcha/getImageCaptchaChallenge.cjs");
8
+ const getPoWCaptchaChallenge = require("./captcha/getPoWCaptchaChallenge.cjs");
9
+ const submitImageCaptchaSolution = require("./captcha/submitImageCaptchaSolution.cjs");
10
+ const submitPoWCaptchaSolution = require("./captcha/submitPoWCaptchaSolution.cjs");
17
11
  function prosopoRouter(env) {
18
12
  const router = express.Router();
19
13
  const userAccessRulesStorage = env.getDb().getUserAccessRulesStorage();
20
14
  router.post(
21
15
  types.ClientApiPaths.GetImageCaptchaChallenge,
22
- async (req, res, next) => {
23
- const tasks$1 = new tasks.Tasks(env, req.logger);
24
- let parsed;
25
- if (!req.ip) {
26
- return next(
27
- new common.ProsopoApiError("API.BAD_REQUEST", {
28
- context: { code: 400, error: "IP address not found" },
29
- i18n: req.i18n,
30
- logger: req.logger
31
- })
32
- );
33
- }
34
- const ipAddress = util.getIPAddress(req.ip || "");
35
- try {
36
- parsed = types.CaptchaRequestBody.parse(req.body);
37
- } catch (err) {
38
- return next(
39
- new common.ProsopoApiError("CAPTCHA.PARSE_ERROR", {
40
- context: { code: 400, error: err },
41
- i18n: req.i18n,
42
- logger: req.logger
43
- })
44
- );
45
- }
46
- const { datasetId, user, dapp, sessionId } = parsed;
47
- validateAddress.validateSiteKey(dapp);
48
- validateAddress.validateAddr(user);
49
- try {
50
- const clientRecord = await tasks$1.db.getClientRecord(dapp);
51
- if (!clientRecord) {
52
- return next(
53
- new common.ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
54
- context: { code: 400, siteKey: dapp },
55
- i18n: req.i18n,
56
- logger: req.logger
57
- })
58
- );
59
- }
60
- const userScope = blacklistRequestInspector.getRequestUserScope(
61
- util.flatten(req.headers),
62
- req.ja4,
63
- req.ip,
64
- user
65
- );
66
- const userAccessPolicy = (await tasks$1.imgCaptchaManager.getPrioritisedAccessPolicies(
67
- userAccessRulesStorage,
68
- dapp,
69
- userScope
70
- ))[0];
71
- const { valid, reason, frictionlessTokenId, solvedImagesCount } = await tasks$1.imgCaptchaManager.isValidRequest(
72
- clientRecord,
73
- types.CaptchaType.image,
74
- env,
75
- sessionId,
76
- userAccessPolicy,
77
- req.ip
78
- );
79
- if (!valid) {
80
- return next(
81
- new common.ProsopoApiError(reason || "API.BAD_REQUEST", {
82
- context: {
83
- code: 400,
84
- siteKey: dapp,
85
- user
86
- },
87
- i18n: req.i18n,
88
- logger: req.logger
89
- })
90
- );
91
- }
92
- const captchaConfig = {
93
- solved: {
94
- count: solvedImagesCount || userAccessPolicy?.solvedImagesCount || env.config.captchas.solved.count
95
- },
96
- unsolved: {
97
- count: userAccessPolicy?.unsolvedImagesCount || env.config.captchas.unsolved.count
98
- }
99
- };
100
- const taskData = await tasks$1.imgCaptchaManager.getRandomCaptchasAndRequestHash(
101
- datasetId,
102
- user,
103
- ipAddress,
104
- captchaConfig,
105
- clientRecord.settings.imageThreshold ?? 0.8,
106
- frictionlessTokenId
107
- );
108
- const captchaResponse = {
109
- [types.ApiParams.status]: "ok",
110
- [types.ApiParams.captchas]: taskData.captchas.map((captcha) => ({
111
- ...captcha,
112
- target: req.t(`TARGET.${captcha.target}`),
113
- items: captcha.items.map(
114
- (item) => datasets.parseCaptchaAssets(item, env.assetsResolver)
115
- )
116
- })),
117
- [types.ApiParams.requestHash]: taskData.requestHash,
118
- [types.ApiParams.timestamp]: taskData.timestamp.toString(),
119
- [types.ApiParams.signature]: {
120
- [types.ApiParams.provider]: {
121
- [types.ApiParams.requestHash]: taskData.signedRequestHash
122
- }
123
- }
124
- };
125
- req.logger.info(() => ({
126
- msg: "Image captcha challenge issued",
127
- data: {
128
- captchaType: types.CaptchaType.image,
129
- requestHash: taskData.requestHash,
130
- solvedImagesCount: captchaConfig.solved.count,
131
- user,
132
- dapp,
133
- sessionId
134
- }
135
- }));
136
- return res.json(captchaResponse);
137
- } catch (err) {
138
- req.logger.error(() => ({
139
- err,
140
- data: req.params,
141
- msg: "Error in image captcha challenge request"
142
- }));
143
- return next(
144
- new common.ProsopoApiError("API.BAD_REQUEST", {
145
- context: {
146
- error: err,
147
- code: 500,
148
- params: req.params,
149
- context: err
150
- },
151
- i18n: req.i18n,
152
- logger: req.logger
153
- })
154
- );
155
- }
156
- }
16
+ (req, res, next) => getImageCaptchaChallenge(env, userAccessRulesStorage)(req, res, next)
157
17
  );
158
18
  router.post(
159
19
  types.ClientApiPaths.SubmitImageCaptchaSolution,
160
- async (req, res, next) => {
161
- const tasks$1 = new tasks.Tasks(env, req.logger);
162
- let parsed;
163
- try {
164
- parsed = types.CaptchaSolutionBody.parse(req.body);
165
- } catch (err) {
166
- return next(
167
- new common.ProsopoApiError("CAPTCHA.PARSE_ERROR", {
168
- context: { code: 400, error: err, body: req.body },
169
- i18n: req.i18n,
170
- logger: req.logger
171
- })
172
- );
173
- }
174
- const { user, dapp } = parsed;
175
- validateAddress.validateSiteKey(dapp);
176
- validateAddress.validateAddr(user);
177
- try {
178
- const clientRecord = await tasks$1.db.getClientRecord(parsed.dapp);
179
- if (!clientRecord) {
180
- return next(
181
- new common.ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
182
- context: { code: 400, siteKey: dapp },
183
- i18n: req.i18n,
184
- logger: req.logger
185
- })
186
- );
187
- }
188
- const result = await tasks$1.imgCaptchaManager.dappUserSolution(
189
- user,
190
- dapp,
191
- parsed[types.ApiParams.requestHash],
192
- parsed[types.ApiParams.captchas],
193
- parsed[types.ApiParams.signature].user.timestamp,
194
- Number.parseInt(parsed[types.ApiParams.timestamp]),
195
- parsed[types.ApiParams.signature].provider.requestHash,
196
- util.getIPAddress(req.ip || ""),
197
- util.flatten(req.headers),
198
- req.ja4
199
- );
200
- const returnValue = {
201
- status: req.i18n.t(
202
- result.verified ? "API.CAPTCHA_PASSED" : "API.CAPTCHA_FAILED"
203
- ),
204
- ...result
205
- };
206
- return res.json(returnValue);
207
- } catch (err) {
208
- req.logger.error(() => ({
209
- err,
210
- body: req.body,
211
- msg: "Error in image captcha solution submission"
212
- }));
213
- return next(
214
- new common.ProsopoApiError("API.BAD_REQUEST", {
215
- context: {
216
- code: 500,
217
- siteKey: req.body.dapp,
218
- error: err
219
- },
220
- i18n: req.i18n,
221
- logger: req.logger
222
- })
223
- );
224
- }
225
- }
20
+ (req, res, next) => submitImageCaptchaSolution(env, userAccessRulesStorage)(req, res, next)
21
+ );
22
+ router.post(
23
+ types.ClientApiPaths.GetPowCaptchaChallenge,
24
+ (req, res, next) => getPoWCaptchaChallenge(env, userAccessRulesStorage)(req, res, next)
226
25
  );
227
- router.post(types.ClientApiPaths.GetPowCaptchaChallenge, async (req, res, next) => {
228
- let parsed;
229
- const tasks$1 = new tasks.Tasks(env);
230
- tasks$1.setLogger(req.logger);
231
- try {
232
- parsed = types.GetPowCaptchaChallengeRequestBody.parse(req.body);
233
- } catch (err) {
234
- return next(
235
- new common.ProsopoApiError("CAPTCHA.PARSE_ERROR", {
236
- context: { code: 400, error: err },
237
- i18n: req.i18n,
238
- logger: req.logger
239
- })
240
- );
241
- }
242
- const { user, dapp, sessionId } = parsed;
243
- validateAddress.validateSiteKey(dapp);
244
- validateAddress.validateAddr(user);
245
- try {
246
- const clientSettings = await tasks$1.db.getClientRecord(dapp);
247
- if (!clientSettings) {
248
- return next(
249
- new common.ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
250
- context: { code: 400, siteKey: dapp },
251
- i18n: req.i18n,
252
- logger: req.logger
253
- })
254
- );
255
- }
256
- const userScope = blacklistRequestInspector.getRequestUserScope(
257
- util.flatten(req.headers),
258
- req.ja4,
259
- req.ip,
260
- user
261
- );
262
- const userAccessPolicy = (await tasks$1.powCaptchaManager.getPrioritisedAccessPolicies(
263
- userAccessRulesStorage,
264
- dapp,
265
- userScope
266
- ))[0];
267
- const { valid, reason, frictionlessTokenId, powDifficulty } = await tasks$1.powCaptchaManager.isValidRequest(
268
- clientSettings,
269
- types.CaptchaType.pow,
270
- env,
271
- sessionId,
272
- userAccessPolicy,
273
- req.ip
274
- );
275
- if (!valid) {
276
- return next(
277
- new common.ProsopoApiError(reason || "API.BAD_REQUEST", {
278
- context: {
279
- code: 400,
280
- siteKey: dapp,
281
- user
282
- },
283
- i18n: req.i18n,
284
- logger: req.logger
285
- })
286
- );
287
- }
288
- const origin = req.headers.origin;
289
- if (!origin) {
290
- return next(
291
- new common.ProsopoApiError("API.BAD_REQUEST", {
292
- context: {
293
- error: "Origin header not found",
294
- code: 400,
295
- siteKey: dapp,
296
- user
297
- },
298
- i18n: req.i18n,
299
- logger: req.logger
300
- })
301
- );
302
- }
303
- const difficulty = powDifficulty || userAccessPolicy?.powDifficulty || clientSettings?.settings?.powDifficulty;
304
- const challenge = await tasks$1.powCaptchaManager.getPowCaptchaChallenge(
305
- user,
306
- dapp,
307
- origin,
308
- difficulty
309
- );
310
- await tasks$1.db.storePowCaptchaRecord(
311
- challenge.challenge,
312
- {
313
- requestedAtTimestamp: challenge.requestedAtTimestamp,
314
- userAccount: user,
315
- dappAccount: dapp
316
- },
317
- challenge.difficulty,
318
- challenge.providerSignature,
319
- compositeIpAddress.getCompositeIpAddress(req.ip || ""),
320
- util.flatten(req.headers),
321
- req.ja4,
322
- frictionlessTokenId
323
- );
324
- const getPowCaptchaResponse = {
325
- [types.ApiParams.status]: "ok",
326
- [types.ApiParams.challenge]: challenge.challenge,
327
- [types.ApiParams.difficulty]: challenge.difficulty,
328
- [types.ApiParams.timestamp]: challenge.requestedAtTimestamp.toString(),
329
- [types.ApiParams.signature]: {
330
- [types.ApiParams.provider]: {
331
- [types.ApiParams.challenge]: challenge.providerSignature
332
- }
333
- }
334
- };
335
- req.logger.info(() => ({
336
- msg: "PoW captcha challenge issued",
337
- data: {
338
- captchaType: types.CaptchaType.pow,
339
- challenge: challenge.challenge,
340
- difficulty: challenge.difficulty,
341
- user,
342
- dapp,
343
- session: sessionId
344
- }
345
- }));
346
- return res.json(getPowCaptchaResponse);
347
- } catch (err) {
348
- req.logger.error(() => ({
349
- err,
350
- body: req.body,
351
- msg: "Error in PoW captcha challenge request"
352
- }));
353
- return next(
354
- new common.ProsopoApiError("API.BAD_REQUEST", {
355
- context: {
356
- code: 500,
357
- siteKey: req.body.dapp,
358
- user: req.body.user,
359
- error: err
360
- },
361
- i18n: req.i18n,
362
- logger: req.logger
363
- })
364
- );
365
- }
366
- });
367
26
  router.post(
368
27
  types.ClientApiPaths.SubmitPowCaptchaSolution,
369
- async (req, res, next) => {
370
- let parsed;
371
- const tasks$1 = new tasks.Tasks(env, req.logger);
372
- try {
373
- parsed = types.SubmitPowCaptchaSolutionBody.parse(req.body);
374
- } catch (err) {
375
- return next(
376
- new common.ProsopoApiError("CAPTCHA.PARSE_ERROR", {
377
- context: { code: 400, error: err, body: req.body },
378
- i18n: req.i18n,
379
- logger: req.logger
380
- })
381
- );
382
- }
383
- const { challenge, signature, nonce, verifiedTimeout, dapp, user } = parsed;
384
- validateAddress.validateSiteKey(dapp);
385
- validateAddress.validateAddr(user);
386
- try {
387
- const clientRecord = await tasks$1.db.getClientRecord(dapp);
388
- if (!clientRecord) {
389
- return next(
390
- new common.ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
391
- context: { code: 400, siteKey: dapp },
392
- i18n: req.i18n,
393
- logger: req.logger
394
- })
395
- );
396
- }
397
- const verified = await tasks$1.powCaptchaManager.verifyPowCaptchaSolution(
398
- challenge,
399
- signature.provider.challenge,
400
- nonce,
401
- verifiedTimeout,
402
- signature.user.timestamp,
403
- util.getIPAddress(req.ip || ""),
404
- util.flatten(req.headers)
405
- );
406
- const response = { status: "ok", verified };
407
- return res.json(response);
408
- } catch (err) {
409
- req.logger.error(() => ({
410
- err,
411
- body: req.body,
412
- msg: "Error in PoW captcha solution submission"
413
- }));
414
- return next(
415
- new common.ProsopoApiError("API.BAD_REQUEST", {
416
- context: {
417
- code: 500,
418
- siteKey: req.body.dapp,
419
- error: err
420
- },
421
- i18n: req.i18n,
422
- logger: req.logger
423
- })
424
- );
425
- }
426
- }
28
+ (req, res, next) => submitPoWCaptchaSolution(env)(req, res, next)
427
29
  );
428
30
  router.post(
429
31
  types.ClientApiPaths.GetFrictionlessCaptchaChallenge,
430
- async (req, res, next) => {
431
- try {
432
- const tasks$1 = new tasks.Tasks(env, req.logger);
433
- const { token, dapp, user } = types.GetFrictionlessCaptchaChallengeRequestBody.parse(req.body);
434
- const existingToken = await tasks$1.db.getFrictionlessTokenRecordByToken(token);
435
- if (existingToken) {
436
- req.logger.info(() => ({
437
- token: existingToken,
438
- msg: "Token has already been used"
439
- }));
440
- return next(
441
- new common.ProsopoApiError("API.BAD_REQUEST", {
442
- context: {
443
- code: 400,
444
- siteKey: dapp,
445
- user
446
- },
447
- i18n: req.i18n,
448
- logger: req.logger
449
- })
450
- );
451
- }
452
- const lScore = tasks$1.frictionlessManager.checkLangRules(
453
- req.headers["accept-language"] || ""
454
- );
455
- const {
456
- baseBotScore,
457
- timestamp,
458
- providerSelectEntropy,
459
- userId,
460
- userAgent
461
- } = await tasks$1.frictionlessManager.decryptPayload(token);
462
- req.logger.debug(() => ({
463
- msg: "Decrypted payload",
464
- data: {
465
- baseBotScore,
466
- timestamp,
467
- providerSelectEntropy,
468
- userId,
469
- userAgent
470
- }
471
- }));
472
- let botScore = baseBotScore + lScore;
473
- const clientRecord = await tasks$1.db.getClientRecord(dapp);
474
- if (!clientRecord) {
475
- return next(
476
- new common.ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
477
- context: { code: 400, siteKey: dapp },
478
- i18n: req.i18n,
479
- logger: req.logger
480
- })
481
- );
482
- }
483
- const { valid, reason } = await tasks$1.frictionlessManager.isValidRequest(
484
- clientRecord,
485
- types.CaptchaType.frictionless,
486
- env
487
- );
488
- if (!valid) {
489
- return next(
490
- new common.ProsopoApiError(reason || "API.BAD_REQUEST", {
491
- context: {
492
- code: 400,
493
- siteKey: dapp,
494
- user
495
- },
496
- i18n: req.i18n,
497
- logger: req.logger
498
- })
499
- );
500
- }
501
- const botThreshold = clientRecord.settings?.frictionlessThreshold || DEFAULT_FRICTIONLESS_THRESHOLD;
502
- const tokenId = await tasks$1.db.storeFrictionlessTokenRecord({
503
- token,
504
- score: botScore,
505
- threshold: botThreshold,
506
- scoreComponents: {
507
- baseScore: baseBotScore,
508
- ...lScore && { lScore }
509
- },
510
- providerSelectEntropy,
511
- ipAddress: compositeIpAddress.getCompositeIpAddress(req.ip || "")
512
- });
513
- const userScope = blacklistRequestInspector.getRequestUserScope(
514
- util.flatten(req.headers),
515
- req.ja4,
516
- req.ip,
517
- user
518
- );
519
- const userAccessPolicy = (await tasks$1.frictionlessManager.getPrioritisedAccessPolicies(
520
- userAccessRulesStorage,
521
- dapp,
522
- userScope
523
- ))[0];
524
- const headersUserAgent = req.headers["user-agent"];
525
- const hashedHeadersUserAgent = headersUserAgent ? hashUserAgent.hashUserAgent(headersUserAgent) : "";
526
- const headersProsopoUser = req.headers["prosopo-user"];
527
- if (hashedHeadersUserAgent !== userAgent || headersProsopoUser !== userId) {
528
- req.logger.info(() => ({
529
- msg: "User agent or user id does not match",
530
- data: {
531
- headersUserAgent,
532
- hashedHeadersUserAgent,
533
- userAgent,
534
- // This is the hashed user agent from the token
535
- headersProsopoUser,
536
- userId
537
- }
538
- }));
539
- return res.json(
540
- await tasks$1.frictionlessManager.sendImageCaptcha(
541
- tokenId,
542
- frictionlessTasksUtils.timestampDecayFunction(timestamp)
543
- )
544
- );
545
- }
546
- if (userAccessPolicy) {
547
- await tasks$1.frictionlessManager.scoreIncreaseAccessPolicy(
548
- userAccessPolicy,
549
- baseBotScore,
550
- botScore,
551
- tokenId
552
- );
553
- if (userAccessPolicy.captchaType === types.CaptchaType.image) {
554
- return res.json(
555
- await tasks$1.frictionlessManager.sendImageCaptcha(
556
- tokenId,
557
- userAccessPolicy.solvedImagesCount
558
- )
559
- );
560
- }
561
- if (userAccessPolicy.captchaType === types.CaptchaType.pow) {
562
- return res.json(
563
- await tasks$1.frictionlessManager.sendPowCaptcha(tokenId)
564
- );
565
- }
566
- }
567
- if (frictionlessTasks.FrictionlessManager.timestampTooOld(timestamp)) {
568
- await tasks$1.frictionlessManager.scoreIncreaseTimestamp(
569
- timestamp,
570
- baseBotScore,
571
- botScore,
572
- tokenId
573
- );
574
- return res.json(
575
- await tasks$1.frictionlessManager.sendImageCaptcha(
576
- tokenId,
577
- frictionlessTasksUtils.timestampDecayFunction(timestamp)
578
- )
579
- );
580
- }
581
- const hostVerified = await tasks$1.frictionlessManager.hostVerified(
582
- providerSelectEntropy
583
- );
584
- if (!hostVerified.verified) {
585
- botScore = await tasks$1.frictionlessManager.scoreIncreaseUnverifiedHost(
586
- hostVerified.domain,
587
- baseBotScore,
588
- botScore,
589
- tokenId
590
- );
591
- }
592
- if (Number(botScore) > botThreshold) {
593
- req.logger.info(() => ({
594
- msg: "Bot score is greater than threshold",
595
- data: {
596
- botScore,
597
- botThreshold,
598
- tokenId
599
- }
600
- }));
601
- return res.json(
602
- await tasks$1.frictionlessManager.sendImageCaptcha(
603
- tokenId,
604
- env.config.captchas.solved.count
605
- )
606
- );
607
- }
608
- return res.json(
609
- await tasks$1.frictionlessManager.sendPowCaptcha(tokenId)
610
- );
611
- } catch (err) {
612
- req.logger.error(() => ({
613
- err,
614
- msg: "Error in frictionless captcha challenge"
615
- }));
616
- return next(
617
- new common.ProsopoApiError("API.BAD_REQUEST", {
618
- context: { code: 400, error: err },
619
- i18n: req.i18n,
620
- logger: req.logger
621
- })
622
- );
623
- }
624
- }
32
+ (req, res, next) => getFrictionlessCaptchaChallenge(env, userAccessRulesStorage)(
33
+ req,
34
+ res,
35
+ next
36
+ )
625
37
  );
626
38
  router.use(apiExpressRouter.handleErrors);
627
39
  return router;
@@ -5,9 +5,10 @@ const apiExpressRouter = require("@prosopo/api-express-router");
5
5
  const common = require("@prosopo/common");
6
6
  const utilCrypto = require("@prosopo/util-crypto");
7
7
  const readTlsClientHello = require("read-tls-client-hello");
8
+ var _documentCurrentScript = typeof document !== "undefined" ? document.currentScript : null;
8
9
  const DEFAULT_JA4 = "ja4";
9
10
  const getJA4 = async (headers, logger) => {
10
- logger = logger || common.getLogger("info", module);
11
+ logger = logger || common.getLogger("info", typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("api/ja4Middleware.cjs", document.baseURI).href);
11
12
  if (process.env.NODE_ENV === "development") {
12
13
  return {
13
14
  ja4PlusFingerprint: `${DEFAULT_JA4}${utilCrypto.randomAsHex().slice(28, 32)}`
@@ -6,12 +6,23 @@ const types = require("@prosopo/types");
6
6
  const utilCrypto = require("@prosopo/util-crypto");
7
7
  const express = require("express");
8
8
  const tasks = require("../tasks/tasks.cjs");
9
+ const apiToggleMaintenanceModeEndpoint = require("./admin/apiToggleMaintenanceModeEndpoint.cjs");
9
10
  function prosopoVerifyRouter(env) {
10
11
  const router = express.Router();
11
12
  router.post(
12
13
  types.ClientApiPaths.VerifyImageCaptchaSolutionDapp,
13
14
  async (req, res, next) => {
14
15
  const tasks$1 = new tasks.Tasks(env, req.logger);
16
+ if (apiToggleMaintenanceModeEndpoint.getMaintenanceMode()) {
17
+ req.logger.info(() => ({
18
+ msg: "Maintenance mode active - returning verified for image captcha verification"
19
+ }));
20
+ const verificationResponse = {
21
+ status: "ok",
22
+ verified: true
23
+ };
24
+ return res.json(verificationResponse);
25
+ }
15
26
  let parsed;
16
27
  try {
17
28
  parsed = types.VerifySolutionBody.parse(req.body);
@@ -47,7 +58,9 @@ function prosopoVerifyRouter(env) {
47
58
  commitmentId,
48
59
  env,
49
60
  maxVerifiedTime,
50
- ip
61
+ ip,
62
+ clientRecord.settings.disallowWebView,
63
+ clientRecord.settings.contextAware?.enabled
51
64
  );
52
65
  req.logger.debug(() => ({ data: { response } }));
53
66
  const verificationResponse = tasks$1.imgCaptchaManager.getVerificationResponse(
@@ -74,6 +87,16 @@ function prosopoVerifyRouter(env) {
74
87
  types.ClientApiPaths.VerifyPowCaptchaSolution,
75
88
  async (req, res, next) => {
76
89
  const tasks$1 = new tasks.Tasks(env, req.logger);
90
+ if (apiToggleMaintenanceModeEndpoint.getMaintenanceMode()) {
91
+ req.logger.info(() => ({
92
+ msg: "Maintenance mode active - returning verified for PoW captcha verification"
93
+ }));
94
+ const verificationResponse = {
95
+ status: "ok",
96
+ verified: true
97
+ };
98
+ return res.json(verificationResponse);
99
+ }
77
100
  let parsed;
78
101
  try {
79
102
  parsed = types.ServerPowCaptchaVerifyRequestBody.parse(req.body);