Авторизация в Next.js: Auth.js, Clerk или своё решение
Практика4 минРедакция Innovialabs
Коротко
Clerk быстрее всего в запуске, но хранит данные пользователей у стороннего сервиса и дорожает с ростом; Auth.js бесплатен и хранит пользователей в вашей базе; своё решение даёт полный контроль. В любом случае проверяйте сессию в каждом серверном компоненте и server action, а не только в middleware.
Коротко
- Clerk — облачный сервис с готовыми экранами входа, управлением пользователями и организациями. Самый быстрый запуск; при росте платите за активных пользователей, а данные хранятся на серверах провайдера.
- Auth.js (бывший NextAuth) — open-source библиотека. Бесплатна, пользователи хранятся в вашей базе через адаптеры, много OAuth-провайдеров из коробки. Интерфейс делаете сами.
- Своё решение — собственные сессии, хеширование паролей и OTP. Максимум контроля и никакой зависимости от вендора, но вся ответственность за безопасность на вас.
Сравнение
| Критерий | Clerk | Auth.js | Своё решение |
|---|---|---|---|
| Стоимость | Бесплатный тариф, дальше оплата за активных пользователей | Бесплатно, платите только за хостинг | Время на разработку и поддержку |
| Скорость запуска | Самая быстрая | Средняя | Самая долгая |
| Контроль | В пределах возможностей провайдера | Высокий | Полный |
| Где данные пользователей | Инфраструктура провайдера | Ваша база | Ваша база |
| Интерфейс | Готовые компоненты | Свой | Свой |
Место хранения данных важно в Узбекистане. Закон о персональных данных требует хранить персональные данные граждан Узбекистана на серверах, расположенных в стране. Если это относится к вашему проекту, зарубежный облачный сервис может не подойти; надёжнее Auth.js или своё решение с локальной базой. Уточните требования для вашего случая у юриста.
Защита маршрутов: три уровня
Middleware удобен, чтобы перенаправлять гостей, но он не должен быть единственной проверкой. Считайте его первым фильтром, а доступ проверяйте рядом с данными.
- Middleware (или proxy в новых версиях Next.js) — перенаправляет неавторизованных с
/dashboardна/login. Он должен быть быстрым: проверить наличие cookie сессии, ничего тяжёлого. - Серверные компоненты — проверяют сессию перед загрузкой данных.
- 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. Типичный сценарий:
- Привести номер к единому формату, например
+998XXXXXXXXX. - Сгенерировать короткий случайный код и сохранить только его хеш со сроком действия.
- Отправить код через местный SMS-шлюз.
- При проверке сравнить хеши, ограничить число попыток и создать сессию.
Обязательно: 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вместоhttpOnlycookie. - Хранить OTP-коды открытым текстом и без ограничения попыток.
- Выбирать облачного провайдера, не проверив требования к месту хранения данных.