Гид по технологиям

Аутентификация с токенами в Next.js: реализация на JWT

6 min read Security Обновлено 30 Dec 2025
Аутентификация JWT в Next.js
Аутентификация JWT в Next.js

TL;DR

Коротко: в Next.js можно реализовать токенную аутентификацию с помощью JWT и библиотеки jose, сохраняя токен в cookie. В статье показан шаг‑за‑шаг процесс: создание формы входа, API‑эндпоинта с подписью JWT, средство проверки токена, middleware для защиты маршрутов и клиентский хук для состояния аутентификации. Внизу — чеклисты, сценарии тестирования и рекомендации по безопасности.

Важно: представленный код — демонстрационный. Для продакшна используйте надёжное хранилище пользователей, защищённые cookie и механизмы обновления токенов.

Мужчина за столом печатает на ноутбуке с кодом на экране.

Token authentication — популярная стратегия защиты веб‑ и мобильных приложений от неавторизованного доступа. В Next.js можно использовать готовые решения вроде NextAuth, но при необходимости получить полный контроль, вы можете реализовать собственную систему с JSON Web Tokens (JWT).

Ниже приведён подробный туториал для Next.js 13 (директория app). Все шаги — от инициализации проекта до защиты маршрутов через middleware — с примерами кода.

Основные понятия в одну фразу

  • JWT: компактный токен для передачи утверждений о пользователе, подписывается секретом.
  • HttpOnly cookie: cookie, недоступные JavaScript, снижают риск XSS.
  • Middleware: серверный промежуточный код, который проверяет запросы перед рендерингом.

Альтернативные подходы (коротко)

  • NextAuth: готовое решение с OAuth, база провайдеров и сессиями.
  • Сеансовая аутентификация (server sessions): с хранением сессий на сервере или в Redis.
  • OAuth2 / OpenID Connect: для федеративного входа через внешнего провайдера.

Требования и зависимости

Установите Next.js и необходимые пакеты:

npx create-next-app@latest next-auth-jwt --experimental-app

Затем в проекте установите:

npm install jose universal-cookie
  • jose — утилиты для создания и проверки JWT.
  • universal-cookie — удобная работа с cookie на клиенте и сервере.

Структура проекта Next.js 13 в VS Code.

1. Интерфейс формы входа

Создайте src/app/login/page.js и добавьте компонент страницы с формой входа. Компонент должен быть клиентским (use client) — так Next.js отделяет код, выполняющийся в браузере.

"use client";
import { useRouter } from "next/navigation";

export default function LoginPage() {
  const router = useRouter();

  const handleSubmit = async (event) => {
    event.preventDefault();
    const formData = new FormData(event.target);
    const username = formData.get("username");
    const password = formData.get("password");
    const res = await fetch("/api/login", {
      method: "POST",
      body: JSON.stringify({ username, password }),
      headers: { "content-type": "application/json" },
    });
    const { success } = await res.json();
    if (success) {
      router.push("/protected");
      router.refresh();
    } else {
      alert("Login failed");
    }
  };

  return (
    
); }

Примечание: use client указывает Next.js, что компонент рендерится на клиенте. handleSubmit выполняется в браузере.

2. API‑эндпоинт входа и генерация JWT

Создайте src/app/api/login/route.js. В примере ниже используются мок‑данные: username “admin” и password “admin”. В реальном проекте сравнивайте с базой пользователей и хешами паролей.

import { SignJWT } from "jose";
import { NextResponse } from "next/server";
import { getJwtSecretKey } from "@/libs/auth";

export async function POST(request) {
  const body = await request.json();
  if (body.username === "admin" && body.password === "admin") {
    const token = await new SignJWT({ username: body.username })
      .setProtectedHeader({ alg: "HS256" })
      .setIssuedAt()
      .setExpirationTime("30s")
      .sign(getJwtSecretKey());

    const response = NextResponse.json(
      { success: true },
      { status: 200, headers: { "content-type": "application/json" } }
    );
    response.cookies.set({ name: "token", value: token, path: "/" });
    return response;
  }
  return NextResponse.json({ success: false });
}

Здесь мы подписываем полезную нагрузку (payload) токена и отправляем токен в cookie. В следующем разделе — функции подписания/проверки.

3. Функции подписи и верификации токена

