LibreChat/api/server/middleware/limiters/twoFactorTempLimiter.test.js
Danny Avila 209e8d1eb6
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: Add Per-User Throttle to 2FA Continuation Attempts (#13583)
* fix: refine auth continuation throttling

* chore: import order
2026-06-07 22:31:45 -04:00

111 lines
3.2 KiB
JavaScript

const jwt = require('jsonwebtoken');
const express = require('express');
const request = require('supertest');
const originalEnv = process.env;
const jwtSecret = 'test-two-factor-secret';
const createToken = (userId) =>
jwt.sign({ userId, twoFAPending: true }, jwtSecret, { expiresIn: '5m' });
const createApp = () => {
jest.resetModules();
process.env = {
...originalEnv,
JWT_SECRET: jwtSecret,
LOGIN_MAX: '2',
LOGIN_WINDOW: '5',
TWO_FACTOR_TEMP_MAX: '2',
TWO_FACTOR_TEMP_WINDOW: '5',
};
jest.doMock('@librechat/api', () => ({
limiterCache: jest.fn(() => undefined),
removePorts: (req) => req?.['ip'],
}));
jest.doMock('~/cache', () => ({
logViolation: jest.fn().mockResolvedValue(undefined),
}));
const setTwoFactorTempUser = require('../setTwoFactorTempUser');
const twoFactorTempLimiter = require('./twoFactorTempLimiter');
const { logViolation } = require('~/cache');
const app = express();
app.set('trust proxy', 1);
app.use(express.json());
app.post('/verify', setTwoFactorTempUser, twoFactorTempLimiter, (req, res) =>
res.status(204).end(),
);
return { app, logViolation };
};
describe('twoFactorTempLimiter', () => {
afterEach(() => {
jest.dontMock('@librechat/api');
jest.dontMock('~/cache');
process.env = originalEnv;
});
it('limits a valid temp-token user across rotating source IPs', async () => {
const { app, logViolation } = createApp();
const tempToken = createToken('user-1');
await request(app)
.post('/verify')
.set('X-Forwarded-For', '203.0.113.1')
.send({ tempToken, token: '000000' })
.expect(204);
await request(app)
.post('/verify')
.set('X-Forwarded-For', '203.0.113.2')
.send({ tempToken, token: '000001' })
.expect(204);
const response = await request(app)
.post('/verify')
.set('X-Forwarded-For', '203.0.113.3')
.send({ tempToken, token: '000002' })
.expect(429);
expect(response.body).toEqual({
message: 'Too many verification attempts, please try again after 5 minutes.',
});
expect(logViolation).toHaveBeenCalledTimes(1);
expect(logViolation.mock.calls[0][0].user).toEqual({ id: 'user-1' });
expect(logViolation.mock.calls[0][3]).toMatchObject({
limiter: 'user',
max: '2',
windowInMinutes: 5,
});
});
it('keeps the existing source IP limit before the user limit', async () => {
const { app, logViolation } = createApp();
await request(app)
.post('/verify')
.set('X-Forwarded-For', '198.51.100.1')
.send({ tempToken: createToken('user-a'), token: '000000' })
.expect(204);
await request(app)
.post('/verify')
.set('X-Forwarded-For', '198.51.100.1')
.send({ tempToken: createToken('user-b'), token: '000001' })
.expect(204);
await request(app)
.post('/verify')
.set('X-Forwarded-For', '198.51.100.1')
.send({ tempToken: createToken('user-c'), token: '000002' })
.expect(429);
expect(logViolation).toHaveBeenCalledTimes(1);
expect(logViolation.mock.calls[0][3]).toMatchObject({
limiter: 'ip',
max: '2',
windowInMinutes: 5,
});
});
});