mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 00:40:14 +01:00
🔒 fix: 2FA Encrypt TOTP Secrets & Improve Docs (#5933)
* 🔒 fix: Integrate TOTP secret retrieval and encryption in Two-Factor Authentication * 🔒 refactor: Simplify TOTP verification by removing commented-out code
This commit is contained in:
parent
06282b584f
commit
538a2a144a
3 changed files with 84 additions and 41 deletions
|
|
@ -3,9 +3,11 @@ const {
|
|||
verifyBackupCode,
|
||||
generateTOTPSecret,
|
||||
generateBackupCodes,
|
||||
getTOTPSecret,
|
||||
} = require('~/server/services/twoFactorService');
|
||||
const { updateUser, getUserById } = require('~/models');
|
||||
const { logger } = require('~/config');
|
||||
const { encryptV2 } = require('~/server/utils/crypto');
|
||||
|
||||
const enable2FAController = async (req, res) => {
|
||||
const safeAppTitle = (process.env.APP_TITLE || 'LibreChat').replace(/\s+/g, '');
|
||||
|
|
@ -15,7 +17,8 @@ const enable2FAController = async (req, res) => {
|
|||
const secret = generateTOTPSecret();
|
||||
const { plainCodes, codeObjects } = await generateBackupCodes();
|
||||
|
||||
const user = await updateUser(userId, { totpSecret: secret, backupCodes: codeObjects });
|
||||
const encryptedSecret = await encryptV2(secret);
|
||||
const user = await updateUser(userId, { totpSecret: encryptedSecret, backupCodes: codeObjects });
|
||||
|
||||
const otpauthUrl = `otpauth://totp/${safeAppTitle}:${user.email}?secret=${secret}&issuer=${safeAppTitle}`;
|
||||
|
||||
|
|
@ -38,14 +41,16 @@ const verify2FAController = async (req, res) => {
|
|||
return res.status(400).json({ message: '2FA not initiated' });
|
||||
}
|
||||
|
||||
let verified = false;
|
||||
if (token && (await verifyTOTP(user.totpSecret, token))) {
|
||||
// Retrieve the plain TOTP secret using getTOTPSecret.
|
||||
const secret = await getTOTPSecret(user.totpSecret);
|
||||
|
||||
if (token && (await verifyTOTP(secret, token))) {
|
||||
return res.status(200).json();
|
||||
} else if (backupCode) {
|
||||
verified = await verifyBackupCode({ user, backupCode });
|
||||
}
|
||||
if (verified) {
|
||||
return res.status(200).json();
|
||||
const verified = await verifyBackupCode({ user, backupCode });
|
||||
if (verified) {
|
||||
return res.status(200).json();
|
||||
}
|
||||
}
|
||||
|
||||
return res.status(400).json({ message: 'Invalid token.' });
|
||||
|
|
@ -65,7 +70,10 @@ const confirm2FAController = async (req, res) => {
|
|||
return res.status(400).json({ message: '2FA not initiated' });
|
||||
}
|
||||
|
||||
if (await verifyTOTP(user.totpSecret, token)) {
|
||||
// Retrieve the plain TOTP secret using getTOTPSecret.
|
||||
const secret = await getTOTPSecret(user.totpSecret);
|
||||
|
||||
if (await verifyTOTP(secret, token)) {
|
||||
return res.status(200).json();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
const jwt = require('jsonwebtoken');
|
||||
const { verifyTOTP, verifyBackupCode } = require('~/server/services/twoFactorService');
|
||||
const { verifyTOTP, verifyBackupCode, getTOTPSecret } = require('~/server/services/twoFactorService');
|
||||
const { setAuthTokens } = require('~/server/services/AuthService');
|
||||
const { getUserById } = require('~/models/userMethods');
|
||||
const { logger } = require('~/config');
|
||||
|
|
@ -24,9 +24,11 @@ const verify2FA = async (req, res) => {
|
|||
return res.status(400).json({ message: '2FA is not enabled for this user' });
|
||||
}
|
||||
|
||||
let verified = false;
|
||||
// Use the new getTOTPSecret function to retrieve (and decrypt if necessary) the TOTP secret.
|
||||
const secret = await getTOTPSecret(user.totpSecret);
|
||||
|
||||
if (token && (await verifyTOTP(user.totpSecret, token))) {
|
||||
let verified = false;
|
||||
if (token && (await verifyTOTP(secret, token))) {
|
||||
verified = true;
|
||||
} else if (backupCode) {
|
||||
verified = await verifyBackupCode({ user, backupCode });
|
||||
|
|
@ -39,7 +41,7 @@ const verify2FA = async (req, res) => {
|
|||
// Prepare user data for response.
|
||||
// If the user is a plain object (from lean queries), we create a shallow copy.
|
||||
const userData = user.toObject ? user.toObject() : { ...user };
|
||||
// Remove sensitive fields
|
||||
// Remove sensitive fields.
|
||||
delete userData.password;
|
||||
delete userData.__v;
|
||||
delete userData.totpSecret;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue