Работа с JWT токенами в TypeScript: библиотека jsonwebtoken и полное руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

JWT токены в TypeScript: полное руководство с библиотекой jsonwebtoken

JSON Web Token (JWT) — это открытый стандарт (RFC 7519) для создания токенов доступа, основанных на формате JSON. JWT широко используется для аутентификации и обмена информацией между клиентом и сервером. В TypeScript, благодаря строгой типизации, работа с JWT становится особенно удобной и безопасной. В этой статье мы рассмотрим, как использовать популярную библиотеку jsonwebtoken в TypeScript для создания, проверки и управления JWT токенами.

Установка и настройка

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

npm install jsonwebtoken
npm install -D @types/jsonwebtoken

Первый пакет — сама библиотека, второй — типы TypeScript для неё. После установки создадим файл jwt.service.ts, где будем хранить всю логику работы с токенами.

Создание JWT токена

Основная функция jsonwebtoken — sign(). Рассмотрим её использование с типизацией:

import jwt from 'jsonwebtoken';

interface TokenPayload {
  userId: number;
  email: string;
  role: 'user' | 'admin';
}

const createAccessToken = (payload: TokenPayload): string => {
  const secret = process.env.JWT_SECRET || 'default_secret';
  const options: jwt.SignOptions = {
    expiresIn: '15m', // токен действителен 15 минут
    algorithm: 'HS256',
  };

  return jwt.sign(payload, secret, options);
};

Важно: никогда не храните секретный ключ в коде. Используйте переменные окружения (.env файл).

Верификация и декодирование токена

Для проверки подлинности токена используется функция verify(). Она автоматически проверяет подпись и срок действия:

const verifyToken = (token: string): TokenPayload => {
  const secret = process.env.JWT_SECRET || 'default_secret';

  try {
    const decoded = jwt.verify(token, secret) as TokenPayload;
    return decoded;
  } catch (error) {
    if (error instanceof jwt.TokenExpiredError) {
      throw new Error('Токен истёк');
    }
    if (error instanceof jwt.JsonWebTokenError) {
      throw new Error('Неверный токен');
    }
    throw error;
  }
};

Обратите внимание на приведение типа as TokenPayload — это гарантирует, что TypeScript будет знать структуру раскодированных данных.

Refresh-токены: продление сессии

На практике access-токены живут недолго (15-30 минут), поэтому нужен механизм их обновления. Для этого используется refresh-токен с более длительным сроком жизни:

const createRefreshToken = (payload: TokenPayload): string => {
  const secret = process.env.JWT_REFRESH_SECRET || 'refresh_secret';
  const options: jwt.SignOptions = {
    expiresIn: '7d', // неделя
    algorithm: 'HS256',
  };

  return jwt.sign(payload, secret, options);
};

const refreshAccessToken = (refreshToken: string): string => {
  const secret = process.env.JWT_REFRESH_SECRET || 'refresh_secret';

  try {
    const decoded = jwt.verify(refreshToken, secret) as TokenPayload;
    // Можно проверить, не отозван ли refresh-токен в базе данных
    return createAccessToken({
      userId: decoded.userId,
      email: decoded.email,
      role: decoded.role,
    });
  } catch {
    throw new Error('Недействительный refresh-токен');
  }
};

Middleware для Express в TypeScript

Интеграция JWT с Express.js требует создания middleware. Вот типизированная версия:

import { Request, Response, NextFunction } from 'express';

interface AuthRequest extends Request {
  user?: TokenPayload;
}

const authMiddleware = (req: AuthRequest, res: Response, next: NextFunction) => {
  const authHeader = req.headers.authorization;

  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Токен не предоставлен' });
  }

  const token = authHeader.split(' ')[1];

  try {
    const decoded = verifyToken(token);
    req.user = decoded;
    next();
  } catch (error) {
    return res.status(401).json({ error: 'Неавторизованный доступ' });
  }
};

Теперь в защищённых маршрутах можно использовать req.user:

app.get('/profile', authMiddleware, (req: AuthRequest, res) => {
  res.json({ user: req.user });
});

Лучшие практики безопасности

  • Храните секреты в .env — используйте dotenv или встроенные механизмы Node.js.
  • Используйте разные секреты для access и refresh токенов.
  • Устанавливайте разумные сроки жизни: access — 15-30 минут,

Рекомендаций