@prosopo/provider 4.15.14 → 4.15.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build$colon$cjs.log +106 -106
- package/.turbo/turbo-build$colon$tsc.log +24 -24
- package/.turbo/turbo-build.log +107 -104
- package/CHANGELOG.md +63 -0
- package/dist/_virtual/_rolldown/runtime.js +3 -0
- package/dist/api/admin/apiAdminRoutesProvider.js +27 -46
- package/dist/api/admin/apiClearAllCountersEndpoint.js +62 -57
- package/dist/api/admin/apiDnsEventEndpoint.js +79 -92
- package/dist/api/admin/apiGetAllDecisionMachinesEndpoint.js +43 -45
- package/dist/api/admin/apiGetDecisionMachineEndpoint.js +40 -40
- package/dist/api/admin/apiRegisterSiteKeyEndpoint.js +23 -22
- package/dist/api/admin/apiRegisterSiteKeysEndpoint.js +26 -30
- package/dist/api/admin/apiRemoveAllDecisionMachinesEndpoint.js +35 -37
- package/dist/api/admin/apiRemoveDecisionMachineEndpoint.js +40 -40
- package/dist/api/admin/apiRemoveDetectorKeyEndpoint.d.ts.map +1 -1
- package/dist/api/admin/apiRemoveDetectorKeyEndpoint.js +29 -31
- package/dist/api/admin/apiRemoveSiteKeyEndpoint.js +33 -29
- package/dist/api/admin/apiRemoveSiteKeysEndpoint.js +32 -31
- package/dist/api/admin/apiToggleMaintenanceModeEndpoint.js +43 -35
- package/dist/api/admin/apiUpdateDecisionMachineEndpoint.js +43 -63
- package/dist/api/admin/apiUpdateDetectorKeyEndpoint.js +36 -35
- package/dist/api/admin/createApiAdminRoutesProvider.js +6 -8
- package/dist/api/blacklistRequestInspector.js +285 -377
- package/dist/api/block.js +17 -30
- package/dist/api/captcha/checkSpamEmail.js +88 -98
- package/dist/api/captcha/contextAwareValidation.js +21 -15
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.js +126 -140
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/constants.js +11 -12
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.js +236 -299
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/decryptSimdReadings.js +10 -13
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js +336 -462
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/honeypotResponse.js +17 -14
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/sessionDedup.js +27 -27
- package/dist/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.js +42 -63
- package/dist/api/captcha/getFrictionlessCaptchaChallenge.js +2 -4
- package/dist/api/captcha/getImageCaptchaChallenge.js +136 -207
- package/dist/api/captcha/getPoWCaptchaChallenge.js +128 -198
- package/dist/api/captcha/getPuzzleCaptchaChallenge.js +132 -201
- package/dist/api/captcha/maintenanceModeResponses.js +38 -47
- package/dist/api/captcha/submitImageCaptchaSolution.js +75 -101
- package/dist/api/captcha/submitPoWCaptchaSolution.js +137 -207
- package/dist/api/captcha/submitPuzzleCaptchaSolution.js +73 -105
- package/dist/api/captcha.js +76 -58
- package/dist/api/dnsEventUrl.js +20 -22
- package/dist/api/domainMiddleware.js +104 -115
- package/dist/api/forwardVerify.js +62 -48
- package/dist/api/handshakeTimingMiddleware.js +43 -62
- package/dist/api/hardBlockVerdictCache.js +114 -117
- package/dist/api/headerCheckMiddleware.js +34 -34
- package/dist/api/ignoreMiddleware.js +13 -15
- package/dist/api/ipInfoMiddleware.js +17 -19
- package/dist/api/ja4.js +149 -188
- package/dist/api/ja4Middleware.js +39 -54
- package/dist/api/metrics.js +245 -257
- package/dist/api/public.js +52 -54
- package/dist/api/robotsMiddleware.js +10 -10
- package/dist/api/startProviderApi.js +205 -232
- package/dist/api/testSiteKey.js +16 -16
- package/dist/api/validateAddress.js +23 -21
- package/dist/api/verify.js +334 -377
- package/dist/cjs/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/cjs/api/admin/apiAdminRoutesProvider.cjs +40 -59
- package/dist/cjs/api/admin/apiClearAllCountersEndpoint.cjs +63 -58
- package/dist/cjs/api/admin/apiDnsEventEndpoint.cjs +82 -93
- package/dist/cjs/api/admin/apiGetAllDecisionMachinesEndpoint.cjs +46 -48
- package/dist/cjs/api/admin/apiGetDecisionMachineEndpoint.cjs +41 -41
- package/dist/cjs/api/admin/apiRegisterSiteKeyEndpoint.cjs +25 -24
- package/dist/cjs/api/admin/apiRegisterSiteKeysEndpoint.cjs +28 -32
- package/dist/cjs/api/admin/apiRemoveAllDecisionMachinesEndpoint.cjs +38 -40
- package/dist/cjs/api/admin/apiRemoveDecisionMachineEndpoint.cjs +41 -41
- package/dist/cjs/api/admin/apiRemoveDetectorKeyEndpoint.cjs +31 -33
- package/dist/cjs/api/admin/apiRemoveSiteKeyEndpoint.cjs +35 -31
- package/dist/cjs/api/admin/apiRemoveSiteKeysEndpoint.cjs +34 -33
- package/dist/cjs/api/admin/apiToggleMaintenanceModeEndpoint.cjs +45 -35
- package/dist/cjs/api/admin/apiUpdateDecisionMachineEndpoint.cjs +45 -65
- package/dist/cjs/api/admin/apiUpdateDetectorKeyEndpoint.cjs +38 -37
- package/dist/cjs/api/admin/createApiAdminRoutesProvider.cjs +7 -8
- package/dist/cjs/api/blacklistRequestInspector.cjs +288 -376
- package/dist/cjs/api/block.cjs +18 -30
- package/dist/cjs/api/captcha/checkSpamEmail.cjs +90 -100
- package/dist/cjs/api/captcha/contextAwareValidation.cjs +21 -14
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.cjs +125 -140
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/constants.cjs +10 -10
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.cjs +238 -301
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/decryptSimdReadings.cjs +9 -12
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/handler.cjs +349 -473
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/honeypotResponse.cjs +18 -14
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/sessionDedup.cjs +26 -26
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.cjs +42 -63
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge.cjs +2 -3
- package/dist/cjs/api/captcha/getImageCaptchaChallenge.cjs +138 -208
- package/dist/cjs/api/captcha/getPoWCaptchaChallenge.cjs +131 -200
- package/dist/cjs/api/captcha/getPuzzleCaptchaChallenge.cjs +135 -203
- package/dist/cjs/api/captcha/maintenanceModeResponses.cjs +38 -44
- package/dist/cjs/api/captcha/submitImageCaptchaSolution.cjs +77 -102
- package/dist/cjs/api/captcha/submitPoWCaptchaSolution.cjs +140 -209
- package/dist/cjs/api/captcha/submitPuzzleCaptchaSolution.cjs +76 -107
- package/dist/cjs/api/captcha.cjs +79 -58
- package/dist/cjs/api/dnsEventUrl.cjs +20 -21
- package/dist/cjs/api/domainMiddleware.cjs +106 -117
- package/dist/cjs/api/forwardVerify.cjs +63 -49
- package/dist/cjs/api/handshakeTimingMiddleware.cjs +43 -61
- package/dist/cjs/api/hardBlockVerdictCache.cjs +114 -114
- package/dist/cjs/api/headerCheckMiddleware.cjs +34 -34
- package/dist/cjs/api/ignoreMiddleware.cjs +12 -14
- package/dist/cjs/api/ipInfoMiddleware.cjs +16 -18
- package/dist/cjs/api/ja4.cjs +149 -187
- package/dist/cjs/api/ja4Middleware.cjs +40 -53
- package/dist/cjs/api/metrics.cjs +229 -228
- package/dist/cjs/api/public.cjs +57 -57
- package/dist/cjs/api/robotsMiddleware.cjs +9 -9
- package/dist/cjs/api/startProviderApi.cjs +219 -237
- package/dist/cjs/api/testSiteKey.cjs +15 -14
- package/dist/cjs/api/validateAddress.cjs +24 -21
- package/dist/cjs/api/verify.cjs +338 -379
- package/dist/cjs/compositeIpAddress.cjs +40 -47
- package/dist/cjs/index.cjs +65 -71
- package/dist/cjs/pairs.cjs +31 -35
- package/dist/cjs/rules/lang.cjs +10 -14
- package/dist/cjs/schedulers/captchaScheduler.cjs +26 -32
- package/dist/cjs/schedulers/getClientList.cjs +35 -33
- package/dist/cjs/schedulers/setClientEntropy.cjs +35 -33
- package/dist/cjs/schedulers/updateSpamEmailDomains.cjs +39 -43
- package/dist/cjs/services/ipComparison.cjs +122 -119
- package/dist/cjs/tasks/captchaManager.cjs +407 -486
- package/dist/cjs/tasks/client/clientTasks.cjs +347 -470
- package/dist/cjs/tasks/dataset/datasetTasks.cjs +25 -29
- package/dist/cjs/tasks/dataset/datasetTasksUtils.cjs +16 -32
- package/dist/cjs/tasks/decisionMachine/decisionMachineRunner.cjs +220 -307
- package/dist/cjs/tasks/detection/decodeBehavior.cjs +408 -317
- package/dist/cjs/tasks/detection/decodePayload.cjs +686 -468
- package/dist/cjs/tasks/detection/decodeSimd.cjs +462 -304
- package/dist/cjs/tasks/detection/getBotScore.cjs +37 -43
- package/dist/cjs/tasks/dnsEvent/enrichDnsEvent.cjs +88 -71
- package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +348 -486
- package/dist/cjs/tasks/frictionless/frictionlessTasksUtils.cjs +13 -30
- package/dist/cjs/tasks/frictionless/routingMachine.cjs +71 -55
- package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +555 -806
- package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasksUtils.cjs +22 -23
- package/dist/cjs/tasks/index.cjs +2 -4
- package/dist/cjs/tasks/powCaptcha/powTasks.cjs +491 -687
- package/dist/cjs/tasks/powCaptcha/powTasksUtils.cjs +17 -29
- package/dist/cjs/tasks/puzzleCaptcha/puzzleTasks.cjs +422 -591
- package/dist/cjs/tasks/puzzleCaptcha/puzzleTasksUtils.cjs +8 -5
- package/dist/cjs/tasks/spam/checkSpamEmail.cjs +133 -141
- package/dist/cjs/tasks/spam/checkTrafficFilter.cjs +118 -76
- package/dist/cjs/tasks/spam/evaluateEmailSpamRules.cjs +75 -84
- package/dist/cjs/tasks/spam/updateSpamEmailDomains.cjs +40 -56
- package/dist/cjs/tasks/tasks.cjs +107 -158
- package/dist/cjs/util/usageCounters.cjs +220 -211
- package/dist/cjs/util.cjs +329 -359
- package/dist/cjs/utils/devicePlatform.cjs +14 -8
- package/dist/cjs/utils/dns.cjs +68 -93
- package/dist/cjs/utils/hashUserAgent.cjs +14 -7
- package/dist/cjs/utils/hashUserIp.cjs +14 -6
- package/dist/cjs/utils/honeypot/encoders.cjs +74 -80
- package/dist/cjs/utils/honeypot/phraseBank.cjs +51 -44
- package/dist/cjs/utils/normalizeRequestIp.cjs +33 -25
- package/dist/compositeIpAddress.js +38 -46
- package/dist/index.js +12 -56
- package/dist/pairs.js +31 -37
- package/dist/rules/lang.js +11 -15
- package/dist/schedulers/captchaScheduler.js +25 -31
- package/dist/schedulers/getClientList.js +34 -32
- package/dist/schedulers/setClientEntropy.js +34 -32
- package/dist/schedulers/updateSpamEmailDomains.js +38 -42
- package/dist/services/ipComparison.js +122 -119
- package/dist/tasks/captchaManager.js +403 -483
- package/dist/tasks/client/clientTasks.js +343 -467
- package/dist/tasks/dataset/datasetTasks.js +24 -28
- package/dist/tasks/dataset/datasetTasksUtils.js +15 -31
- package/dist/tasks/decisionMachine/decisionMachineRunner.js +217 -306
- package/dist/tasks/detection/getBotScore.js +37 -43
- package/dist/tasks/dnsEvent/enrichDnsEvent.d.ts.map +1 -1
- package/dist/tasks/dnsEvent/enrichDnsEvent.js +88 -74
- package/dist/tasks/dnsEvent/enrichDnsEvent.js.map +1 -1
- package/dist/tasks/frictionless/frictionlessTasks.js +342 -479
- package/dist/tasks/frictionless/frictionlessTasksUtils.js +14 -32
- package/dist/tasks/frictionless/routingMachine.js +72 -56
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +550 -801
- package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.js +21 -22
- package/dist/tasks/index.js +1 -3
- package/dist/tasks/powCaptcha/powTasks.d.ts.map +1 -1
- package/dist/tasks/powCaptcha/powTasks.js +486 -682
- package/dist/tasks/powCaptcha/powTasks.js.map +1 -1
- package/dist/tasks/powCaptcha/powTasksUtils.js +15 -28
- package/dist/tasks/puzzleCaptcha/puzzleTasks.d.ts.map +1 -1
- package/dist/tasks/puzzleCaptcha/puzzleTasks.js +417 -586
- package/dist/tasks/puzzleCaptcha/puzzleTasks.js.map +1 -1
- package/dist/tasks/puzzleCaptcha/puzzleTasksUtils.js +9 -6
- package/dist/tasks/spam/checkSpamEmail.js +133 -141
- package/dist/tasks/spam/checkTrafficFilter.d.ts +1 -0
- package/dist/tasks/spam/checkTrafficFilter.d.ts.map +1 -1
- package/dist/tasks/spam/checkTrafficFilter.js +117 -77
- package/dist/tasks/spam/checkTrafficFilter.js.map +1 -1
- package/dist/tasks/spam/evaluateEmailSpamRules.js +75 -86
- package/dist/tasks/spam/updateSpamEmailDomains.js +36 -54
- package/dist/tasks/tasks.js +98 -150
- package/dist/tests/integration/api/blacklistRequestInspector.integration.test.js +4 -1
- package/dist/tests/integration/api/blacklistRequestInspector.integration.test.js.map +1 -1
- package/dist/tests/unit/api/block.unit.test.js +8 -4
- package/dist/tests/unit/api/block.unit.test.js.map +1 -1
- package/dist/tests/unit/api/captcha/maintenanceModeShortCircuit.unit.test.js +8 -6
- package/dist/tests/unit/api/captcha/maintenanceModeShortCircuit.unit.test.js.map +1 -1
- package/dist/tests/unit/api/forwardVerify.unit.test.js +3 -1
- package/dist/tests/unit/api/forwardVerify.unit.test.js.map +1 -1
- package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js +15 -7
- package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js.map +1 -1
- package/dist/tests/unit/schedulers/captchaScheduler.unit.test.js +24 -18
- package/dist/tests/unit/schedulers/captchaScheduler.unit.test.js.map +1 -1
- package/dist/tests/unit/schedulers/getClientList.unit.test.js +19 -13
- package/dist/tests/unit/schedulers/getClientList.unit.test.js.map +1 -1
- package/dist/tests/unit/schedulers/setClientEntropy.unit.test.js +19 -13
- package/dist/tests/unit/schedulers/setClientEntropy.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/client/clientTasks.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.js +22 -16
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.js.map +1 -1
- package/dist/tests/unit/tasks/spam/checkTrafficFilter.unit.test.js +140 -0
- package/dist/tests/unit/tasks/spam/checkTrafficFilter.unit.test.js.map +1 -1
- package/dist/tests/unit/testUtils/mockProviderEnv.d.ts +7 -7
- package/dist/util/usageCounters.js +220 -212
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +327 -364
- package/dist/util.js.map +1 -1
- package/dist/utils/devicePlatform.js +15 -9
- package/dist/utils/dns.js +64 -77
- package/dist/utils/hashUserAgent.js +14 -7
- package/dist/utils/hashUserIp.js +14 -6
- package/dist/utils/honeypot/encoders.js +74 -80
- package/dist/utils/honeypot/phraseBank.js +49 -44
- package/dist/utils/normalizeRequestIp.js +34 -26
- package/package.json +24 -24
package/dist/api/captcha.js
CHANGED
|
@@ -1,63 +1,81 @@
|
|
|
1
|
-
import
|
|
1
|
+
import checkSpamEmail_default from "./captcha/checkSpamEmail.js";
|
|
2
|
+
import handler_default from "./captcha/getFrictionlessCaptchaChallenge/handler.js";
|
|
3
|
+
import getImageCaptchaChallenge_default from "./captcha/getImageCaptchaChallenge.js";
|
|
4
|
+
import getPoWCaptchaChallenge_default from "./captcha/getPoWCaptchaChallenge.js";
|
|
5
|
+
import getPuzzleCaptchaChallenge_default from "./captcha/getPuzzleCaptchaChallenge.js";
|
|
6
|
+
import submitImageCaptchaSolution_default from "./captcha/submitImageCaptchaSolution.js";
|
|
7
|
+
import submitPoWCaptchaSolution_default from "./captcha/submitPoWCaptchaSolution.js";
|
|
8
|
+
import submitPuzzleCaptchaSolution_default from "./captcha/submitPuzzleCaptchaSolution.js";
|
|
2
9
|
import { ClientApiPaths } from "@prosopo/types";
|
|
10
|
+
import { handleErrors } from "@prosopo/api-express-router";
|
|
3
11
|
import express from "express";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
//#region src/api/captcha.ts
|
|
13
|
+
/**
|
|
14
|
+
* Wraps an async route handler so that any rejection - including synchronous
|
|
15
|
+
* throws (e.g. from validateSiteKey/validateAddr) that become rejected
|
|
16
|
+
* promises inside the async handler - is forwarded to Express's error handler.
|
|
17
|
+
* Without this, Express 4 does not observe the rejection and the request hangs
|
|
18
|
+
* instead of returning the intended 4xx response.
|
|
19
|
+
*
|
|
20
|
+
* The handler is invoked inside `Promise.resolve().then(...)` so that even a
|
|
21
|
+
* truly synchronous throw (from a non-async handler) is captured as a rejected
|
|
22
|
+
* promise and forwarded to `next`, rather than propagating synchronously.
|
|
23
|
+
*/
|
|
24
|
+
var asyncHandler = (handler) => (req, res, next) => {
|
|
25
|
+
Promise.resolve().then(() => handler(req, res, next)).catch(next);
|
|
15
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* Returns a router connected to the database which can interact with the Proposo protocol
|
|
29
|
+
*
|
|
30
|
+
* @return {Router} - A middleware router that can interact with the Prosopo protocol
|
|
31
|
+
* @param {ProviderEnvironment} env - The Prosopo environment
|
|
32
|
+
*/
|
|
16
33
|
function prosopoRouter(env) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
34
|
+
const router = express.Router();
|
|
35
|
+
let userAccessRulesStorage;
|
|
36
|
+
try {
|
|
37
|
+
userAccessRulesStorage = env.getDb().getUserAccessRulesStorage();
|
|
38
|
+
} catch (err) {
|
|
39
|
+
env.logger.warn(() => ({
|
|
40
|
+
msg: "User access rules storage unavailable; captcha endpoints will skip access-policy checks",
|
|
41
|
+
err
|
|
42
|
+
}));
|
|
43
|
+
userAccessRulesStorage = void 0;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Provides a Captcha puzzle to a Dapp User
|
|
47
|
+
*/
|
|
48
|
+
router.post(ClientApiPaths.GetImageCaptchaChallenge, asyncHandler(getImageCaptchaChallenge_default(env, userAccessRulesStorage)));
|
|
49
|
+
/**
|
|
50
|
+
* Receives solved CAPTCHA challenges from the user, stores to database, and checks against solution commitment
|
|
51
|
+
*/
|
|
52
|
+
router.post(ClientApiPaths.SubmitImageCaptchaSolution, asyncHandler(submitImageCaptchaSolution_default(env)));
|
|
53
|
+
/**
|
|
54
|
+
* Supplies a PoW challenge to a Dapp User
|
|
55
|
+
*/
|
|
56
|
+
router.post(ClientApiPaths.GetPowCaptchaChallenge, asyncHandler(getPoWCaptchaChallenge_default(env, userAccessRulesStorage)));
|
|
57
|
+
/**
|
|
58
|
+
* Verifies a user's PoW solution as being approved or not
|
|
59
|
+
*/
|
|
60
|
+
router.post(ClientApiPaths.SubmitPowCaptchaSolution, asyncHandler(submitPoWCaptchaSolution_default(env)));
|
|
61
|
+
/**
|
|
62
|
+
* Gets a frictionless captcha challenge
|
|
63
|
+
*/
|
|
64
|
+
router.post(ClientApiPaths.GetFrictionlessCaptchaChallenge, asyncHandler(handler_default(env, userAccessRulesStorage)));
|
|
65
|
+
/**
|
|
66
|
+
* Supplies a Puzzle challenge to a Dapp User
|
|
67
|
+
*/
|
|
68
|
+
router.post(ClientApiPaths.GetPuzzleCaptchaChallenge, asyncHandler(getPuzzleCaptchaChallenge_default(env, userAccessRulesStorage)));
|
|
69
|
+
/**
|
|
70
|
+
* Verifies a user's Puzzle solution as being approved or not
|
|
71
|
+
*/
|
|
72
|
+
router.post(ClientApiPaths.SubmitPuzzleCaptchaSolution, asyncHandler(submitPuzzleCaptchaSolution_default(env)));
|
|
73
|
+
/**
|
|
74
|
+
* Checks if an email domain is spam
|
|
75
|
+
*/
|
|
76
|
+
router.post(ClientApiPaths.CheckSpamEmail, asyncHandler(checkSpamEmail_default(env)));
|
|
77
|
+
router.use(handleErrors);
|
|
78
|
+
return router;
|
|
59
79
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
prosopoRouter
|
|
63
|
-
};
|
|
80
|
+
//#endregion
|
|
81
|
+
export { asyncHandler, prosopoRouter };
|
package/dist/api/dnsEventUrl.js
CHANGED
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
import { createHmac } from "node:crypto";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
//#region src/api/dnsEventUrl.ts
|
|
3
|
+
var EXTENSIONS = [
|
|
4
|
+
"gif",
|
|
5
|
+
"png",
|
|
6
|
+
"webp"
|
|
7
|
+
];
|
|
8
|
+
var buildDnsEventUrl = (sessionId) => {
|
|
9
|
+
const subzone = process.env.DNS_EVENT_SUBZONE;
|
|
10
|
+
const secret = process.env.DNS_EVENT_HMAC_SECRET;
|
|
11
|
+
if (!subzone || !secret) return;
|
|
12
|
+
const path = derivePath(sessionId, secret);
|
|
13
|
+
const port = process.env.DNS_EVENT_PORT;
|
|
14
|
+
return `https://${port ? `${sessionId}.${subzone}:${port}` : `${sessionId}.${subzone}`}${path}?sid=${sessionId}`;
|
|
13
15
|
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const ext = EXTENSIONS[(digest.at(8) ?? 0) % EXTENSIONS.length];
|
|
20
|
-
return `/${name}.${ext}`;
|
|
21
|
-
};
|
|
22
|
-
export {
|
|
23
|
-
buildDnsEventUrl,
|
|
24
|
-
derivePath
|
|
16
|
+
var derivePath = (sessionId, secret) => {
|
|
17
|
+
const mac = createHmac("sha256", secret);
|
|
18
|
+
mac.update(sessionId);
|
|
19
|
+
const digest = mac.digest();
|
|
20
|
+
return `/${digest.subarray(0, 8).toString("hex")}.${EXTENSIONS[(digest.at(8) ?? 0) % EXTENSIONS.length]}`;
|
|
25
21
|
};
|
|
22
|
+
//#endregion
|
|
23
|
+
export { buildDnsEventUrl, derivePath };
|
|
@@ -1,124 +1,113 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { recordDomainValidation } from "./metrics.js";
|
|
2
|
+
import { Tasks } from "../tasks/tasks.js";
|
|
3
|
+
import { getMaintenanceMode } from "./admin/apiToggleMaintenanceModeEndpoint.js";
|
|
4
|
+
import { isReservedTestSiteKey } from "./testSiteKey.js";
|
|
2
5
|
import { ProsopoApiError } from "@prosopo/common";
|
|
3
|
-
import {
|
|
6
|
+
import { decodeGoogleTranslateHost, parseUrl } from "@prosopo/util";
|
|
4
7
|
import { validateAddress } from "@prosopo/util-crypto";
|
|
5
8
|
import { ZodError } from "zod";
|
|
6
|
-
import "
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
throw unauthorizedOriginError(req.i18n, void 0, req.logger);
|
|
57
|
-
const candidateOrigins = [origin, ...googleTranslateOrigins(origin)];
|
|
58
|
-
for (const candidate of candidateOrigins) {
|
|
59
|
-
for (const domain of allowedDomains) {
|
|
60
|
-
if (tasks.clientTaskManager.domainPatternMatcher(candidate, domain)) {
|
|
61
|
-
recordDomainValidation("allowed");
|
|
62
|
-
next();
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
throw unauthorizedOriginError(req.i18n, origin, req.logger);
|
|
68
|
-
} catch (err) {
|
|
69
|
-
recordDomainValidation("rejected");
|
|
70
|
-
if (err instanceof ProsopoApiError || err instanceof ZodError || err instanceof SyntaxError) {
|
|
71
|
-
handleErrors(err, req, res, next);
|
|
72
|
-
} else {
|
|
73
|
-
res.status(401).json({ error: "Unauthorized", message: err });
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
};
|
|
79
|
-
const googleTranslateOrigins = (origin) => {
|
|
80
|
-
try {
|
|
81
|
-
const url = parseUrl(origin);
|
|
82
|
-
const decodedHost = decodeGoogleTranslateHost(url.hostname);
|
|
83
|
-
if (!decodedHost) return [];
|
|
84
|
-
return [`${url.protocol}//${decodedHost}`];
|
|
85
|
-
} catch {
|
|
86
|
-
return [];
|
|
87
|
-
}
|
|
9
|
+
import { handleErrors } from "@prosopo/api-express-router";
|
|
10
|
+
//#region src/api/domainMiddleware.ts
|
|
11
|
+
var domainMiddleware = (env) => {
|
|
12
|
+
let cachedTasks;
|
|
13
|
+
return async (req, res, next) => {
|
|
14
|
+
try {
|
|
15
|
+
if (getMaintenanceMode()) {
|
|
16
|
+
req.logger.info(() => ({ msg: "Maintenance mode active - skipping domain validation" }));
|
|
17
|
+
next();
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (!cachedTasks) cachedTasks = new Tasks(env);
|
|
21
|
+
const tasks = cachedTasks;
|
|
22
|
+
const siteKey = req.headers["prosopo-site-key"];
|
|
23
|
+
if (!siteKey) throw siteKeyNotRegisteredError(req.i18n, "No sitekey provided", req.logger);
|
|
24
|
+
try {
|
|
25
|
+
validateAddress(siteKey, false, 42);
|
|
26
|
+
} catch (err) {
|
|
27
|
+
throw invalidSiteKeyError(req.i18n, siteKey, req.logger);
|
|
28
|
+
}
|
|
29
|
+
if (isReservedTestSiteKey(siteKey)) {
|
|
30
|
+
recordDomainValidation("test_key");
|
|
31
|
+
next();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const clientSettings = await tasks.db.getClientRecord(siteKey);
|
|
35
|
+
if (!clientSettings) throw siteKeyNotRegisteredError(req.i18n, siteKey, req.logger);
|
|
36
|
+
const allowedDomains = clientSettings.settings?.domains;
|
|
37
|
+
if (!allowedDomains) throw siteKeyInvalidDomainError(req.i18n, siteKey, req.hostname, req.logger);
|
|
38
|
+
const origin = req.headers.origin;
|
|
39
|
+
if (!origin) throw unauthorizedOriginError(req.i18n, void 0, req.logger);
|
|
40
|
+
const candidateOrigins = [origin, ...googleTranslateOrigins(origin)];
|
|
41
|
+
for (const candidate of candidateOrigins) for (const domain of allowedDomains) if (tasks.clientTaskManager.domainPatternMatcher(candidate, domain)) {
|
|
42
|
+
recordDomainValidation("allowed");
|
|
43
|
+
next();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
throw unauthorizedOriginError(req.i18n, origin, req.logger);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
recordDomainValidation("rejected");
|
|
49
|
+
if (err instanceof ProsopoApiError || err instanceof ZodError || err instanceof SyntaxError) handleErrors(err, req, res, next);
|
|
50
|
+
else {
|
|
51
|
+
res.status(401).json({
|
|
52
|
+
error: "Unauthorized",
|
|
53
|
+
message: err
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
88
59
|
};
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
60
|
+
var googleTranslateOrigins = (origin) => {
|
|
61
|
+
try {
|
|
62
|
+
const url = parseUrl(origin);
|
|
63
|
+
const decodedHost = decodeGoogleTranslateHost(url.hostname);
|
|
64
|
+
if (!decodedHost) return [];
|
|
65
|
+
return [`${url.protocol}//${decodedHost}`];
|
|
66
|
+
} catch {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
95
69
|
};
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
70
|
+
var siteKeyNotRegisteredError = (i18n, dapp, logger) => {
|
|
71
|
+
return new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
|
|
72
|
+
context: {
|
|
73
|
+
code: 400,
|
|
74
|
+
siteKey: dapp
|
|
75
|
+
},
|
|
76
|
+
i18n,
|
|
77
|
+
logger
|
|
78
|
+
});
|
|
102
79
|
};
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
80
|
+
var invalidSiteKeyError = (i18n, siteKey, logger) => {
|
|
81
|
+
return new ProsopoApiError("API.INVALID_SITE_KEY", {
|
|
82
|
+
context: {
|
|
83
|
+
code: 400,
|
|
84
|
+
siteKey
|
|
85
|
+
},
|
|
86
|
+
i18n,
|
|
87
|
+
logger
|
|
88
|
+
});
|
|
109
89
|
};
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
logger
|
|
120
|
-
});
|
|
90
|
+
var unauthorizedOriginError = (i18n, origin, logger) => {
|
|
91
|
+
return new ProsopoApiError("API.UNAUTHORIZED_ORIGIN_URL", {
|
|
92
|
+
context: {
|
|
93
|
+
code: 400,
|
|
94
|
+
origin
|
|
95
|
+
},
|
|
96
|
+
i18n,
|
|
97
|
+
logger
|
|
98
|
+
});
|
|
121
99
|
};
|
|
122
|
-
|
|
123
|
-
|
|
100
|
+
var siteKeyInvalidDomainError = (i18n, dapp, domain, logger) => {
|
|
101
|
+
return new ProsopoApiError("API.UNAUTHORIZED_ORIGIN_URL", {
|
|
102
|
+
context: {
|
|
103
|
+
code: 400,
|
|
104
|
+
message: "No domains are allowed for this site key. Please fix in the Procaptcha Portal",
|
|
105
|
+
siteKey: dapp,
|
|
106
|
+
domain
|
|
107
|
+
},
|
|
108
|
+
i18n,
|
|
109
|
+
logger
|
|
110
|
+
});
|
|
124
111
|
};
|
|
112
|
+
//#endregion
|
|
113
|
+
export { domainMiddleware };
|
|
@@ -1,54 +1,68 @@
|
|
|
1
1
|
import { ProviderApi } from "@prosopo/api";
|
|
2
2
|
import { getProviders } from "@prosopo/load-balancer";
|
|
3
|
+
//#region src/api/forwardVerify.ts
|
|
4
|
+
/**
|
|
5
|
+
* A client can send a verify request to *any* pronode, not necessarily the
|
|
6
|
+
* provider that issued the token. This determines the issuing provider from the
|
|
7
|
+
* `providerUrl` embedded in the token and, when it is a *different* provider to
|
|
8
|
+
* this one, forwards the verification request to it and returns its response —
|
|
9
|
+
* mirroring the AWS Lambda verify endpoint, but running on a provider.
|
|
10
|
+
*
|
|
11
|
+
* Returns `null` when this provider is the issuer (or the issuer cannot be
|
|
12
|
+
* determined), signalling the caller to verify locally as before.
|
|
13
|
+
*
|
|
14
|
+
* @param env - The provider environment
|
|
15
|
+
* @param logger - Request-scoped logger
|
|
16
|
+
* @param path - The verify path to forward to (kept identical, so the issuer
|
|
17
|
+
* routes the request to the correct captcha-type verification)
|
|
18
|
+
* @param providerUrl - The issuing provider url, decoded from the token
|
|
19
|
+
* @param dapp - The site key (used as the `Prosopo-Site-Key` header)
|
|
20
|
+
* @param user - The user account (used as the `Prosopo-User` header)
|
|
21
|
+
* @param body - The original, already-parsed request body, forwarded verbatim
|
|
22
|
+
*/
|
|
3
23
|
function normalizeProviderUrl(url) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
24
|
+
try {
|
|
25
|
+
const parsed = new URL(url);
|
|
26
|
+
parsed.hostname = parsed.hostname.replace(/^(ipv4|ipv6)\./, "");
|
|
27
|
+
return parsed.toString().replace(/\/$/, "");
|
|
28
|
+
} catch {
|
|
29
|
+
return url.replace(/\/$/, "");
|
|
30
|
+
}
|
|
11
31
|
}
|
|
12
32
|
async function forwardVerifyIfNotIssuer(args) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
msg: "Forwarding verify request to issuing provider",
|
|
47
|
-
data: { providerUrl, path, dapp }
|
|
48
|
-
}));
|
|
49
|
-
const providerApi = new ProviderApi(issuer.url, dapp);
|
|
50
|
-
return providerApi.forwardVerify(path, body, user);
|
|
33
|
+
const { env, logger, path, providerUrl, dapp, user, body, alreadyForwarded } = args;
|
|
34
|
+
if (alreadyForwarded) return null;
|
|
35
|
+
if (!providerUrl) return null;
|
|
36
|
+
let providers;
|
|
37
|
+
try {
|
|
38
|
+
providers = await getProviders(env.defaultEnvironment);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
logger.warn(() => ({
|
|
41
|
+
msg: "Failed to load provider list; verifying locally",
|
|
42
|
+
err: error instanceof Error ? error : new Error(String(error)),
|
|
43
|
+
data: { providerUrl }
|
|
44
|
+
}));
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const normalizedProviderUrl = normalizeProviderUrl(providerUrl);
|
|
48
|
+
const issuer = providers.find((provider) => normalizeProviderUrl(provider.url) === normalizedProviderUrl);
|
|
49
|
+
if (!issuer) {
|
|
50
|
+
logger.warn(() => ({
|
|
51
|
+
msg: "Token providerUrl is not a known provider; verifying locally",
|
|
52
|
+
data: { providerUrl }
|
|
53
|
+
}));
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
if (env.pair?.address && issuer.address === env.pair.address) return null;
|
|
57
|
+
logger.info(() => ({
|
|
58
|
+
msg: "Forwarding verify request to issuing provider",
|
|
59
|
+
data: {
|
|
60
|
+
providerUrl,
|
|
61
|
+
path,
|
|
62
|
+
dapp
|
|
63
|
+
}
|
|
64
|
+
}));
|
|
65
|
+
return new ProviderApi(issuer.url, dapp).forwardVerify(path, body, user);
|
|
51
66
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
};
|
|
67
|
+
//#endregion
|
|
68
|
+
export { forwardVerifyIfNotIssuer };
|
|
@@ -1,66 +1,47 @@
|
|
|
1
|
-
import { handleErrors } from "@prosopo/api-express-router";
|
|
2
1
|
import { getLogger } from "@prosopo/logger";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const getHandshakeTiming = (headers, logger) => {
|
|
24
|
-
const log = logger ?? getLogger("info", "provider:handshake-timing");
|
|
25
|
-
return {
|
|
26
|
-
tcpToChelloUs: parseTimingHeader(
|
|
27
|
-
headers[HEADER_TCP_TO_CHELLO],
|
|
28
|
-
log,
|
|
29
|
-
HEADER_TCP_TO_CHELLO
|
|
30
|
-
),
|
|
31
|
-
chelloToHandshakeUs: parseTimingHeader(
|
|
32
|
-
headers[HEADER_CHELLO_TO_HANDSHAKE],
|
|
33
|
-
log,
|
|
34
|
-
HEADER_CHELLO_TO_HANDSHAKE
|
|
35
|
-
)
|
|
36
|
-
};
|
|
2
|
+
import { handleErrors } from "@prosopo/api-express-router";
|
|
3
|
+
//#region src/api/handshakeTimingMiddleware.ts
|
|
4
|
+
var HEADER_TCP_TO_CHELLO = "x-tls-tcp-to-chello-us";
|
|
5
|
+
var HEADER_CHELLO_TO_HANDSHAKE = "x-tls-chello-to-handshake-us";
|
|
6
|
+
var parseTimingHeader = (raw, logger, headerName) => {
|
|
7
|
+
if (raw === void 0) return;
|
|
8
|
+
const value = Array.isArray(raw) ? raw[0] : raw;
|
|
9
|
+
if (value === void 0) return;
|
|
10
|
+
const parsed = Number.parseInt(value, 10);
|
|
11
|
+
if (Number.isNaN(parsed) || parsed < 0) {
|
|
12
|
+
logger.debug(() => ({
|
|
13
|
+
msg: "Ignoring malformed handshake timing header",
|
|
14
|
+
data: {
|
|
15
|
+
header: headerName,
|
|
16
|
+
raw: value
|
|
17
|
+
}
|
|
18
|
+
}));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
return parsed;
|
|
37
22
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
if (timing.chelloToHandshakeUs !== void 0) {
|
|
46
|
-
req.chelloToHandshakeUs = timing.chelloToHandshakeUs;
|
|
47
|
-
}
|
|
48
|
-
if (timing.tcpToChelloUs !== void 0 || timing.chelloToHandshakeUs !== void 0) {
|
|
49
|
-
req.logger = req.logger.with(
|
|
50
|
-
{
|
|
51
|
-
tcpToChelloUs: timing.tcpToChelloUs,
|
|
52
|
-
chelloToHandshakeUs: timing.chelloToHandshakeUs
|
|
53
|
-
},
|
|
54
|
-
"handshakeTiming"
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
next();
|
|
58
|
-
} catch (err) {
|
|
59
|
-
return handleErrors(err, req, res, next);
|
|
60
|
-
}
|
|
61
|
-
};
|
|
23
|
+
var getHandshakeTiming = (headers, logger) => {
|
|
24
|
+
const log = logger ?? getLogger("info", "provider:handshake-timing");
|
|
25
|
+
return {
|
|
26
|
+
tcpToChelloUs: parseTimingHeader(headers[HEADER_TCP_TO_CHELLO], log, HEADER_TCP_TO_CHELLO),
|
|
27
|
+
chelloToHandshakeUs: parseTimingHeader(headers[HEADER_CHELLO_TO_HANDSHAKE], log, HEADER_CHELLO_TO_HANDSHAKE)
|
|
28
|
+
};
|
|
62
29
|
};
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
30
|
+
var handshakeTimingMiddleware = (env) => {
|
|
31
|
+
return async (req, res, next) => {
|
|
32
|
+
try {
|
|
33
|
+
const timing = getHandshakeTiming(req.headers, req.logger);
|
|
34
|
+
if (timing.tcpToChelloUs !== void 0) req.tcpToChelloUs = timing.tcpToChelloUs;
|
|
35
|
+
if (timing.chelloToHandshakeUs !== void 0) req.chelloToHandshakeUs = timing.chelloToHandshakeUs;
|
|
36
|
+
if (timing.tcpToChelloUs !== void 0 || timing.chelloToHandshakeUs !== void 0) req.logger = req.logger.with({
|
|
37
|
+
tcpToChelloUs: timing.tcpToChelloUs,
|
|
38
|
+
chelloToHandshakeUs: timing.chelloToHandshakeUs
|
|
39
|
+
}, "handshakeTiming");
|
|
40
|
+
next();
|
|
41
|
+
} catch (err) {
|
|
42
|
+
return handleErrors(err, req, res, next);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
66
45
|
};
|
|
46
|
+
//#endregion
|
|
47
|
+
export { getHandshakeTiming, handshakeTimingMiddleware };
|