Создайте src/libs/auth.js и положите туда логику работы с секретом и верификации токена.

import { jwtVerify } from "jose";

export function getJwtSecretKey() {
  const secret = process.env.NEXT_PUBLIC_JWT_SECRET_KEY;
  if (!secret) {
    throw new Error("JWT Secret key is not matched");
  }
  return new TextEncoder().encode(secret);
}

export async function verifyJwtToken(token) {
  try {
    const { payload } = await jwtVerify(token, getJwtSecretKey());
    return payload;
  } catch (error) {
    return null;
  }
}

Добавьте файл .env в корень проекта со значением секретного ключа (здесь показан пример):

NEXT_PUBLIC_JWT_SECRET_KEY=your_secret_key

Важно: не храните секрет в публичном репозитории.

4. Защищённая страница

Создайте src/app/protected/page.js, к которой будет доступ только у авторизованных пользователей:

export default function ProtectedPage() {
  return 

Very protected page

; }

5. Клиентский хук для состояния аутентификации

Хук позволяет компонентам узнавать, авторизован ли пользователь. Создайте src/hooks/useAuth/index.js:

"use client";
import React from "react";
import Cookies from "universal-cookie";
import { verifyJwtToken } from "@/libs/auth";

export function useAuth() {
  const [auth, setAuth] = React.useState(null);

  const getVerifiedtoken = async () => {
    const cookies = new Cookies();
    const token = cookies.get("token") ?? null;
    const verifiedToken = token ? await verifyJwtToken(token) : null;
    setAuth(verifiedToken);
  };

  React.useEffect(() => {
    getVerifiedtoken();
  }, []);

  return auth;
}

Пример использования в app/page.js:

"use client";
import { useAuth } from "@/hooks/useAuth";
import Link from "next/link";

export default function Home() {
  const auth = useAuth();
  return (
    <>
      

Public Home Page

); }

6. Middleware для защиты маршрутов

Создайте src/middleware.js, чтобы перенаправлять неавторизованных пользователей на страницу входа и предотвращать доступ авторизованных к странице входа.

import { NextResponse } from "next/server";
import { verifyJwtToken } from "@/libs/auth";

const AUTH_PAGES = ["/login"];

const isAuthPages = (url) => AUTH_PAGES.some((page) => page.startsWith(url));

export async function middleware(request) {
  const { url, nextUrl, cookies } = request;
  const { value: token } = cookies.get("token") ?? { value: null };
  const hasVerifiedToken = token && (await verifyJwtToken(token));
  const isAuthPageRequested = isAuthPages(nextUrl.pathname);

  if (isAuthPageRequested) {
    if (!hasVerifiedToken) {
      const response = NextResponse.next();
      response.cookies.delete("token");
      return response;
    }
    const response = NextResponse.redirect(new URL(`/`, url));
    return response;
  }

  if (!hasVerifiedToken) {
    const searchParams = new URLSearchParams(nextUrl.searchParams);
    searchParams.set("next", nextUrl.pathname);
    const response = NextResponse.redirect(new URL(`/login?${searchParams}`, url));
    response.cookies.delete("token");
    return response;
  }

  return NextResponse.next();
}

export const config = { matcher: ["/login", "/protected/:path*"] };

Middleware — серверный «страж», который решает, пускать ли пользователя дальше.

Критерии приёмки

  • Пользователь с корректными данными получает cookie с токеном и переходит на /protected.
  • Попытка зайти на /protected без токена перенаправляет на /login с параметром next.
  • Просроченный или поддельный токен приводит к удалению cookie и редиректу на /login.
  • Страница /login доступна только неавторизованным; авторизованные пользователи перенаправляются на /.

Сценарии тестирования (test cases)

  • Успешный вход: POST /api/login с валидным набором логин/пароль возвращает success:true и ставит cookie.
  • Неверный логин: возвращается success:false.
  • Просроченный токен: middleware удаляет token и перенаправляет на /login.
  • Попытка доступа к /protected без токена: redirect на /login?next=/protected.

Чеклисты по ролям

  • Разработчик:
    • Реализовать проверку пароля через безопасный хеш (bcrypt/argon2).
    • Не логировать секретные значения.
    • Настроить refresh token, если нужен длительный доступ.
  • DevOps:
    • Хранить секреты в защищённом хранилище (Vault, Secrets Manager).
    • Настроить HTTPS и HSTS.
  • Инженер по безопасности:
    • Проверить конфигурацию cookie (HttpOnly, Secure, SameSite).
    • Провести тесты на XSS и CSRF.

Безопасность и рекомендации по харднингу

  • Используйте HttpOnly и Secure cookie для хранения токенов вместо localStorage. Это снижает риск XSS.
  • Устанавливайте SameSite=strict или lax в зависимости от сценария, чтобы уменьшить угрозы CSRF.
  • Делайте короткие сроки жизни JWT и реализуйте refresh token с возможностью отзыва.
  • Хешируйте пароли на сервере (bcrypt/argon2).
  • Ограничьте попытки входа (rate limiting, account lockout).
  • Подпись токена храните в безопасном месте, не в репозитории.
  • Проводите аудит зависимостей и регулярно обновляйте jose и другие пакеты.

Когда подход на JWT может не подойти (контрпримеры)

  • Если нужно мгновенно аннулировать доступ по всем сессиям — классические JWT без централизованного хранения сложнее отозвать.
  • Для сценариев высокого соответствия (compliance) может потребоваться серверное хранение сессий и журнала доступа.
  • Если приложение подразумевает очень частую смену прав доступа, имеет смысл использовать серверные сессии или short lived tokens + introspection.

Примеры curl для отладки

  • Попытка входа:
curl -i -X POST http://localhost:3000/api/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"admin"}'
  • Доступ к защищённому маршруту (cookie нужно передать вручную при отладке):
curl -i http://localhost:3000/protected --cookie "token=YOUR_TOKEN"

Приватность и соответствие требованиям (GDPR и похожие)

  • Минимизируйте персональные данные в JWT: включайте только необходимую информацию.
  • Храните PII отдельно и защищённо; избегайте большого набора персональных данных в токене.
  • Если требуется согласие на cookie, показывайте пользователю явную подсказку и давайте выбор.

Ментальные модели и эвристики

  • Short lived token + refresh token: уменьшает окно риска при краже токена.
  • Публичный ключ/приватный ключ (asymmetric) полезен, если подписывать токены должен один сервис, а проверять — многие.
  • «Принцип наименьших привилегий»: токен должен давать ровно те права, которые нужны.

Краткая методология развертывания

  1. Разработать API для аутентификации и авторизации.
  2. Выбрать формат токенов и место хранения (cookie vs storage).
  3. Реализовать подпись и верификацию токенов.
  4. Настроить middleware для защиты маршрутов.
  5. Провести нагрузочное и безопасность‑тестирование.

Краткий глоссарий

  • JWT: JSON Web Token — компактный способ передачи утверждений в виде подписанного JSON.
  • HttpOnly: атрибут cookie, запрещающий доступ через JavaScript.
  • Refresh token: токен для получения нового access token, обычно хранится более защищённо.

Итог и рекомендации

  • Реализация на JWT даёт гибкость, но накладывает ответственность: управление сроками жизни, отзыв токенов и безопасность хранилища секретов.
  • Для большинства приложений начните с короткого срока жизни access token и безопасных cookie.
  • Рассмотрите готовые решения (NextAuth) при ограниченных ресурсах на поддержание собственной системы.

Короткое объявление для команды (пример)

Мы добавили базовую JWT‑аутентификацию в Next.js: форма входа, серверная генерация токенов, клиентский хук и middleware для защиты маршрутов. Требуется доработать хранение паролей, настроить refresh token и усилить конфигурацию cookie перед релизом.


Примечание: код в примере служит учебной целью. Перед выпуском в продакшн выполните аудит безопасности и доработайте управление сессиями.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Как установить macOS Ventura beta на Mac
macOS

Как установить macOS Ventura beta на Mac

Mail на Mac: настройка и руководство
macOS

Mail на Mac: настройка и руководство

Как отменить отправку письма на Mac
macOS

Как отменить отправку письма на Mac

Запланировать email на Mac — способы и инструкция
Productivity

Запланировать email на Mac — способы и инструкция

Click: создание CLI на Python
Python

Click: создание CLI на Python

Chicken of the VNC — VNC‑клиент для macOS
Инструменты

Chicken of the VNC — VNC‑клиент для macOS