← Назад к вопросам

Как реализовать аутентификацию с 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

  1. Хранение токенов — httpOnly cookies или localStorage
  2. Передача токена — Authorization: Bearer TOKEN
  3. Ротация токенов — обновлять перед истечением
  4. HTTPS только — всегда использовать
  5. Длинные secrets — минимум 32 символа
  6. Время жизни — access 15м, refresh 7d
  7. Черный список — logout удаляет refresh токен
  8. Secrets в .env — никогда не коммитить
Как реализовать аутентификацию с JWT в Node.js? | PrepBro