fix(models): update user and token operations to use centralized functions

This commit is contained in:
Danny Avila 2025-05-30 13:59:30 -04:00
parent 6e278f6932
commit 3831ad8202
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
10 changed files with 48 additions and 58 deletions

View file

@ -1,8 +1,6 @@
const mongoose = require('mongoose'); const { findToken, updateToken, createToken } = require('~/models');
const { encryptV2 } = require('~/server/utils/crypto'); const { encryptV2 } = require('~/server/utils/crypto');
const Token = require('~/db/models').Token;
/** /**
* Handles the OAuth token by creating or updating the token. * Handles the OAuth token by creating or updating the token.
* @param {object} fields * @param {object} fields
@ -31,11 +29,11 @@ async function handleOAuthToken({
expiresIn: parseInt(expiresIn, 10) || 3600, expiresIn: parseInt(expiresIn, 10) || 3600,
}; };
const existingToken = await Token.findToken({ userId, identifier }); const existingToken = await findToken({ userId, identifier });
if (existingToken) { if (existingToken) {
return await Token.updateToken({ identifier }, tokenData); return await updateToken({ identifier }, tokenData);
} else { } else {
return await Token.createToken(tokenData); return await createToken(tokenData);
} }
} }

View file

@ -1,8 +1,7 @@
const mongoose = require('mongoose'); const mongoose = require('mongoose');
const { logger } = require('@librechat/data-schemas'); const { logger } = require('@librechat/data-schemas');
const { getRandomValues, hashToken } = require('~/server/utils/crypto'); const { getRandomValues, hashToken } = require('~/server/utils/crypto');
const { createToken, findToken } = require('~/models');
const Token = require('~/db/models').Token;
/** /**
* @module inviteUser * @module inviteUser
@ -24,7 +23,7 @@ const createInvite = async (email) => {
const fakeUserId = new mongoose.Types.ObjectId(); const fakeUserId = new mongoose.Types.ObjectId();
await Token.createToken({ await createToken({
userId: fakeUserId, userId: fakeUserId,
email, email,
token: hash, token: hash,
@ -51,7 +50,7 @@ const getInvite = async (encodedToken, email) => {
try { try {
const token = decodeURIComponent(encodedToken); const token = decodeURIComponent(encodedToken);
const hash = await hashToken(token); const hash = await hashToken(token);
const invite = await Token.findToken({ token: hash, email }); const invite = await findToken({ token: hash, email });
if (!invite) { if (!invite) {
throw new Error('Invite not found or email does not match'); throw new Error('Invite not found or email does not match');

View file

@ -9,12 +9,10 @@ const {
requestPasswordReset, requestPasswordReset,
setOpenIDAuthTokens, setOpenIDAuthTokens,
} = require('~/server/services/AuthService'); } = require('~/server/services/AuthService');
const { findUser, getUserById } = require('~/models'); const { findUser, getUserById, deleteAllUserSessions, findSession } = require('~/models');
const { getOpenIdConfig } = require('~/strategies'); const { getOpenIdConfig } = require('~/strategies');
const { isEnabled } = require('~/server/utils'); const { isEnabled } = require('~/server/utils');
const Session = require('~/db/models').Session;
const registrationController = async (req, res) => { const registrationController = async (req, res) => {
try { try {
const response = await registerUser(req.body); const response = await registerUser(req.body);
@ -50,7 +48,7 @@ const resetPasswordController = async (req, res) => {
if (resetPasswordService instanceof Error) { if (resetPasswordService instanceof Error) {
return res.status(400).json(resetPasswordService); return res.status(400).json(resetPasswordService);
} else { } else {
await Session.deleteAllUserSessions({ userId: req.body.userId }); await deleteAllUserSessions({ userId: req.body.userId });
return res.status(200).json(resetPasswordService); return res.status(200).json(resetPasswordService);
} }
} catch (e) { } catch (e) {
@ -98,7 +96,7 @@ const refreshController = async (req, res) => {
} }
// Find the session with the hashed refresh token // Find the session with the hashed refresh token
const session = await Session.findSession({ const session = await findSession({
userId: userId, userId: userId,
refreshToken: refreshToken, refreshToken: refreshToken,
}); });

View file

@ -1,16 +1,15 @@
const mongoose = require('mongoose');
const { logger } = require('@librechat/data-schemas'); const { logger } = require('@librechat/data-schemas');
const { const {
verifyTOTP,
getTOTPSecret,
verifyBackupCode,
generateTOTPSecret, generateTOTPSecret,
generateBackupCodes, generateBackupCodes,
verifyTOTP,
verifyBackupCode,
getTOTPSecret,
} = require('~/server/services/twoFactorService'); } = require('~/server/services/twoFactorService');
const { getUserById, updateUser } = require('~/models');
const { encryptV3 } = require('~/server/utils/crypto'); const { encryptV3 } = require('~/server/utils/crypto');
const safeAppTitle = (process.env.APP_TITLE || 'LibreChat').replace(/\s+/g, '');
const User = require('~/db/models').User; const safeAppTitle = (process.env.APP_TITLE || 'LibreChat').replace(/\s+/g, '');
/** /**
* Enable 2FA for the user by generating a new TOTP secret and backup codes. * Enable 2FA for the user by generating a new TOTP secret and backup codes.
@ -26,7 +25,7 @@ const enable2FA = async (req, res) => {
const encryptedSecret = encryptV3(secret); const encryptedSecret = encryptV3(secret);
// Update the user record: store the secret & backup codes and set twoFactorEnabled to false. // Update the user record: store the secret & backup codes and set twoFactorEnabled to false.
const user = await User.updateUser(userId, { const user = await updateUser(userId, {
totpSecret: encryptedSecret, totpSecret: encryptedSecret,
backupCodes: codeObjects, backupCodes: codeObjects,
twoFactorEnabled: false, twoFactorEnabled: false,
@ -48,7 +47,7 @@ const verify2FA = async (req, res) => {
try { try {
const userId = req.user.id; const userId = req.user.id;
const { token, backupCode } = req.body; const { token, backupCode } = req.body;
const user = await User.getUserById(userId); const user = await getUserById(userId);
if (!user || !user.totpSecret) { if (!user || !user.totpSecret) {
return res.status(400).json({ message: '2FA not initiated' }); return res.status(400).json({ message: '2FA not initiated' });
@ -80,7 +79,7 @@ const confirm2FA = async (req, res) => {
try { try {
const userId = req.user.id; const userId = req.user.id;
const { token } = req.body; const { token } = req.body;
const user = await User.getUserById(userId); const user = await getUserById(userId);
if (!user || !user.totpSecret) { if (!user || !user.totpSecret) {
return res.status(400).json({ message: '2FA not initiated' }); return res.status(400).json({ message: '2FA not initiated' });
@ -88,7 +87,7 @@ const confirm2FA = async (req, res) => {
const secret = await getTOTPSecret(user.totpSecret); const secret = await getTOTPSecret(user.totpSecret);
if (await verifyTOTP(secret, token)) { if (await verifyTOTP(secret, token)) {
await User.updateUser(userId, { twoFactorEnabled: true }); await updateUser(userId, { twoFactorEnabled: true });
return res.status(200).json(); return res.status(200).json();
} }
return res.status(400).json({ message: 'Invalid token.' }); return res.status(400).json({ message: 'Invalid token.' });
@ -104,7 +103,7 @@ const confirm2FA = async (req, res) => {
const disable2FA = async (req, res) => { const disable2FA = async (req, res) => {
try { try {
const userId = req.user.id; const userId = req.user.id;
await User.updateUser(userId, { totpSecret: null, backupCodes: [], twoFactorEnabled: false }); await updateUser(userId, { totpSecret: null, backupCodes: [], twoFactorEnabled: false });
return res.status(200).json(); return res.status(200).json();
} catch (err) { } catch (err) {
logger.error('[disable2FA]', err); logger.error('[disable2FA]', err);
@ -119,7 +118,7 @@ const regenerateBackupCodes = async (req, res) => {
try { try {
const userId = req.user.id; const userId = req.user.id;
const { plainCodes, codeObjects } = await generateBackupCodes(); const { plainCodes, codeObjects } = await generateBackupCodes();
await User.updateUser(userId, { backupCodes: codeObjects }); await updateUser(userId, { backupCodes: codeObjects });
return res.status(200).json({ return res.status(200).json({
backupCodes: plainCodes, backupCodes: plainCodes,
backupCodesHash: codeObjects, backupCodesHash: codeObjects,

View file

@ -4,9 +4,17 @@ const {
webSearchKeys, webSearchKeys,
extractWebSearchEnvVars, extractWebSearchEnvVars,
} = require('librechat-data-provider'); } = require('librechat-data-provider');
const mongoose = require('mongoose');
const { logger } = require('@librechat/data-schemas'); const { logger } = require('@librechat/data-schemas');
const { getFiles, deleteFiles, deleteConvos, deletePresets, deleteMessages } = require('~/models'); const {
getFiles,
updateUser,
deleteFiles,
deleteConvos,
deletePresets,
deleteMessages,
deleteUserById,
deleteAllUserSessions,
} = require('~/models');
const { updateUserPluginAuth, deleteUserPluginAuth } = require('~/server/services/PluginService'); const { updateUserPluginAuth, deleteUserPluginAuth } = require('~/server/services/PluginService');
const { updateUserPluginsService, deleteUserKey } = require('~/server/services/UserService'); const { updateUserPluginsService, deleteUserKey } = require('~/server/services/UserService');
const { verifyEmail, resendVerificationEmail } = require('~/server/services/AuthService'); const { verifyEmail, resendVerificationEmail } = require('~/server/services/AuthService');
@ -16,7 +24,6 @@ const { deleteAllSharedLinks } = require('~/models/Share');
const { deleteToolCalls } = require('~/models/ToolCall'); const { deleteToolCalls } = require('~/models/ToolCall');
const Transaction = require('~/db/models').Transaction; const Transaction = require('~/db/models').Transaction;
const Session = require('~/db/models').Session;
const Balance = require('~/db/models').Balance; const Balance = require('~/db/models').Balance;
const User = require('~/db/models').User; const User = require('~/db/models').User;
@ -32,7 +39,7 @@ const getUserController = async (req, res) => {
const originalAvatar = userData.avatar; const originalAvatar = userData.avatar;
try { try {
userData.avatar = await getNewS3URL(userData.avatar); userData.avatar = await getNewS3URL(userData.avatar);
await User.updateUser(userData.id, { avatar: userData.avatar }); await updateUser(userData.id, { avatar: userData.avatar });
} catch (error) { } catch (error) {
userData.avatar = originalAvatar; userData.avatar = originalAvatar;
logger.error('Error getting new S3 URL for avatar:', error); logger.error('Error getting new S3 URL for avatar:', error);
@ -153,7 +160,7 @@ const deleteUserController = async (req, res) => {
try { try {
await deleteMessages({ user: user.id }); // delete user messages await deleteMessages({ user: user.id }); // delete user messages
await Session.deleteAllUserSessions({ userId: user.id }); // delete user sessions await deleteAllUserSessions({ userId: user.id }); // delete user sessions
await Transaction.deleteMany({ user: user.id }); // delete user transactions await Transaction.deleteMany({ user: user.id }); // delete user transactions
await deleteUserKey({ userId: user.id, all: true }); // delete user keys await deleteUserKey({ userId: user.id, all: true }); // delete user keys
await Balance.deleteMany({ user: user._id }); // delete user balances await Balance.deleteMany({ user: user._id }); // delete user balances
@ -161,7 +168,7 @@ const deleteUserController = async (req, res) => {
/* TODO: Delete Assistant Threads */ /* TODO: Delete Assistant Threads */
await deleteConvos(user.id); // delete user convos await deleteConvos(user.id); // delete user convos
await deleteUserPluginAuth(user.id, null, true); // delete user plugin auth await deleteUserPluginAuth(user.id, null, true); // delete user plugin auth
await User.deleteUserById(user.id); // delete user await deleteUserById(user.id); // delete user
await deleteAllSharedLinks(user.id); // delete user shared links await deleteAllSharedLinks(user.id); // delete user shared links
await deleteUserFiles(req); // delete user files await deleteUserFiles(req); // delete user files
await deleteFiles(null, user.id); // delete database files in case of orphaned files from previous steps await deleteFiles(null, user.id); // delete database files in case of orphaned files from previous steps

