← Назад к вопросам
Как реализовать аутентификацию с JWT в Node.js?
2.2 Middle🔥 291 комментариев
#API и сетевые протоколы#Безопасность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Аутентификация с JWT в Node.js
JWT (JSON Web Token) — это стандартный способ передачи данных между клиентом и сервером в защищённом виде.
Структура JWT
JWT состоит из трёх частей: header.payload.signature
Установка зависимостей
npm install jsonwebtoken express bcryptjs dotenv
Генерация и проверка JWT
const jwt = require("jsonwebtoken");
const ACCESS_TOKEN_SECRET = process.env.ACCESS_TOKEN_SECRET;
const REFRESH_TOKEN_SECRET = process.env.REFRESH_TOKEN_SECRET;
// Генерация access токена
function generateAccessToken(userId) {
return jwt.sign(
{ userId, role: "user" },
ACCESS_TOKEN_SECRET,
{ expiresIn: "15m" }
);
}
// Генерация refresh токена
function generateRefreshToken(userId) {
return jwt.sign(
{ userId },
REFRESH_TOKEN_SECRET,
{ expiresIn: "7d" }
);
}
// Проверка токена
function verifyAccessToken(token) {
try {
return jwt.verify(token, ACCESS_TOKEN_SECRET);
} catch (error) {
if (error.name === "TokenExpiredError") {
throw new Error("Token expired");
}
throw new Error("Invalid token");
}
}
Middleware для проверки токена
const authenticateToken = (req, res, next) => {
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({ error: "Access token required" });
}
try {
const decoded = jwt.verify(token, ACCESS_TOKEN_SECRET);
req.userId = decoded.userId;
req.user = decoded;
next();
} catch (error) {
if (error.name === "TokenExpiredError") {
return res.status(401).json({ error: "Token expired" });
}
return res.status(403).json({ error: "Invalid token" });
}
};
Регистрация и вход
const express = require("express");
const bcrypt = require("bcryptjs");
const app = express();
app.use(express.json());
const users = new Map();
// Регистрация
app.post("/auth/register", async (req, res) => {
try {
const { email, password, name } = req.body;
if (users.has(email)) {
return res.status(409).json({ error: "User already exists" });
}
const hashedPassword = await bcrypt.hash(password, 10);
const user = {
userId: Date.now().toString(),
email,
name,
password: hashedPassword,
};
users.set(email, user);
res.status(201).json({
message: "User registered successfully",
userId: user.userId,
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Вход
app.post("/auth/login", async (req, res) => {
try {
const { email, password } = req.body;
const user = users.get(email);
if (!user) {
return res.status(401).json({ error: "Invalid credentials" });
}
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
return res.status(401).json({ error: "Invalid credentials" });
}
const accessToken = generateAccessToken(user.userId);
const refreshToken = generateRefreshToken(user.userId);
if (!user.refreshTokens) user.refreshTokens = [];
user.refreshTokens.push(refreshToken);
res.json({
accessToken,
refreshToken,
user: {
userId: user.userId,
email: user.email,
name: user.name,
},
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
Обновление и выход
// Обновление токена
app.post("/auth/refresh", (req, res) => {
try {
const { refreshToken } = req.body;
if (!refreshToken) {
return res.status(401).json({ error: "Refresh token required" });
}
const decoded = jwt.verify(refreshToken, REFRESH_TOKEN_SECRET);
const newAccessToken = generateAccessToken(decoded.userId);
res.json({ accessToken: newAccessToken });
} catch (error) {
res.status(403).json({ error: "Invalid refresh token" });
}
});
// Выход
app.post("/auth/logout", authenticateToken, (req, res) => {
const { refreshToken } = req.body;
for (const user of users.values()) {
if (user.userId === req.userId) {
user.refreshTokens = user.refreshTokens?.filter(t => t !== refreshToken) || [];
break;
}
}
res.json({ message: "Logged out successfully" });
});
Защищённые endpoints
// Получение профиля
app.get("/api/profile", authenticateToken, (req, res) => {
const user = Array.from(users.values()).find(u => u.userId === req.userId);
res.json({
userId: user.userId,
email: user.email,
name: user.name,
});
});
// Текущий пользователь
app.get("/api/me", authenticateToken, (req, res) => {
res.json({ user: req.user });
});
Проверка ролей
const authorizeRole = (...roles) => {
return (req, res, next) => {
if (!req.user || !roles.includes(req.user.role)) {
return res.status(403).json({ error: "Forbidden" });
}
next();
};
};
app.post(
"/api/admin/users",
authenticateToken,
authorizeRole("admin"),
(req, res) => {
res.json({ message: "User created" });
}
);
Best Practices
- Хранение токенов — httpOnly cookies или localStorage
- Передача токена — Authorization: Bearer TOKEN
- Ротация токенов — обновлять перед истечением
- HTTPS только — всегда использовать
- Длинные secrets — минимум 32 символа
- Время жизни — access 15м, refresh 7d
- Черный список — logout удаляет refresh токен
- Secrets в .env — никогда не коммитить