mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-19 09:50:15 +01:00
112 lines
3.1 KiB
JavaScript
112 lines
3.1 KiB
JavaScript
|
|
const {
|
||
|
|
verifyTOTP,
|
||
|
|
verifyBackupCode,
|
||
|
|
generateTOTPSecret,
|
||
|
|
generateBackupCodes,
|
||
|
|
} = require('~/server/services/twoFactorService');
|
||
|
|
const { updateUser, getUserById } = require('~/models');
|
||
|
|
const { logger } = require('~/config');
|
||
|
|
|
||
|
|
const enable2FAController = async (req, res) => {
|
||
|
|
const safeAppTitle = (process.env.APP_TITLE || 'LibreChat').replace(/\s+/g, '');
|
||
|
|
|
||
|
|
try {
|
||
|
|
const userId = req.user.id;
|
||
|
|
const secret = generateTOTPSecret();
|
||
|
|
const { plainCodes, codeObjects } = await generateBackupCodes();
|
||
|
|
|
||
|
|
const user = await updateUser(userId, { totpSecret: secret, backupCodes: codeObjects });
|
||
|
|
|
||
|
|
const otpauthUrl = `otpauth://totp/${safeAppTitle}:${user.email}?secret=${secret}&issuer=${safeAppTitle}`;
|
||
|
|
|
||
|
|
res.status(200).json({
|
||
|
|
otpauthUrl,
|
||
|
|
backupCodes: plainCodes,
|
||
|
|
});
|
||
|
|
} catch (err) {
|
||
|
|
logger.error('[enable2FAController]', err);
|
||
|
|
res.status(500).json({ message: err.message });
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const verify2FAController = async (req, res) => {
|
||
|
|
try {
|
||
|
|
const userId = req.user.id;
|
||
|
|
const { token, backupCode } = req.body;
|
||
|
|
const user = await getUserById(userId);
|
||
|
|
if (!user || !user.totpSecret) {
|
||
|
|
return res.status(400).json({ message: '2FA not initiated' });
|
||
|
|
}
|
||
|
|
|
||
|
|
let verified = false;
|
||
|
|
if (token && (await verifyTOTP(user.totpSecret, token))) {
|
||
|
|
return res.status(200).json();
|
||
|
|
} else if (backupCode) {
|
||
|
|
verified = await verifyBackupCode({ user, backupCode });
|
||
|
|
}
|
||
|
|
if (verified) {
|
||
|
|
return res.status(200).json();
|
||
|
|
}
|
||
|
|
|
||
|
|
return res.status(400).json({ message: 'Invalid token.' });
|
||
|
|
} catch (err) {
|
||
|
|
logger.error('[verify2FAController]', err);
|
||
|
|
res.status(500).json({ message: err.message });
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const confirm2FAController = async (req, res) => {
|
||
|
|
try {
|
||
|
|
const userId = req.user.id;
|
||
|
|
const { token } = req.body;
|
||
|
|
const user = await getUserById(userId);
|
||
|
|
|
||
|
|
if (!user || !user.totpSecret) {
|
||
|
|
return res.status(400).json({ message: '2FA not initiated' });
|
||
|
|
}
|
||
|
|
|
||
|
|
if (await verifyTOTP(user.totpSecret, token)) {
|
||
|
|
return res.status(200).json();
|
||
|
|
}
|
||
|
|
|
||
|
|
return res.status(400).json({ message: 'Invalid token.' });
|
||
|
|
} catch (err) {
|
||
|
|
logger.error('[confirm2FAController]', err);
|
||
|
|
res.status(500).json({ message: err.message });
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const disable2FAController = async (req, res) => {
|
||
|
|
try {
|
||
|
|
const userId = req.user.id;
|
||
|
|
await updateUser(userId, { totpSecret: null, backupCodes: [] });
|
||
|
|
res.status(200).json();
|
||
|
|
} catch (err) {
|
||
|
|
logger.error('[disable2FAController]', err);
|
||
|
|
res.status(500).json({ message: err.message });
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const regenerateBackupCodesController = async (req, res) => {
|
||
|
|
try {
|
||
|
|
const userId = req.user.id;
|
||
|
|
const { plainCodes, codeObjects } = await generateBackupCodes();
|
||
|
|
await updateUser(userId, { backupCodes: codeObjects });
|
||
|
|
res.status(200).json({
|
||
|
|
backupCodes: plainCodes,
|
||
|
|
backupCodesHash: codeObjects,
|
||
|
|
});
|
||
|
|
} catch (err) {
|
||
|
|
logger.error('[regenerateBackupCodesController]', err);
|
||
|
|
res.status(500).json({ message: err.message });
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
module.exports = {
|
||
|
|
enable2FAController,
|
||
|
|
verify2FAController,
|
||
|
|
confirm2FAController,
|
||
|
|
disable2FAController,
|
||
|
|
regenerateBackupCodesController,
|
||
|
|
};
|