Node.js как разрешить пользователю входить в систему с помощью имени пользователя ИЛИ электронной почты

#node.js #authentication

#node.js #аутентификация

Вопрос:

У меня есть система входа / регистрации в Node, которая работает просто отлично. Однако на данный момент пользователь может входить в систему только через свою электронную почту, но я хочу, чтобы пользователь мог входить в систему либо по электронной почте, либо по имени пользователя. Я считаю, что проблема существует в моей модели (моя система использует подход MVC), но я также включу соответствующий код контроллера.

userController.js

 module.exports.post_login = async (req, res) => {
  const { email, password } = req.body;

  try {
    const user = await User.login(email, password);
    const token = createToken(user._id);
    res.cookie('jwt', token, { httpOnly: true, maxAge: maxAge * 1000 });
    res.status(200).json({ user: user._id });
  }
  catch (err) {
    const errors = handleErrors(err);
    res.status(400).json({ errors });
  }

}
  

userModel.js

 const userSchema = new mongoose.Schema({
  //some user schema code that works just fine 
});

userSchema.pre('save', async function(next) {
  const salt = await bcrypt.genSalt();
  this.password = await bcrypt.hash(this.password, salt);
  next();
});

// static method to login user
userSchema.statics.login = async function(email, password) {
  const user = await this.findOne({ email });
  if (user) {
    const auth = await bcrypt.compare(password, user.password);
    if (auth) {
      return user;
    }
    throw Error('incorrect password');
  }
  throw Error('incorrect email');
};

const User = mongoose.model('user', userSchema);

module.exports = User;
  

Здесь позвольте мне включить файл просмотра. Извините за это.

login.ejs

         <form action='/register' class="form" method='POST'>
        <div id='login'>
            <h1>Greetings!<h1>
                <h4>We're so happy you've returned.<h4>
                <div class="input-group">
                <input type="text" name="email" autocomplete='off'
                placeholder="Username or Email Address" required />
                </div>
                <div class="email error"></div>
                <div class="input-group">
                <input type="password" name="password"
                placeholder="Password" autocomplete='off'
                required />
                <div class="password error"></div>
                </div>
                <button type='submit' class='btn'><span>Login</span></button><br />
            <p>
            <h6><u><a href="forgot-password.php">Forgot Password?</a></u></h6>
        </form>
    </div>
    <script>
      const form = document.querySelector('form')
      //const usernameError = document.querySelector('.username.error')
      const emailError = document.querySelector('.email.error')
      const passwordError = document.querySelector('.password.error')

      form.addEventListener('submit', async (e)=>{
        e.preventDefault();
        //reset errors
        /*usernameError.textContent =*/ emailError.textContent = passwordError.textContent = '';
        // get values
        //const username = form.username.value
        const email = form.email.value
        const password = form.password.value

        try {
          const res = await fetch('/login', {
            method: 'POST',
            body: JSON.stringify({ email, password }),
            headers: {'Content-Type': 'application/json'}
          })
          const data = await res.json()
          console.log(data)
          if(data.errors){
            //usernameError.textContent = data.errors.username 
            emailError.textContent = data.errors.email
            passwordError.textContent = data.errors.password
          }
          if(data.user){
            location.assign('/profile')
          }
        }
        catch (err) {
          console.log(err)
        }
      })
    </script>
  

Я понимаю, что в контроллере мне нужно добавить имя пользователя при запросе тела и в инструкции try, но я подожду, чтобы увидеть, что вы, ребята / девушки, предлагаете.

Проблема, вероятно, существует в модели при методе findOne(): const user = await this.findOne({ email }); .

Я просто не уверен, какой метод использовать вместо этого, и я бы предпочел отложить ваши предложения о том, как включить массив в это место (я предполагаю, что мне понадобится username, email тип массива).). Любая помощь будет оценена. Если вам нужно больше кода, пожалуйста, дайте мне знать, и я его предоставлю. Спасибо!

Комментарии:

1. $or: не работает. Проблема в том, что в login.ejs я определяю «email» как const email = form.email.value , но у меня нет ничего в форме для определения имени пользователя. Как мне извлечь имя пользователя из коллекции MongoDB, а не из формы?

Ответ №1:

вам нужно найти пользователя по его имени пользователя или электронной почте, я думаю, это может помочь заменить

 const user = await this.findOne({ email });
  

Автор:

 const user = await this.findOne(
    {
        $or: [
             { email },{ username }
        ]
    }
)
  

имя пользователя должно считываться из тела запроса, как вы говорите

Комментарии:

1. Спасибо, я еще не запустил код, но завтра я это сделаю. Просто сообщаю вам, что я включил файл просмотра (login.ejs), который, я думаю, также может помочь. Извините, я забыл об этом.

Ответ №2:

Если я правильно понял, у вас есть один ввод, который может содержать имя пользователя или адрес электронной почты. Я бы предложил получить это значение и получить пользователя с помощью оператора or.

таким образом, вы передаете только входное значение в качестве параметра и извлекаете пользователя.

 const user = await this.findOne({ $or: [ {email: value}, {username: value} ] });
  

Комментарии:

1. Спасибо, я еще не запустил код, но завтра я это сделаю. Просто сообщаю вам, что я включил файл просмотра (login.ejs), который, я думаю, также может помочь. Извините, я забыл об этом.