Урок 10

Middleware и авторизация

Middleware выполняется до рендера страницы. Полезно для авторизации, редиректов, A/B-тестов.

Создание

middleware.ts в корне проекта (рядом с app/):

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
    return NextResponse.next();
}

Matcher

Middleware применяется ко всем роутам. Ограничь:

export const config = {
    matcher: ['/dashboard/:path*', '/admin/:path*'],
};

Или исключи:

export const config = {
    matcher: ['/((?!api|_next/static|favicon.ico).*)'],
};

Редирект

export function middleware(request: NextRequest) {
    if (!request.cookies.has('token')) {
        return NextResponse.redirect(new URL('/login', request.url));
    }

    return NextResponse.next();
}

Rewrite

Меняет URL без редиректа:

return NextResponse.rewrite(new URL('/dashboard', request.url));

Заголовки

const response = NextResponse.next();
response.headers.set('X-Custom', 'value');
return response;

Cookies

const token = request.cookies.get('token')?.value;

const response = NextResponse.next();
response.cookies.set('visited', 'true', { httpOnly: true });
return response;

Проверка авторизации

import { verifyToken } from '@/lib/auth';

export async function middleware(request: NextRequest) {
    const token = request.cookies.get('token')?.value;

    if (!token) {
        return NextResponse.redirect(new URL('/login', request.url));
    }

    const user = await verifyToken(token);
    if (!user) {
        return NextResponse.redirect(new URL('/login', request.url));
    }

    const response = NextResponse.next();
    response.headers.set('X-User-Id', String(user.id));
    return response;
}

export const config = {
    matcher: ['/dashboard/:path*'],
};

Роли

export function middleware(request: NextRequest) {
    const role = request.cookies.get('role')?.value;

    if (request.nextUrl.pathname.startsWith('/admin') && role !== 'admin') {
        return NextResponse.redirect(new URL('/forbidden', request.url));
    }

    return NextResponse.next();
}

Аутентификация в Server Actions

Middleware защищает страницы, но не API. Проверяй внутри:

'use server';

import { cookies } from 'next/headers';

export async function deletePost(id: number) {
    const token = (await cookies()).get('token')?.value;
    const user = await verifyToken(token);

    if (!user) throw new Error('Unauthorized');

    // проверка прав
}

Сессии

Next.js не имеет встроенных сессий. Варианты:

  • Cookies — JWT или session ID
  • Сторонние библиотеки — Auth.js, Lucia
  • Свой слой — Prisma + cookies

Auth.js

npm install next-auth@beta

auth.ts:

import NextAuth from 'next-auth';
import GitHub from 'next-auth/providers/github';

export const { handlers, auth, signIn, signOut } = NextAuth({
    providers: [GitHub],
});

app/api/auth/[...nextauth]/route.ts:

import { handlers } from '@/auth';

export const { GET, POST } = handlers;

Использование:

import { auth } from '@/auth';

const session = await auth();
if (!session) redirect('/login');

Защита роутов

// app/dashboard/layout.tsx
import { auth } from '@/auth';
import { redirect } from 'next/navigation';

export default async function Layout({ children }) {
    const session = await auth();
    if (!session) redirect('/login');

    return <>{children}</>;
}

Итоги

  • middleware.ts в корне
  • matcher для ограничения
  • redirect, rewrite, next
  • Cookies и заголовки
  • Middleware не защищает API — проверяй внутри
  • Auth.js для полноценной аутентификации