Ответ
Да, реализовывал различные схемы аутентификации и авторизации в приложениях на Node.js.
1. JWT (JSON Web Tokens) для stateless-аутентификации.
Это наиболее частый выбор для RESTful API и микросервисов. Использую библиотеки jsonwebtoken для подписи/верификации и bcrypt для хэширования паролей.
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
// Эндпоинт логина
app.post('/api/auth/login', async (req, res) => {
try {
const { email, password } = req.body;
const user = await UserModel.findOne({ email });
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const isPasswordValid = await bcrypt.compare(password, user.passwordHash);
if (!isPasswordValid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const token = jwt.sign(
{
userId: user.id,
role: user.role // Включаем роль для авторизации
},
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);
res.json({ token, userId: user.id });
} catch (error) {
res.status(500).json({ error: 'Login failed' });
}
});
Защита роутов с помощью middleware:
const authMiddleware = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1]; // Bearer <token>
if (!token) return res.status(401).send('Access denied');
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded; // Добавляем данные пользователя в запрос
next();
} catch (err) {
res.status(400).send('Invalid token');
}
};
app.get('/api/profile', authMiddleware, (req, res) => {
res.json({ profile: '...', userId: req.user.userId });
});
2. Сессии (stateful).
Использовал express-session с хранилищем в Redis для традиционных веб-приложений, где нужны сессии.
3. OAuth 2.0 / OpenID Connect.
Интегрировал сторонние провайдеры (Google, GitHub) с помощью passport.js. Например, для аутентификации через Google:
const GoogleStrategy = require('passport-google-oauth20').Strategy;
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: '/api/auth/google/callback'
},
async (accessToken, refreshToken, profile, done) => {
// Находим или создаем пользователя в нашей БД на основе profile.id
const user = await findOrCreateUser(profile);
done(null, user);
}
));
4. Авторизация на основе ролей (RBAC). После аутентификации проверял права доступа. Часто создавал второй middleware для авторизации:
const authorize = (...allowedRoles) => {
return (req, res, next) => {
if (!req.user || !allowedRoles.includes(req.user.role)) {
return res.status(403).send('Forbidden');
}
next();
};
};
// Только админы могут удалять пользователей
app.delete('/api/users/:id', authMiddleware, authorize('admin'), deleteUserHandler);
Ключевые практики безопасности, которые я соблюдаю:
- Всегда храню пароли в виде хэшей (использую
bcryptс достаточным фактором стоимости). - Использую HTTPS в продакшене.
- Устанавливаю безопасные флаги для куков (HttpOnly, Secure, SameSite) при работе с сессиями.
- Регулярно обновляю секретные ключи (JWT_SECRET) и использую разные ключи для разных сред.
- Ограничиваю частоту попыток входа (rate limiting) для предотвращения брутфорса.
Ответ 18+ 🔞
А, ну это ж классика, ёпта! Каждый второй проект с этим сталкивается — как бы так пользователей пустить, но чтоб не натворили делов. Сам перепробовал кучу вариантов, сейчас разложу по полочкам, без воды.
1. JWT — наш бронепоезд для API.
Это когда тебе похуй на состояние сервера, главное — токен в кармане. Берёшь jsonwebtoken и bcrypt, и вперёд. Смотри, как просто:
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
// Эндпоинт, где народ логинится
app.post('/api/auth/login', async (req, res) => {
try {
const { email, password } = req.body;
const user = await UserModel.findOne({ email });
// Нет юзера? Иди нахуй с такими кредами
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Пароль сверяем — не в лоб, а через хэш, конечно
const isPasswordValid = await bcrypt.compare(password, user.passwordHash);
if (!isPasswordValid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Всё ок? Выписываем пропускную бумажку
const token = jwt.sign(
{
userId: user.id,
role: user.role // Роль пригодится для авторизации
},
process.env.JWT_SECRET, // Секрет, который надо хранить как зеницу ока
{ expiresIn: '7d' } // Чтоб не вечно гулял
);
res.json({ token, userId: user.id });
} catch (error) {
res.status(500).json({ error: 'Login failed' });
}
});
А потом на каждый защищённый роут ставишь такого вышибалу:
const authMiddleware = (req, res, next) => {
// Выковыриваем токен из заголовка
const token = req.headers.authorization?.split(' ')[1]; // Bearer <token>
if (!token) return res.status(401).send('Access denied'); // Нет токена? Пошёл вон!
try {
// Проверяем, не поддельный ли
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded; // Вешаем данные юзера на запрос
next(); // Проходи, браток
} catch (err) {
res.status(400).send('Invalid token'); // Токен просрочен или битый
}
};
// Используем так
app.get('/api/profile', authMiddleware, (req, res) => {
res.json({ profile: '...', userId: req.user.userId });
});
2. Сессии — старый добрый stateful-подход.
Бывает, что JWT — это овердохуища сложности, а тебе нужно просто хранить состояние на сервере. Тогда express-session с Redis в помощь. Традиционно, надёжно, но свой геморрой есть.
3. OAuth 2.0 / OpenID Connect — когда лень свои формы городить.
Пусть Google или GitHub сами аутентифицируют. passport.js — наш проводник в этот мир. Настраиваешь стратегию, и народ входит через сторонние аккаунты. Удобно, пользователям нравится, доверия ебать ноль к тебе лично не нужно.
const GoogleStrategy = require('passport-google-oauth20').Strategy;
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: '/api/auth/google/callback'
},
async (accessToken, refreshToken, profile, done) => {
// Ищешь юзера в своей БД по profile.id или создаёшь нового
const user = await findOrCreateUser(profile);
done(null, user);
}
));
4. Авторизация — кто куда может. Аутентификация — это узнать, кто ты. Авторизация — проверить, можно ли тебе вот это вот. Чаще всего делаю на ролях (RBAC). После middleware для аутентификации ставишь второй — для проверки прав.
// Мидлварь, которая проверяет роль
const authorize = (...allowedRoles) => {
return (req, res, next) => {
// Если юзера нет или его роли нет в списке разрешённых — отказ
if (!req.user || !allowedRoles.includes(req.user.role)) {
return res.status(403).send('Forbidden');
}
next();
};
};
// Пример: удалять пользователей могут только админы
app.delete('/api/users/:id', authMiddleware, authorize('admin'), deleteUserHandler);
А теперь, блядь, про безопасность, без этого нихуя.
- Пароли — только хэш, и только
bcrypt. Никаких MD5, ёпта, это же не 2002-й год на дворе. - HTTPS в продакшене — обязательно. Иначе все твои токены как открытка.
- Куки для сессий — с флагами
HttpOnly,Secure,SameSite. Чтобы не украли через какой-нибудь JS. - Секретные ключи (типа
JWT_SECRET) — храни в переменных окружения, регулярно меняй и не коммить в репу, ядрёна вошь! - Rate limiting на эндпоинты входа — чтобы боты не устроили тебе брутфорс-вечеринку.
Вот, собственно, и вся магия. Главное — не выебывайся со сложными схемами, если проект маленький. Бери что проще и надёжнее. А то знаю я этих архитекторов, которые на пет-проекте OAuth с PKCE внедряют — мудя сплошная.