Интеграция с Passport.js: стратегия local

Стратегия local в Passport.js используется для аутентификации пользователей на основе логина и пароля, передаваемых непосредственно в приложение. В отличие от OAuth-стратегий, здесь вся ответственность за хранение, проверку и безопасность паролей лежит на серверной части.

Ключевой аспект такой схемы — корректная работа с паролями через хэширование. Прямое хранение паролей недопустимо, поэтому используется библиотека password-hash или аналогичные решения (bcrypt, argon2). Основная идея — хранить не сам пароль, а его криптографический отпечаток.


Принцип работы local-стратегии

Local-стратегия строится вокруг проверки двух значений:

  • идентификатор пользователя (обычно email или username)
  • пароль

При попытке входа Passport вызывает функцию проверки, где происходит:

  1. поиск пользователя в базе
  2. получение сохранённого хэша пароля
  3. сравнение введённого пароля с хэшем

Пример логики:

passport.use(new LocalStrategy(
  function(username, password, done) {
    User.findOne({ username: username }, function (err, user) {
      if (err) return done(err);
      if (!user) return done(null, false);

      // проверка пароля
      if (!verifyPassword(password, user.passwordHash)) {
        return done(null, false);
      }

      return done(null, user);
    });
  }
));

Хэширование паролей с password-hash

Библиотека password-hash предоставляет простой API для создания и проверки хэшей:

const passwordHash = require('password-hash');

const hash = passwordHash.generate('mySecretPassword');

Сгенерированная строка включает алгоритм, соль и сам хэш. Это делает хранение безопаснее, так как одинаковые пароли дают разные хэши.

Проверка выполняется так:

const isValid = passwordHash.verify('mySecretPassword', hash);

Регистрация пользователя и хранение пароля

При создании пользователя важно хэшировать пароль до сохранения в базу данных.

function registerUser(username, password) {
  const hashedPassword = passwordHash.generate(password);

  const user = new User({
    username: username,
    passwordHash: hashedPassword
  });

  return user.save();
}

Критический момент — хэширование должно происходить только на сервере. Любые попытки хранить пароль в открытом виде приводят к уязвимостям.


Проверка пароля в стратегии local

Интеграция с Passport.js требует явного вызова проверки пароля внутри стратегии:

passport.use(new LocalStrategy(
  function(username, password, done) {
    User.findOne({ username: username }, function(err, user) {
      if (err) return done(err);
      if (!user) return done(null, false);

      const isValid = passwordHash.verify(password, user.passwordHash);

      if (!isValid) {
        return done(null, false);
      }

      return done(null, user);
    });
  }
));

В этом сценарии Passport не знает ничего о хэшировании — вся логика инкапсулирована в callback-функции.


Сериализация и десериализация пользователя

Для работы с сессиями Passport требует определения двух функций:

passport.serializeUser(function(user, done) {
  done(null, user.id);
});

passport.deserializeUser(function(id, done) {
  User.findById(id, function(err, user) {
    done(err, user);
  });
});

Сериализация сохраняет минимальный идентификатор пользователя в сессии, а десериализация восстанавливает объект пользователя при каждом запросе.


Интеграция с Express

Типичный стек использования local-стратегии включает Express middleware:

const express = require('express');
const passport = require('passport');
const session = require('express-session');

const app = express();

app.use(express.urlencoded({ extended: true }));
app.use(session({ secret: 'secretKey', resave: false, saveUninitialized: false }));
app.use(passport.initialize());
app.use(passport.session());

Маршрут логина:

app.post('/login',
  passport.authenticate('local', {
    successRedirect: '/dashboard',
    failureRedirect: '/login'
  })
);

Ошибки при работе с хэшированием паролей

Распространённые проблемы в реализации local-стратегии:

  • сравнение строк вместо использования verify
  • хранение пароля в базе без хэширования
  • повторное хэширование уже хэшированного значения
  • использование устаревших алгоритмов без соли
  • отсутствие обработки ошибок при поиске пользователя

Особенно критична ошибка прямого сравнения:

if (password === user.passwordHash) // ошибка

Повышение уровня безопасности

Хотя password-hash обеспечивает базовую защиту, в реальных системах часто применяются более устойчивые алгоритмы:

  • bcrypt с настройкой salt rounds
  • argon2 как современный стандарт

Дополнительно учитываются:

  • ограничение попыток входа
  • задержки при неправильных попытках
  • логирование подозрительной активности
  • использование HTTPS для передачи данных

Обработка ошибок аутентификации

Passport позволяет детализировать причину отказа:

return done(null, false, { message: 'Неверный пароль' });

Это сообщение может использоваться для отображения UI, но не должно раскрывать, существует ли пользователь в системе.


Структура типового потока аутентификации

Local-стратегия в связке с хэшированием формирует последовательность:

  1. пользователь отправляет логин и пароль
  2. сервер ищет пользователя
  3. извлекается хэш пароля
  4. выполняется проверка через password-hash.verify
  5. при успехе создаётся сессия
  6. Passport сериализует идентификатор пользователя
  7. последующие запросы используют восстановление через десериализацию

Роль middleware в цепочке Passport.js

Каждый этап аутентификации в Passport.js реализуется через middleware. Это позволяет интегрировать проверку пользователя в HTTP-поток без явного управления состоянием.

Middleware-архитектура обеспечивает:

  • модульность проверки
  • повторное использование стратегий
  • независимость от бизнес-логики приложения

Работа с несколькими стратегиями

Local-стратегия часто используется параллельно с OAuth:

passport.use('local', new LocalStrategy(...));
passport.use('google', new GoogleStrategy(...));

Это позволяет комбинировать традиционную авторизацию и внешние провайдеры.


Хэширование как базовый слой безопасности

Хэширование пароля — это не просто техническая деталь, а фундаментальная часть модели безопасности:

  • предотвращает утечку реальных паролей
  • защищает при компрометации базы данных
  • усложняет атаки методом перебора

Даже при использовании password-hash важно понимать, что безопасность зависит не только от алгоритма, но и от архитектуры всей системы аутентификации.