1. Введение
В любом современном веб-приложении есть защищённые страницы: личный кабинет, страница профиля, разделы для администраторов и т.д. Доступ к ним должен быть только у авторизованных пользователей. Если пользователь не авторизован — его нужно либо перенаправить на страницу входа, либо показать ошибку.
Проверять авторизацию можно прямо внутри компонентов или на сервере в API-обработчиках, но куда удобнее и надёжнее делать это централизованно — с помощью middleware. Тогда вы точно не забудете "прикрыть" нужный маршрут, и логика проверки будет в одном месте.
Напомним: файл middleware.ts (или middleware.js) лежит в корне проекта или в папке src/. Каждый раз, когда пользователь делает запрос к вашему приложению, этот файл выполняется до того, как страница или API-обработчик начнут работать.
Middleware получает объект запроса (Request) и может:
- Пропустить запрос дальше (ничего не делать)
- Прервать выполнение (вернуть ответ самому)
- Сделать редирект (например, на /login)
- Изменить заголовки, куки и т.д.
2. Как устроена авторизация в Next.js-приложениях?
В Next.js (особенно с использованием next-auth) авторизация чаще всего реализуется через куки: после входа пользователя на сервере создаётся специальная кука с сессионным токеном. Этот токен отправляется с каждым запросом, и по нему можно узнать — кто перед нами: гость или залогиненный пользователь.
Важный момент:
В middleware мы не можем использовать React-хуки или получать данные из Client Components, но можем работать с куками и заголовками запроса.
Пример: простая проверка наличия куки
Давайте рассмотрим базовый вариант: если у пользователя нет куки session-token — отправляем его на страницу входа.
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Получаем куку из запроса
const session = request.cookies.get('session-token')?.value
// Если куки нет — редиректим на /login
if (!session) {
return NextResponse.redirect(new URL('/login', request.url))
}
// Если всё хорошо — пропускаем запрос дальше
return NextResponse.next()
}
Объяснение:
Мы достаём значение куки с помощью request.cookies.get().
Если куки нет, возвращаем редирект на /login.
Если кука есть — пропускаем запрос дальше.
3. Проверка авторизации только для определённых маршрутов
Часто не нужно защищать всё приложение — только часть маршрутов (например, /dashboard, /profile, /admin). Как это сделать?
В Next.js middleware можно ограничить срабатывание через экспорт объекта config:
export const config = {
matcher: ['/dashboard/:path*', '/profile/:path*', '/admin/:path*']
}
Теперь middleware будет работать только для этих маршрутов.
Полный пример:
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const session = request.cookies.get('session-token')?.value
if (!session) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
// Ограничиваем срабатывание только на защищённых маршрутах
export const config = {
matcher: ['/dashboard/:path*', '/profile/:path*', '/admin/:path*']
}
4. Интеграция с next-auth: проверяем сессию
Если вы используете next-auth, то для проверки авторизации в middleware есть специальный способ — через функцию getToken из пакета next-auth/jwt. Она позволяет расшифровать токен прямо внутри middleware.
Установка (если ещё не установлено):
npm install next-auth
Пример:
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { getToken } from 'next-auth/jwt'
export async function middleware(request: NextRequest) {
// Получаем токен пользователя (если есть)
const token = await getToken({ req: request, secret: process.env.NEXTAUTH_SECRET })
// Если токена нет — редиректим на /login
if (!token) {
return NextResponse.redirect(new URL('/login', request.url))
}
// Если токен есть — пропускаем
return NextResponse.next()
}
export const config = {
matcher: ['/dashboard/:path*', '/profile/:path*', '/admin/:path*']
}
Объяснение:
getToken сам найдёт и расшифрует токен из куки.
Если токена нет — пользователь не авторизован.
5. Продвинутый пример: проверка роли пользователя
Допустим, у вас есть разные роли: обычный пользователь и админ. И вы хотите, чтобы на /admin допускались только админы.
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { getToken } from 'next-auth/jwt'
export async function middleware(request: NextRequest) {
const token = await getToken({ req: request, secret: process.env.NEXTAUTH_SECRET })
// Если токена нет — редиректим на /login
if (!token) {
return NextResponse.redirect(new URL('/login', request.url))
}
// Проверяем: если пользователь НЕ админ и идёт на /admin — редиректим на главную
if (
request.nextUrl.pathname.startsWith('/admin') &&
token.role !== 'admin'
) {
return NextResponse.redirect(new URL('/', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ['/dashboard/:path*', '/profile/:path*', '/admin/:path*']
}
Объяснение:
В объекте token можно хранить любые данные пользователя (например, роль).
Если роль не admin, а маршрут — /admin, делаем редирект на главную страницу.
6. Практика: защита страниц профиля и админки
Давайте добавим в наше учебное приложение защиту для маршрутов /profile и /admin.
- В файле middleware.ts реализуем проверку токена.
- Для /profile — достаточно просто быть авторизованным.
- Для /admin — нужна роль admin.
Код:
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { getToken } from 'next-auth/jwt'
export async function middleware(request: NextRequest) {
const token = await getToken({ req: request, secret: process.env.NEXTAUTH_SECRET })
if (!token) {
// Не авторизован — всегда на /login
return NextResponse.redirect(new URL('/login', request.url))
}
// Если идём на /admin, проверяем роль
if (request.nextUrl.pathname.startsWith('/admin') && token.role !== 'admin') {
// Не админ — на главную
return NextResponse.redirect(new URL('/', request.url))
}
// Всё хорошо
return NextResponse.next()
}
export const config = {
matcher: ['/profile/:path*', '/admin/:path*']
}
7. Типичные ошибки при проверке авторизации в Middleware
Ошибка №1: Проверка авторизации на всех маршрутах подряд.
Если не ограничить срабатывание middleware через matcher, вы можете случайно заблокировать даже публичные страницы (например, /about, /login, /api/public). Это приведёт к "вечному редиректу" или невозможности попасть на страницу входа.
Ошибка №2: Не тот секрет для getToken.
Если вы забыли передать правильный secret в getToken, токен не расшифруется, и все пользователи будут считаться неавторизованными. Проверьте, что переменная окружения NEXTAUTH_SECRET совпадает с тем, что использует next-auth.
Ошибка №3: Проверка только наличия куки, а не её валидности.
Бывает, что кука есть, но она просрочена или подделана. Лучше использовать getToken или аналогичные методы, чем просто проверять наличие строки.
Ошибка №4: Использование fetch или сторонних сетевых запросов в middleware.
Middleware должен работать очень быстро и синхронно. Не делайте сетевых запросов к базе или API — это замедлит все переходы по сайту.
Ошибка №5: Редирект на тот же маршрут.
Если в middleware вы делаете редирект на ту же страницу (например, /login редиректит на /login), получите бесконечную петлю. Проверьте, что маршрут назначения отличается от текущего.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