| const { logger } = require('@librechat/data-schemas'); |
| const { errorsToString } = require('librechat-data-provider'); |
| const { isEnabled, checkEmailConfig } = require('@librechat/api'); |
| const { Strategy: PassportLocalStrategy } = require('passport-local'); |
| const { findUser, comparePassword, updateUser } = require('~/models'); |
| const { loginSchema } = require('./validators'); |
|
|
| |
| const verificationEnabledTimestamp = 1717788018; |
|
|
| async function validateLoginRequest(req) { |
| const { error } = loginSchema.safeParse(req.body); |
| return error ? errorsToString(error.errors) : null; |
| } |
|
|
| async function passportLogin(req, email, password, done) { |
| try { |
| const validationError = await validateLoginRequest(req); |
| if (validationError) { |
| logError('Passport Local Strategy - Validation Error', { reqBody: req.body }); |
| logger.error(`[Login] [Login failed] [Username: ${email}] [Request-IP: ${req.ip}]`); |
| return done(null, false, { message: validationError }); |
| } |
|
|
| const user = await findUser({ email: email.trim() }, '+password'); |
| if (!user) { |
| logError('Passport Local Strategy - User Not Found', { email }); |
| logger.error(`[Login] [Login failed] [Username: ${email}] [Request-IP: ${req.ip}]`); |
| return done(null, false, { message: 'Email does not exist.' }); |
| } |
|
|
| if (!user.password) { |
| logError('Passport Local Strategy - User has no password', { email }); |
| logger.error(`[Login] [Login failed] [Username: ${email}] [Request-IP: ${req.ip}]`); |
| return done(null, false, { message: 'Email does not exist.' }); |
| } |
|
|
| const isMatch = await comparePassword(user, password); |
| if (!isMatch) { |
| logError('Passport Local Strategy - Password does not match', { isMatch }); |
| logger.error(`[Login] [Login failed] [Username: ${email}] [Request-IP: ${req.ip}]`); |
| return done(null, false, { message: 'Incorrect password.' }); |
| } |
|
|
| const emailEnabled = checkEmailConfig(); |
| const userCreatedAtTimestamp = Math.floor(new Date(user.createdAt).getTime() / 1000); |
|
|
| if ( |
| !emailEnabled && |
| !user.emailVerified && |
| userCreatedAtTimestamp < verificationEnabledTimestamp |
| ) { |
| await updateUser(user._id, { emailVerified: true }); |
| user.emailVerified = true; |
| } |
|
|
| const unverifiedAllowed = isEnabled(process.env.ALLOW_UNVERIFIED_EMAIL_LOGIN); |
| if (user.expiresAt && unverifiedAllowed) { |
| await updateUser(user._id, {}); |
| } |
|
|
| if (!user.emailVerified && !unverifiedAllowed) { |
| logError('Passport Local Strategy - Email not verified', { email }); |
| logger.error(`[Login] [Login failed] [Username: ${email}] [Request-IP: ${req.ip}]`); |
| return done(null, user, { message: 'Email not verified.' }); |
| } |
|
|
| logger.info(`[Login] [Login successful] [Username: ${email}] [Request-IP: ${req.ip}]`); |
| return done(null, user); |
| } catch (err) { |
| return done(err); |
| } |
| } |
|
|
| function logError(title, parameters) { |
| const entries = Object.entries(parameters).map(([name, value]) => ({ name, value })); |
| logger.error(title, { parameters: entries }); |
| } |
|
|
| module.exports = () => |
| new PassportLocalStrategy( |
| { |
| usernameField: 'email', |
| passwordField: 'password', |
| session: false, |
| passReqToCallback: true, |
| }, |
| passportLogin, |
| ); |
|
|