View file

@ -1,14 +1,12 @@
const jwt = require('jsonwebtoken'); const jwt = require('jsonwebtoken');
const mongoose = require('mongoose'); const { logger } = require('@librechat/data-schemas');
const { const {
verifyTOTP, verifyTOTP,
verifyBackupCode,
getTOTPSecret, getTOTPSecret,
verifyBackupCode,
} = require('~/server/services/twoFactorService'); } = require('~/server/services/twoFactorService');
const { setAuthTokens } = require('~/server/services/AuthService'); const { setAuthTokens } = require('~/server/services/AuthService');
const { logger } = require('@librechat/data-schemas'); const { getUserById } = require('~/models');
const User = require('~/db/models').User;
/** /**
* Verifies the 2FA code during login using a temporary token. * Verifies the 2FA code during login using a temporary token.
@ -27,7 +25,7 @@ const verify2FAWithTempToken = async (req, res) => {
return res.status(401).json({ message: 'Invalid or expired temporary token' }); return res.status(401).json({ message: 'Invalid or expired temporary token' });
} }
const user = await User.getUserById(payload.userId); const user = await getUserById(payload.userId);
if (!user || !user.twoFactorEnabled) { if (!user || !user.twoFactorEnabled) {
return res.status(400).json({ message: '2FA is not enabled for this user' }); return res.status(400).json({ message: '2FA is not enabled for this user' });
} }

View file

@ -1,8 +1,6 @@
const mongoose = require('mongoose');
const { webcrypto } = require('node:crypto'); const { webcrypto } = require('node:crypto');
const { hashBackupCode, decryptV3, decryptV2 } = require('~/server/utils/crypto'); const { hashBackupCode, decryptV3, decryptV2 } = require('~/server/utils/crypto');
const { updateUser } = require('~/models');
const User = require('~/db/models').User;
// Base32 alphabet for TOTP secret encoding. // Base32 alphabet for TOTP secret encoding.
const BASE32_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; const BASE32_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
@ -174,7 +172,7 @@ const verifyBackupCode = async ({ user, backupCode }) => {
: codeObj, : codeObj,
); );
// Update the user record with the marked backup code. // Update the user record with the marked backup code.
await User.updateUser(user._id, { backupCodes: updatedBackupCodes }); await updateUser(user._id, { backupCodes: updatedBackupCodes });
return true; return true;
} }
return false; return false;

View file

@ -1,9 +1,7 @@
const mongoose = require('mongoose');
const { logger } = require('@librechat/data-schemas'); const { logger } = require('@librechat/data-schemas');
const { SystemRoles } = require('librechat-data-provider'); const { SystemRoles } = require('librechat-data-provider');
const { Strategy: JwtStrategy, ExtractJwt } = require('passport-jwt'); const { Strategy: JwtStrategy, ExtractJwt } = require('passport-jwt');
const { getUserById, updateUser } = require('~/models');
const User = require('~/db/models').User;
// JWT strategy // JWT strategy
const jwtLogin = () => const jwtLogin = () =>
@ -14,12 +12,12 @@ const jwtLogin = () =>
}, },
async (payload, done) => { async (payload, done) => {
try { try {
const user = await User.getUserById(payload?.id, '-password -__v -totpSecret'); const user = await getUserById(payload?.id, '-password -__v -totpSecret');
if (user) { if (user) {
user.id = user._id.toString(); user.id = user._id.toString();
if (!user.role) { if (!user.role) {
user.role = SystemRoles.USER; user.role = SystemRoles.USER;
await User.updateUser(user.id, { role: user.role }); await updateUser(user.id, { role: user.role });
} }
done(null, user); done(null, user);
} else { } else {

View file

@ -1,14 +1,11 @@
const fs = require('fs'); const fs = require('fs');
const mongoose = require('mongoose');
const LdapStrategy = require('passport-ldapauth'); const LdapStrategy = require('passport-ldapauth');
const { SystemRoles } = require('librechat-data-provider'); const { SystemRoles } = require('librechat-data-provider');
const { logger } = require('@librechat/data-schemas'); const { logger } = require('@librechat/data-schemas');
const { createUser, findUser, updateUser } = require('~/models'); const { createUser, findUser, updateUser, countUsers } = require('~/models');
const { getBalanceConfig } = require('~/server/services/Config'); const { getBalanceConfig } = require('~/server/services/Config');
const { isEnabled } = require('~/server/utils'); const { isEnabled } = require('~/server/utils');
const User = require('~/db/models').User;
const { const {
LDAP_URL, LDAP_URL,
LDAP_BIND_DN, LDAP_BIND_DN,
@ -117,7 +114,7 @@ const ldapLogin = new LdapStrategy(ldapOptions, async (userinfo, done) => {
} }
if (!user) { if (!user) {
const isFirstRegisteredUser = (await User.countUsers()) === 0; const isFirstRegisteredUser = (await countUsers()) === 0;
user = { user = {
provider: 'ldap', provider: 'ldap',
ldapId, ldapId,

View file

@ -2,11 +2,9 @@ const { logger } = require('@librechat/data-schemas');
const { errorsToString } = require('librechat-data-provider'); const { errorsToString } = require('librechat-data-provider');
const { Strategy: PassportLocalStrategy } = require('passport-local'); const { Strategy: PassportLocalStrategy } = require('passport-local');
const { isEnabled, checkEmailConfig } = require('~/server/utils'); const { isEnabled, checkEmailConfig } = require('~/server/utils');
const { findUser, comparePassword } = require('~/models'); const { findUser, comparePassword, updateUser } = require('~/models');
const { loginSchema } = require('./validators'); const { loginSchema } = require('./validators');
const User = require('~/db/models').User;
// Unix timestamp for 2024-06-07 15:20:18 Eastern Time // Unix timestamp for 2024-06-07 15:20:18 Eastern Time
const verificationEnabledTimestamp = 1717788018; const verificationEnabledTimestamp = 1717788018;
@ -46,13 +44,13 @@ async function passportLogin(req, email, password, done) {
!user.emailVerified && !user.emailVerified &&
userCreatedAtTimestamp < verificationEnabledTimestamp userCreatedAtTimestamp < verificationEnabledTimestamp
) { ) {
await User.updateUser(user._id, { emailVerified: true }); await updateUser(user._id, { emailVerified: true });
user.emailVerified = true; user.emailVerified = true;
} }
const unverifiedAllowed = isEnabled(process.env.ALLOW_UNVERIFIED_EMAIL_LOGIN); const unverifiedAllowed = isEnabled(process.env.ALLOW_UNVERIFIED_EMAIL_LOGIN);
if (user.expiresAt && unverifiedAllowed) { if (user.expiresAt && unverifiedAllowed) {
await User.updateUser(user._id, {}); await updateUser(user._id, {});
} }
if (!user.emailVerified && !unverifiedAllowed) { if (!user.emailVerified && !unverifiedAllowed) {