Аутентификация пользователей в Node.js с Passport.js и PostgreSQL

Важно: в примерах используется хеширование паролей через bcryptjs и хранение только хэшей паролей. Никогда не сохраняйте пароли в открытом виде.
Что вы реализуете
- Таблица users в PostgreSQL с полями id, email и password.
- Node.js-сервер на Express с эндпоинтами /auth/signup и /auth/login.
- Вспомогательные функции для работы с базой: проверка существования email, создание пользователя, сравнение паролей.
- Конфигурация Passport с локальными стратегиями для регистрации и входа.
- Базовые рекомендации по тестированию, безопасности и соответствию требованиям конфиденциальности.
Почему этот подход работает
Passport.js отделяет логику аутентификации от маршрутов и упрощает замену стратегии (например, на JWT или OAuth). PostgreSQL обеспечивает надёжное хранение и ACID-свойства для пользовательских данных. bcryptjs используется для безопасного хеширования паролей.
Пререквизиты
- Node.js (LTS) и npm
- PostgreSQL
- Базовые знания JavaScript и SQL
Создание базы данных и таблицы пользователей
В psql создайте базу данных nodeapp и таблицу users. Рекомендуется использовать VARCHAR вместо CHAR для полей, чтобы избежать лишних пробелов на конце строки.
CREATE DATABASE nodeapp;CREATE TABLE users (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email VARCHAR(128) UNIQUE NOT NULL,
password CHAR(60) NOT NULL
);Примечание: CHAR(60) выбран потому, что стандартный bcrypt-хэш имеет длину около 60 символов. При желании используйте VARCHAR(255) для расширенной гибкости.
Создание Node-сервера (Express)
Создайте папку проекта, инициализируйте npm и установите необходимые пакеты:
mkdir postgres-auth
npm init -y
npm install expressПример базового сервера в index.js:
const express = require("express");
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.listen(3000, () => console.log("Listening on port 3000"));Запустите сервер: в консоли появится “Listening on port 3000”.
Подключение к PostgreSQL (node-postgres)
Установите драйвер:
npm install pgПример файла db.js, который создаёт клиент и подключается к базе:
const { Client } = require("pg");
const { user, host, database, password, port } = require("./dbConfig");
const client = new Client({
user,
host,
database,
password,
port,
});
client.connect();
module.exports = client;Файл dbConfig.js с локальными параметрами подключения (не храните реальные пароли в репозитории):
module.exports = {
user: "postgres",
host: "localhost",
database: "nodeapp",
password: "yourPassword",
port: 5432,
};Безопасность: используйте переменные окружения (process.env) и не коммитьте секреты в VCS.
Помощники для работы с базой (helper.js)
Создайте отдельный модуль helper.js, который инкапсулирует операции с пользователями. Это облегчает тестирование и позволяет избежать дублирования кода.
Установите bcryptjs:
npm install bcryptjsПример helper.js:
const client = require("./db.js");
const bcrypt = require("bcryptjs");
const emailExists = async (email) => {
const data = await client.query("SELECT * FROM users WHERE email=$1", [
email,
]);
if (data.rowCount == 0) return false;
return data.rows[0];
};
const createUser = async (email, password) => {
const salt = await bcrypt.genSalt(10);
const hash = await bcrypt.hash(password, salt);
const data = await client.query(
"INSERT INTO users(email, password) VALUES ($1, $2) RETURNING id, email, password",
[email, hash]
);
if (data.rowCount == 0) return false;
return data.rows[0];
};
const matchPassword = async (password, hashPassword) => {
const match = await bcrypt.compare(password, hashPassword);
return match;
};
module.exports = { emailExists, createUser, matchPassword };Пояснение:
- emailExists возвращает false или объект пользователя из БД.
- createUser хеширует пароль и сохраняет запись.
- matchPassword сравнивает введённый пароль с хешем.
Установка и настройка Passport.js
Passport.js — это middleware для Node.js, который поддерживает множество стратегий аутентификации. Для локальной (email/password) аутентификации используем passport-local.
Установка:
npm install passport
npm install passport-localСоздайте файл passportConfig.js и подключите локальную стратегию и вспомогательные функции:
const LocalStrategy = require("passport-local");
const { emailExists, createUser, matchPassword } = require("./helper");
module.exports = (passport) => {
passport.use(
"local-signup",
new LocalStrategy(
{
usernameField: "email",
passwordField: "password",
},
async (email, password, done) => {
try {
const userExists = await emailExists(email);
if (userExists) {
return done(null, false);
}
const user = await createUser(email, password);
return done(null, user);
} catch (error) {
done(error);
}
}
)
);
passport.use(
"local-login",
new LocalStrategy(
{
usernameField: "email",
passwordField: "password",
},
async (email, password, done) => {
try {
const user = await emailExists(email);
if (!user) return done(null, false);
const isMatch = await matchPassword(password, user.password);
if (!isMatch) return done(null, false);
return done(null, { id: user.id, email: user.email });
} catch (error) {
return done(error, false);
}
}
)
);
};Пояснение: каждая стратегия вызывает done(error, user, info). Мы отключили сессии (см. ниже про JWT и сессии).
Маршруты регистрации и входа
В файле server.js подключите Passport и зарегистрируйте маршруты:
const express = require("express");
const app = express();
const passport = require("passport");
require("./passportConfig")(passport);
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.post(
"/auth/signup",
passport.authenticate("local-signup", { session: false }),
(req, res, next) => {
res.json({
user: req.user,
});
}
);
app.post(
"/auth/login",
passport.authenticate("local-login", { session: false }),
(req, res, next) => {
res.json({ user: req.user });
}
);
app.listen(3000, () => console.log("Listening on port 3000"));Отключённые сессии ({ session: false }) — это удобный вариант для API, где вы хотите возвращать токен (например, JWT) и хранить состояние на клиенте.
Альтернативы хранению состояния
- Сессии на сервере (express-session + connect-pg-simple): состояние хранится в базе/Redis и управляется сервером.
- JWT (JSON Web Token): сервер выдаёт токен при логине, клиент хранит его и отправляет в Authorization заголовке.
Выбор зависит от требований: JWT проще масштабировать (без состояния на сервере), сессии удобнее для быстрого отзыва доступа и централизации контроля.
Проверка API: unit-тесты и интеграционные тесты
Для тестирования используйте Jest и супертест (supertest) для HTTP-запросов к серверу.
Пример тестов (упрощённо):
npm install --save-dev jest supertestПример теста регистрации:
const request = require("supertest");
const app = require("../server"); // экспортируйте app в server.js
describe("POST /auth/signup", () => {
it("создаёт нового пользователя", async () => {
const res = await request(app).post('/auth/signup').send({
email: 'test@example.com',
password: 'Password123'
});
expect(res.statusCode).toBe(200);
expect(res.body.user).toHaveProperty('email', 'test@example.com');
});
});Критерии приёмки:
- Успешная регистрация возвращает 200 и объект user без поля password.
- Повторная регистрация с тем же email возвращает ошибку/403.
- Успешный вход возвращает user.id и user.email.
Важно: в тестовой среде используйте отдельную тестовую базу и сбрасывайте данные между тестами.
Риски и меры безопасности
- Хранение паролей: храните только хэши (bcrypt). Не храните открытые пароли.
- Brute-force: используйте ограничение частоты (rate limiting) для эндпоинтов авторизации.
- HTTPS: всегда шифруйте трафик в продакшене.
- Заголовки безопасности: используйте helmet для общих заголовков безопасности.
- Валидация входных данных: проверяйте формат email и минимальную длину пароля.
- CORS: настраивайте разрешённые источники для API.
- Логи: не логируйте пароли или токены.
Рекомендации по пакетам:
- express-rate-limit для ограничения запросов.
- helmet для безопасных HTTP-заголовков.
- express-validator или Joi для валидации схемы входящих данных.
Соответствие конфиденциальности (GDPR-подобные требования)
- Минимизируйте собираемые данные: храните только необходимые поля.
- Удаление по запросу: реализуйте механизм удаления учётной записи и связанных данных.
- Право на переносимость: предоставляйте экспорт данных по запросу.
- Обработка паролей: используйте надёжное хеширование и защиту от утечек.
Юридическая заметка: при работе с персональными данными уточните требования локального законодательства и требования GDPR, если они применимы.
Модель зрелости аутентификации (уровни)
- Уровень 0: без аутентификации — открытые API.
- Уровень 1: базовая локальная аутентификация (email/password, bcrypt).
- Уровень 2: сессии с серверным хранилищем и возможностью отзыва.
- Уровень 3: JWT с ротацией рефреш-токенов и защитой от CSRF.
- Уровень 4: многофакторная аутентификация (MFA), мониторинг аномалий.
Этот пример покрывает уровень 1–2 и служит отправной точкой для развития.
Шаблон playbook для быстрой настройки (SOP)
- Настроить PostgreSQL и создать базу nodeapp.
- Инициализировать проект npm и установить express, pg, passport, passport-local, bcryptjs.
- Создать dbConfig.js и db.js (использовать переменные окружения в проде).
- Создать helper.js с функциями emailExists, createUser, matchPassword.
- Создать passportConfig.js с локальными стратегиями.
- Создать server.js, экспортировать app для тестов и начать прослушивание порта.
- Добавить rate limiting и helmet.
- Написать unit и интеграционные тесты (Jest + supertest).
- Настроить CI для запуска тестов и проверок безопасности.
- Развернуть на хостинге, настроить HTTPS и мониторинг.
Контрольный список ролей
Разработчик:
- Реализовать helper.js и passportConfig.js
- Написать unit-тесты
- Не хранить секреты в репозитории
DevOps/Инфраструктура:
- Настроить базу данных и резервное копирование
- Обеспечить HTTPS и доступность
- Мониторинг и алерты
Инженер по безопасности:
- Провести ревью зависимостей
- Настроить rate limiting и WAF при необходимости
Тестовые сценарии и критерии приёмки
- Успешная регистрация с валидным email и паролем -> 200, user.email возвращён, password не возвращён.
- Регистрация с уже существующим email -> 400/409, тело ответа с описанием ошибки.
- Вход с правильными учетными данными -> 200, возвращается id и email.
- Вход с неправильным паролем -> 401, без деталей о причинах (не раскрывать, что именно неверно).
- Попытка SQL-инъекции в поле email должна быть нейтрализована параметризованными запросами.
Миграции и совместимость
- Для версионирования схемы используйте миграционные инструменты (например, node-pg-migrate, knex migrate, Flyway).
- Убедитесь, что хэши старых паролей корректно распознаются при смене algorithма: добавьте план миграции паролей.
Решение: когда это не подходит
- Если приложение требует масштабной авторизации и делегирования прав — рассмотрите OAuth2 / OpenID Connect и стороннего провайдера (Auth0, Keycloak).
- Для полностью безсерверных решений существует необходимость в других паттернах авторизации (например, Cognito или Firebase Auth).
Диаграмма выбора стратегии (Mermaid)
flowchart TD
A[Нужно аутентифицировать пользователей?] --> B{Нужны ли сессии на сервере?}
B -- Да --> C[Использовать express-session + хранение в БД/Redis]
B -- Нет --> D{Требуется масштабирование без состояния?}
D -- Да --> E[Использовать JWT с ротацией рефреш-токенов]
D -- Нет --> F[Использовать Passport.js с локальными стратегиями]
C --> G[Добавить защиту от CSRF и управление сессиями]
E --> H[Реализовать чёрный список / ротацию токенов]
F --> I[Подключить bcryptjs и защиту от brute-force]Примеры команд развёртывания и мониторинга
- Перенос конфигурации в переменные окружения:
- DATABASE_URL, JWT_SECRET, NODE_ENV
- Настройка PM2 или systemd для процесса Node.
- Настройка ежедневных бэкапов PostgreSQL.
Социальные превью
OG title: Аутентификация в Node.js с Passport.js и PostgreSQL OG description: Пошаговое руководство по добавлению безопасной аутентификации (регистрация, вход) в приложение на Node.js с PostgreSQL и Passport.js.
Краткое объявление (100–200 слов): Если вам нужно быстро добавить безопасную аутентификацию в Node-приложение, это руководство даст рабочие шаги: таблицу users в PostgreSQL, вспомогательные функции для работы с БД, конфигурацию Passport.js и маршруты регистрации/входа. Также рассмотрены альтернативы (JWT, сессии), рекомендации по безопасности и тестированию.
Итог
Ключевые выводы:
- Passport.js упрощает реализацию локальной аутентификации и легко заменяется на другие стратегии.
- Храните только хэши паролей (bcrypt) и используйте параметризованные запросы.
- Тестируйте эндпоинты автоматизированно и используйте отдельную тестовую базу.
- Добавьте защитные меры: rate limiting, HTTPS, helmet и валидацию входящих данных.
Важно: начинайте с базовой реализации и постепенно улучшайте зрелость системы (ротация токенов, MFA, аудит), ориентируясь на требования безопасности и удобство пользователей.
Похожие материалы
TOML в Rust: чтение и запись
Анализ тональности на Python с VADER и Tkinter
Проверить прокси в Windows
Темы рабочего стола в Ubuntu 18.04 LTS
Что делать, если Logitech G Pro Wireless не работает