mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-06-27 01:41:19 +00:00
Some checks failed
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run
GitNexus Index / index (push) Waiting to run
GitNexus Index / post-index (push) Blocked by required conditions
Publish `@librechat/client` to NPM / pack (push) Waiting to run
Publish `@librechat/client` to NPM / publish-npm (push) Blocked by required conditions
Publish `librechat-data-provider` to NPM / pack (push) Waiting to run
Publish `librechat-data-provider` to NPM / publish-npm (push) Blocked by required conditions
Docker Dev Images Build / build (Dockerfile, librechat-dev, node) (push) Waiting to run
Docker Dev Images Build / build (Dockerfile.multi, librechat-dev-api, api-build) (push) Waiting to run
Sync Locize Translations & Create Translation PR / Sync Translation Keys with Locize (push) Waiting to run
Sync Locize Translations & Create Translation PR / Create Translation PR on Version Published (push) Blocked by required conditions
Sync Helm Chart Tags / Ignore non-main push (push) Waiting to run
Sync Helm Chart Tags / Sync chart tags (push) Waiting to run
Publish `@librechat/data-schemas` to NPM / pack (push) Has been cancelled
Publish `@librechat/data-schemas` to NPM / publish-npm (push) Has been cancelled
* fix: refine auth continuation throttling * chore: import order
101 lines
2.6 KiB
JavaScript
101 lines
2.6 KiB
JavaScript
const jwt = require('jsonwebtoken');
|
|
const { createHash } = require('crypto');
|
|
const rateLimit = require('express-rate-limit');
|
|
const { ViolationTypes } = require('librechat-data-provider');
|
|
const { limiterCache, removePorts } = require('@librechat/api');
|
|
const { logViolation } = require('~/cache');
|
|
|
|
const {
|
|
LOGIN_WINDOW = 5,
|
|
LOGIN_MAX = 7,
|
|
LOGIN_VIOLATION_SCORE,
|
|
TWO_FACTOR_TEMP_WINDOW = LOGIN_WINDOW,
|
|
TWO_FACTOR_TEMP_MAX = LOGIN_MAX,
|
|
TWO_FACTOR_TEMP_VIOLATION_SCORE,
|
|
} = process.env;
|
|
const windowMs = TWO_FACTOR_TEMP_WINDOW * 60 * 1000;
|
|
const max = TWO_FACTOR_TEMP_MAX;
|
|
const score = TWO_FACTOR_TEMP_VIOLATION_SCORE ?? LOGIN_VIOLATION_SCORE;
|
|
const windowInMinutes = windowMs / 60000;
|
|
const message = `Too many verification attempts, please try again after ${windowInMinutes} minutes.`;
|
|
|
|
const hashLimiterKey = (value) => createHash('sha256').update(value).digest('hex');
|
|
|
|
const getUserLimiterKey = (req) => {
|
|
const userId = req.user?.id ?? req.user?._id;
|
|
if (userId) {
|
|
return `user:${userId.toString()}`;
|
|
}
|
|
|
|
const tempToken = req.body?.tempToken;
|
|
if (typeof tempToken === 'string' && tempToken) {
|
|
return `temp:${hashLimiterKey(tempToken)}`;
|
|
}
|
|
|
|
const ip = removePorts(req);
|
|
return ip ? `ip:${ip}` : 'ip:unknown';
|
|
};
|
|
|
|
const getTempTokenUserId = (tempToken) => {
|
|
if (!tempToken) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
const payload = jwt.verify(tempToken, process.env.JWT_SECRET);
|
|
return payload?.userId ?? null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
const createHandler = (limiter) => async (req, res) => {
|
|
const type = ViolationTypes.LOGINS;
|
|
const errorMessage = {
|
|
type,
|
|
max,
|
|
limiter,
|
|
windowInMinutes,
|
|
};
|
|
|
|
const userId = getTempTokenUserId(req.body?.tempToken);
|
|
if (userId && !req.user) {
|
|
req.user = { id: userId };
|
|
} else if (userId && !req.user.id && !req.user._id) {
|
|
req.user.id = userId;
|
|
}
|
|
|
|
await logViolation(req, res, type, errorMessage, score);
|
|
return res.status(429).json({ message });
|
|
};
|
|
|
|
const ipLimiterOptions = {
|
|
windowMs,
|
|
max,
|
|
handler: createHandler('ip'),
|
|
keyGenerator: removePorts,
|
|
store: limiterCache('two_factor_temp_limiter'),
|
|
};
|
|
|
|
const userLimiterOptions = {
|
|
windowMs,
|
|
max,
|
|
handler: createHandler('user'),
|
|
keyGenerator: getUserLimiterKey,
|
|
store: limiterCache('two_factor_temp_user_limiter'),
|
|
};
|
|
|
|
const twoFactorTempIpLimiter = rateLimit(ipLimiterOptions);
|
|
const twoFactorTempUserLimiter = rateLimit(userLimiterOptions);
|
|
|
|
const twoFactorTempLimiter = (req, res, next) => {
|
|
twoFactorTempIpLimiter(req, res, (err) => {
|
|
if (err) {
|
|
return next(err);
|
|
}
|
|
|
|
return twoFactorTempUserLimiter(req, res, next);
|
|
});
|
|
};
|
|
|
|
module.exports = twoFactorTempLimiter;
|