Перейти к содержанию
Innovialabs

Авторизация в Next.js: Auth.js, Clerk или своё решение

Практика4 мин

Коротко

Clerk быстрее всего в запуске, но хранит данные пользователей у стороннего сервиса и дорожает с ростом; Auth.js бесплатен и хранит пользователей в вашей базе; своё решение даёт полный контроль. В любом случае проверяйте сессию в каждом серверном компоненте и server action, а не только в middleware.

Коротко

  • Clerk — облачный сервис с готовыми экранами входа, управлением пользователями и организациями. Самый быстрый запуск; при росте платите за активных пользователей, а данные хранятся на серверах провайдера.
  • Auth.js (бывший NextAuth) — open-source библиотека. Бесплатна, пользователи хранятся в вашей базе через адаптеры, много OAuth-провайдеров из коробки. Интерфейс делаете сами.
  • Своё решение — собственные сессии, хеширование паролей и OTP. Максимум контроля и никакой зависимости от вендора, но вся ответственность за безопасность на вас.

Сравнение

КритерийClerkAuth.jsСвоё решение
СтоимостьБесплатный тариф, дальше оплата за активных пользователейБесплатно, платите только за хостингВремя на разработку и поддержку
Скорость запускаСамая быстраяСредняяСамая долгая
КонтрольВ пределах возможностей провайдераВысокийПолный
Где данные пользователейИнфраструктура провайдераВаша базаВаша база
ИнтерфейсГотовые компонентыСвойСвой

Место хранения данных важно в Узбекистане. Закон о персональных данных требует хранить персональные данные граждан Узбекистана на серверах, расположенных в стране. Если это относится к вашему проекту, зарубежный облачный сервис может не подойти; надёжнее Auth.js или своё решение с локальной базой. Уточните требования для вашего случая у юриста.

Защита маршрутов: три уровня

Middleware удобен, чтобы перенаправлять гостей, но он не должен быть единственной проверкой. Считайте его первым фильтром, а доступ проверяйте рядом с данными.

  1. Middleware (или proxy в новых версиях Next.js) — перенаправляет неавторизованных с /dashboard на /login. Он должен быть быстрым: проверить наличие cookie сессии, ничего тяжёлого.
  2. Серверные компоненты — проверяют сессию перед загрузкой данных.
  3. Server actions и route handlers — проверяют сессию и права внутри каждого действия.
// app/dashboard/page.tsx
import { auth } from '@/auth'
import { redirect } from 'next/navigation'

export default async function Dashboard() {
  const session = await auth()
  if (!session?.user) redirect('/login')
  const projects = await getProjects(session.user.id)
  // render projects
}

Server actions — это публичные эндпоинты

Server action можно вызвать прямым POST-запросом, даже если кнопка скрыта в интерфейсе. Всегда проверяйте и аутентификацию, и права владения:

'use server'
import { auth } from '@/auth'

export async function deleteProject(id: string) {
  const session = await auth()
  if (!session?.user) throw new Error('Unauthorized')
  const project = await db.project.findUnique({ where: { id } })
  if (project?.ownerId !== session.user.id) throw new Error('Forbidden')
  await db.project.delete({ where: { id } })
}

Хороший паттерн — слой доступа к данным: функции вроде getProjects, которые сами проверяют сессию, чтобы ни одна страница не могла про это забыть.

Вход по номеру телефона и SMS

В Узбекистане многие пользователи предпочитают номер телефона, а не email. Типичный сценарий:

  1. Привести номер к единому формату, например +998XXXXXXXXX.
  2. Сгенерировать короткий случайный код и сохранить только его хеш со сроком действия.
  3. Отправить код через местный SMS-шлюз.
  4. При проверке сравнить хеши, ограничить число попыток и создать сессию.

Обязательно: rate limiting по номеру и по IP (SMS стоят денег и привлекают злоупотребления), короткое время жизни кода и блокировка после нескольких неверных попыток. В Auth.js этот сценарий реализуется через Credentials-провайдер; Clerk поддерживает вход по телефону, но проверьте тарифы и доставку для вашей страны.

Вход через Telegram

Telegram — естественный способ входа для местной аудитории. Два распространённых варианта:

  • Telegram Login Widget на сайте: Telegram возвращает данные пользователя с полем hash. Проверьте его на сервере через HMAC-SHA256, где ключ — SHA-256 от токена вашего бота, и убедитесь, что auth_date свежий.
  • Telegram Mini App: проверьте initData на сервере по алгоритму из документации Telegram, затем создайте собственную сессию.
import crypto from 'node:crypto'

export function verifyTelegramLogin(data: Record<string, string>, botToken: string) {
  const { hash, ...fields } = data
  const checkString = Object.keys(fields).sort().map((k) => `${k}=${fields[k]}`).join('\n')
  const secret = crypto.createHash('sha256').update(botToken).digest()
  const expected = crypto.createHmac('sha256', secret).update(checkString).digest('hex')
  const fresh = Date.now() / 1000 - Number(fields.auth_date) < 86400
  return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(hash ?? ''))
}

Никогда не доверяйте данным Telegram с клиента без этой проверки.

Частые ошибки

  • Полагаться только на middleware и оставлять server actions без защиты.
  • Хранить сессии или токены в localStorage вместо httpOnly cookie.
  • Хранить OTP-коды открытым текстом и без ограничения попыток.
  • Выбирать облачного провайдера, не проверив требования к месту хранения данных.

Частые вопросы

Да, но это нужно планировать. Придётся экспортировать пользователей, сопоставить их ID с вашей базой и решить вопрос с паролями — обычно их нельзя выгрузить в пригодном виде, поэтому пользователям может понадобиться вход по коду или сброс пароля.

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.