| | 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, |
| | ); |
| |
|