@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.
- package/CHANGELOG.md +214 -0
- package/dist/api/admin/apiAdminRoutesProvider.js +13 -18
- package/dist/api/admin/apiToggleMaintenanceModeEndpoint.js +40 -0
- package/dist/api/blacklistRequestInspector.js +4 -4
- package/dist/api/captcha/getFrictionlessCaptchaChallenge.js +338 -0
- package/dist/api/captcha/getImageCaptchaChallenge.js +150 -0
- package/dist/api/captcha/getPoWCaptchaChallenge.js +156 -0
- package/dist/api/captcha/submitImageCaptchaSolution.js +87 -0
- package/dist/api/captcha/submitPoWCaptchaSolution.js +77 -0
- package/dist/api/captcha.js +18 -606
- package/dist/api/verify.js +24 -1
- package/dist/cjs/api/admin/apiAdminRoutesProvider.cjs +13 -18
- package/dist/cjs/api/admin/apiRegisterSiteKeyEndpoint.cjs +2 -1
- package/dist/cjs/api/admin/apiRemoveDetectorKeyEndpoint.cjs +3 -2
- package/dist/cjs/api/admin/apiToggleMaintenanceModeEndpoint.cjs +41 -0
- package/dist/cjs/api/blacklistRequestInspector.cjs +3 -3
- package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge.cjs +337 -0
- package/dist/cjs/api/captcha/getImageCaptchaChallenge.cjs +149 -0
- package/dist/cjs/api/captcha/getPoWCaptchaChallenge.cjs +155 -0
- package/dist/cjs/api/captcha/submitImageCaptchaSolution.cjs +86 -0
- package/dist/cjs/api/captcha/submitPoWCaptchaSolution.cjs +76 -0
- package/dist/cjs/api/captcha.cjs +17 -605
- package/dist/cjs/api/ja4Middleware.cjs +2 -1
- package/dist/cjs/api/verify.cjs +24 -1
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/schedulers/setClientEntropy.cjs +36 -0
- package/dist/cjs/tasks/captchaManager.cjs +7 -22
- package/dist/cjs/tasks/client/clientTasks.cjs +18 -36
- package/dist/cjs/tasks/detection/decodePayload.cjs +385 -714
- package/dist/cjs/tasks/detection/getBotScore.cjs +15 -2
- package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +136 -30
- package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +25 -13
- package/dist/cjs/tasks/powCaptcha/powTasks.cjs +8 -8
- package/dist/cjs/tasks/tasks.cjs +1 -0
- package/dist/cjs/util.cjs +14 -1
- package/dist/cjs/utils/hashUserIp.cjs +9 -0
- package/dist/index.js +2 -0
- package/dist/schedulers/setClientEntropy.js +36 -0
- package/dist/tasks/captchaManager.js +5 -21
- package/dist/tasks/client/clientTasks.js +19 -37
- package/dist/tasks/detection/decodePayload.js +385 -714
- package/dist/tasks/detection/getBotScore.js +17 -4
- package/dist/tasks/frictionless/frictionlessTasks.js +137 -31
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +25 -13
- package/dist/tasks/powCaptcha/powTasks.js +8 -8
- package/dist/tasks/tasks.js +1 -0
- package/dist/util.js +14 -1
- package/dist/utils/hashUserIp.js +9 -0
- package/package.json +24 -25
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const common = require("@prosopo/common");
|
|
3
|
+
const types = require("@prosopo/types");
|
|
4
|
+
const util = require("@prosopo/util");
|
|
5
|
+
const compositeIpAddress = require("../../compositeIpAddress.cjs");
|
|
6
|
+
require("../../tasks/index.cjs");
|
|
7
|
+
const blacklistRequestInspector = require("../blacklistRequestInspector.cjs");
|
|
8
|
+
const validateAddress = require("../validateAddress.cjs");
|
|
9
|
+
const tasks = require("../../tasks/tasks.cjs");
|
|
10
|
+
const getPoWCaptchaChallenge = (env, userAccessRulesStorage) => async (req, res, next) => {
|
|
11
|
+
let parsed;
|
|
12
|
+
const tasks$1 = new tasks.Tasks(env);
|
|
13
|
+
tasks$1.setLogger(req.logger);
|
|
14
|
+
try {
|
|
15
|
+
parsed = types.GetPowCaptchaChallengeRequestBody.parse(req.body);
|
|
16
|
+
} catch (err) {
|
|
17
|
+
return next(
|
|
18
|
+
new common.ProsopoApiError("CAPTCHA.PARSE_ERROR", {
|
|
19
|
+
context: { code: 400, error: err },
|
|
20
|
+
i18n: req.i18n,
|
|
21
|
+
logger: req.logger
|
|
22
|
+
})
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
const { user, dapp, sessionId } = parsed;
|
|
26
|
+
validateAddress.validateSiteKey(dapp);
|
|
27
|
+
validateAddress.validateAddr(user);
|
|
28
|
+
try {
|
|
29
|
+
const clientSettings = await tasks$1.db.getClientRecord(dapp);
|
|
30
|
+
if (!clientSettings) {
|
|
31
|
+
return next(
|
|
32
|
+
new common.ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
|
|
33
|
+
context: { code: 400, siteKey: dapp },
|
|
34
|
+
i18n: req.i18n,
|
|
35
|
+
logger: req.logger
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
const userScope = blacklistRequestInspector.getRequestUserScope(
|
|
40
|
+
util.flatten(req.headers),
|
|
41
|
+
req.ja4,
|
|
42
|
+
req.ip,
|
|
43
|
+
user
|
|
44
|
+
);
|
|
45
|
+
const userAccessPolicy = (await tasks$1.powCaptchaManager.getPrioritisedAccessPolicies(
|
|
46
|
+
userAccessRulesStorage,
|
|
47
|
+
dapp,
|
|
48
|
+
userScope
|
|
49
|
+
))[0];
|
|
50
|
+
const {
|
|
51
|
+
valid,
|
|
52
|
+
reason,
|
|
53
|
+
sessionId: validSessionId,
|
|
54
|
+
powDifficulty
|
|
55
|
+
} = await tasks$1.powCaptchaManager.isValidRequest(
|
|
56
|
+
clientSettings,
|
|
57
|
+
types.CaptchaType.pow,
|
|
58
|
+
env,
|
|
59
|
+
sessionId,
|
|
60
|
+
userAccessPolicy,
|
|
61
|
+
req.ip
|
|
62
|
+
);
|
|
63
|
+
if (!valid) {
|
|
64
|
+
return next(
|
|
65
|
+
new common.ProsopoApiError(reason || "API.BAD_REQUEST", {
|
|
66
|
+
context: {
|
|
67
|
+
code: 400,
|
|
68
|
+
siteKey: dapp,
|
|
69
|
+
user
|
|
70
|
+
},
|
|
71
|
+
i18n: req.i18n,
|
|
72
|
+
logger: req.logger
|
|
73
|
+
})
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
const origin = req.headers.origin;
|
|
77
|
+
if (!origin) {
|
|
78
|
+
return next(
|
|
79
|
+
new common.ProsopoApiError("API.BAD_REQUEST", {
|
|
80
|
+
context: {
|
|
81
|
+
error: "Origin header not found",
|
|
82
|
+
code: 400,
|
|
83
|
+
siteKey: dapp,
|
|
84
|
+
user
|
|
85
|
+
},
|
|
86
|
+
i18n: req.i18n,
|
|
87
|
+
logger: req.logger
|
|
88
|
+
})
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
const difficulty = powDifficulty || userAccessPolicy?.powDifficulty || clientSettings?.settings?.powDifficulty;
|
|
92
|
+
const challenge = await tasks$1.powCaptchaManager.getPowCaptchaChallenge(
|
|
93
|
+
user,
|
|
94
|
+
dapp,
|
|
95
|
+
origin,
|
|
96
|
+
difficulty
|
|
97
|
+
);
|
|
98
|
+
await tasks$1.db.storePowCaptchaRecord(
|
|
99
|
+
challenge.challenge,
|
|
100
|
+
{
|
|
101
|
+
requestedAtTimestamp: challenge.requestedAtTimestamp,
|
|
102
|
+
userAccount: user,
|
|
103
|
+
dappAccount: dapp
|
|
104
|
+
},
|
|
105
|
+
challenge.difficulty,
|
|
106
|
+
challenge.providerSignature,
|
|
107
|
+
compositeIpAddress.getCompositeIpAddress(req.ip || ""),
|
|
108
|
+
util.flatten(req.headers),
|
|
109
|
+
req.ja4,
|
|
110
|
+
validSessionId
|
|
111
|
+
);
|
|
112
|
+
const getPowCaptchaResponse = {
|
|
113
|
+
[types.ApiParams.status]: "ok",
|
|
114
|
+
[types.ApiParams.challenge]: challenge.challenge,
|
|
115
|
+
[types.ApiParams.difficulty]: challenge.difficulty,
|
|
116
|
+
[types.ApiParams.timestamp]: challenge.requestedAtTimestamp.toString(),
|
|
117
|
+
[types.ApiParams.signature]: {
|
|
118
|
+
[types.ApiParams.provider]: {
|
|
119
|
+
[types.ApiParams.challenge]: challenge.providerSignature
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
req.logger.info(() => ({
|
|
124
|
+
msg: "PoW captcha challenge issued",
|
|
125
|
+
data: {
|
|
126
|
+
captchaType: types.CaptchaType.pow,
|
|
127
|
+
challenge: challenge.challenge,
|
|
128
|
+
difficulty: challenge.difficulty,
|
|
129
|
+
user,
|
|
130
|
+
dapp,
|
|
131
|
+
session: sessionId
|
|
132
|
+
}
|
|
133
|
+
}));
|
|
134
|
+
return res.json(getPowCaptchaResponse);
|
|
135
|
+
} catch (err) {
|
|
136
|
+
req.logger.error(() => ({
|
|
137
|
+
err,
|
|
138
|
+
body: req.body,
|
|
139
|
+
msg: "Error in PoW captcha challenge request"
|
|
140
|
+
}));
|
|
141
|
+
return next(
|
|
142
|
+
new common.ProsopoApiError("API.BAD_REQUEST", {
|
|
143
|
+
context: {
|
|
144
|
+
code: 500,
|
|
145
|
+
siteKey: req.body.dapp,
|
|
146
|
+
user: req.body.user,
|
|
147
|
+
error: err
|
|
148
|
+
},
|
|
149
|
+
i18n: req.i18n,
|
|
150
|
+
logger: req.logger
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
module.exports = getPoWCaptchaChallenge;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const common = require("@prosopo/common");
|
|
3
|
+
const types = require("@prosopo/types");
|
|
4
|
+
const util = require("@prosopo/util");
|
|
5
|
+
require("../../tasks/index.cjs");
|
|
6
|
+
const apiToggleMaintenanceModeEndpoint = require("../admin/apiToggleMaintenanceModeEndpoint.cjs");
|
|
7
|
+
const validateAddress = require("../validateAddress.cjs");
|
|
8
|
+
const tasks = require("../../tasks/tasks.cjs");
|
|
9
|
+
const submitImageCaptchaSolution = (env, userAccessRulesStorage) => async (req, res, next) => {
|
|
10
|
+
const tasks$1 = new tasks.Tasks(env, req.logger);
|
|
11
|
+
if (apiToggleMaintenanceModeEndpoint.getMaintenanceMode()) {
|
|
12
|
+
req.logger.info(() => ({
|
|
13
|
+
msg: "Maintenance mode active - returning verified for image captcha"
|
|
14
|
+
}));
|
|
15
|
+
const result = {
|
|
16
|
+
status: "ok",
|
|
17
|
+
captchas: [],
|
|
18
|
+
verified: true
|
|
19
|
+
};
|
|
20
|
+
return res.json(result);
|
|
21
|
+
}
|
|
22
|
+
let parsed;
|
|
23
|
+
try {
|
|
24
|
+
parsed = types.CaptchaSolutionBody.parse(req.body);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
return next(
|
|
27
|
+
new common.ProsopoApiError("CAPTCHA.PARSE_ERROR", {
|
|
28
|
+
context: { code: 400, error: err, body: req.body },
|
|
29
|
+
i18n: req.i18n,
|
|
30
|
+
logger: req.logger
|
|
31
|
+
})
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
const { user, dapp } = parsed;
|
|
35
|
+
validateAddress.validateSiteKey(dapp);
|
|
36
|
+
validateAddress.validateAddr(user);
|
|
37
|
+
try {
|
|
38
|
+
const clientRecord = await tasks$1.db.getClientRecord(parsed.dapp);
|
|
39
|
+
if (!clientRecord) {
|
|
40
|
+
return next(
|
|
41
|
+
new common.ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
|
|
42
|
+
context: { code: 400, siteKey: dapp },
|
|
43
|
+
i18n: req.i18n,
|
|
44
|
+
logger: req.logger
|
|
45
|
+
})
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const result = await tasks$1.imgCaptchaManager.dappUserSolution(
|
|
49
|
+
user,
|
|
50
|
+
dapp,
|
|
51
|
+
parsed[types.ApiParams.requestHash],
|
|
52
|
+
parsed[types.ApiParams.captchas],
|
|
53
|
+
parsed[types.ApiParams.signature].user.timestamp,
|
|
54
|
+
Number.parseInt(parsed[types.ApiParams.timestamp]),
|
|
55
|
+
parsed[types.ApiParams.signature].provider.requestHash,
|
|
56
|
+
util.getIPAddress(req.ip || ""),
|
|
57
|
+
util.flatten(req.headers),
|
|
58
|
+
req.ja4
|
|
59
|
+
);
|
|
60
|
+
const returnValue = {
|
|
61
|
+
status: req.i18n.t(
|
|
62
|
+
result.verified ? "API.CAPTCHA_PASSED" : "API.CAPTCHA_FAILED"
|
|
63
|
+
),
|
|
64
|
+
...result
|
|
65
|
+
};
|
|
66
|
+
return res.json(returnValue);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
req.logger.error(() => ({
|
|
69
|
+
err,
|
|
70
|
+
body: req.body,
|
|
71
|
+
msg: "Error in image captcha solution submission"
|
|
72
|
+
}));
|
|
73
|
+
return next(
|
|
74
|
+
new common.ProsopoApiError("API.BAD_REQUEST", {
|
|
75
|
+
context: {
|
|
76
|
+
code: 500,
|
|
77
|
+
siteKey: req.body.dapp,
|
|
78
|
+
error: err
|
|
79
|
+
},
|
|
80
|
+
i18n: req.i18n,
|
|
81
|
+
logger: req.logger
|
|
82
|
+
})
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
module.exports = submitImageCaptchaSolution;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const common = require("@prosopo/common");
|
|
3
|
+
const types = require("@prosopo/types");
|
|
4
|
+
const util = require("@prosopo/util");
|
|
5
|
+
const tasks = require("../../tasks/tasks.cjs");
|
|
6
|
+
const apiToggleMaintenanceModeEndpoint = require("../admin/apiToggleMaintenanceModeEndpoint.cjs");
|
|
7
|
+
const validateAddress = require("../validateAddress.cjs");
|
|
8
|
+
const submitPoWCaptchaSolution = (env) => async (req, res, next) => {
|
|
9
|
+
let parsed;
|
|
10
|
+
const tasks$1 = new tasks.Tasks(env, req.logger);
|
|
11
|
+
if (apiToggleMaintenanceModeEndpoint.getMaintenanceMode()) {
|
|
12
|
+
req.logger.info(() => ({
|
|
13
|
+
msg: "Maintenance mode active - returning verified"
|
|
14
|
+
}));
|
|
15
|
+
const response = {
|
|
16
|
+
status: "ok",
|
|
17
|
+
verified: true
|
|
18
|
+
};
|
|
19
|
+
return res.json(response);
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
parsed = types.SubmitPowCaptchaSolutionBody.parse(req.body);
|
|
23
|
+
} catch (err) {
|
|
24
|
+
return next(
|
|
25
|
+
new common.ProsopoApiError("CAPTCHA.PARSE_ERROR", {
|
|
26
|
+
context: { code: 400, error: err, body: req.body },
|
|
27
|
+
i18n: req.i18n,
|
|
28
|
+
logger: req.logger
|
|
29
|
+
})
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
const { challenge, signature, nonce, verifiedTimeout, dapp, user } = parsed;
|
|
33
|
+
validateAddress.validateSiteKey(dapp);
|
|
34
|
+
validateAddress.validateAddr(user);
|
|
35
|
+
try {
|
|
36
|
+
const clientRecord = await tasks$1.db.getClientRecord(dapp);
|
|
37
|
+
if (!clientRecord) {
|
|
38
|
+
return next(
|
|
39
|
+
new common.ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", {
|
|
40
|
+
context: { code: 400, siteKey: dapp },
|
|
41
|
+
i18n: req.i18n,
|
|
42
|
+
logger: req.logger
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
const verified = await tasks$1.powCaptchaManager.verifyPowCaptchaSolution(
|
|
47
|
+
challenge,
|
|
48
|
+
signature.provider.challenge,
|
|
49
|
+
nonce,
|
|
50
|
+
verifiedTimeout,
|
|
51
|
+
signature.user.timestamp,
|
|
52
|
+
util.getIPAddress(req.ip || ""),
|
|
53
|
+
util.flatten(req.headers)
|
|
54
|
+
);
|
|
55
|
+
const response = { status: "ok", verified };
|
|
56
|
+
return res.json(response);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
req.logger.error(() => ({
|
|
59
|
+
err,
|
|
60
|
+
body: req.body,
|
|
61
|
+
msg: "Error in PoW captcha solution submission"
|
|
62
|
+
}));
|
|
63
|
+
return next(
|
|
64
|
+
new common.ProsopoApiError("API.BAD_REQUEST", {
|
|
65
|
+
context: {
|
|
66
|
+
code: 500,
|
|
67
|
+
siteKey: req.body.dapp,
|
|
68
|
+
error: err
|
|
69
|
+
},
|
|
70
|
+
i18n: req.i18n,
|
|
71
|
+
logger: req.logger
|
|
72
|
+
})
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
module.exports = submitPoWCaptchaSolution;
|