بازگشت به وبلاگ
nextjs ۴ دقیقه مطالعه

راهنمای کامل احراز هویت در Next.js

احراز هویت در Next.js فقط صفحه ورود نیست؛ باید کلاینت، سرور، API، redirect، session، و امنیت کوکی‌ها را با هم طراحی کرد.

تیم ابران
یادداشت‌های محصول و زیرساخت · ۲۵ مرداد ۱۴۰۵
راهنمای کامل احراز هویت در Next.js

راهنمای کامل احراز هویت در Next.js

احراز هویت در یک برنامه Next.js معمولاً ساده شروع می‌شود: یک فرم ورود، یک دکمه خروج، و چند route محافظت‌شده. اما در عمل، همین بخش به یکی از حساس‌ترین مرزهای برنامه تبدیل می‌شود؛ چون باید هم در مرورگر درست کار کند، هم در سمت سرور قابل اعتماد باشد، هم API را محافظت کند، و هم تجربه کاربر را خراب نکند.

در Next.js مدرن، مخصوصاً با App Router، بهتر است از همان ابتدا احراز هویت را به عنوان یک جریان چندلایه ببینیم: session در کوکی امن نگهداری می‌شود، سرور منبع اصلی حقیقت است، middleware فقط کارهای سبک انجام می‌دهد، و کامپوننت‌های کلاینت فقط برای تعامل کاربر استفاده می‌شوند.

مسئله اصلی چیست؟

کاربر ممکن است از چند مسیر وارد برنامه شود: صفحه داشبورد، یک API route، یک لینک دعوت، یا یک صفحه عمومی که بخشی از آن به وضعیت ورود وابسته است. اگر هر بخش برنامه تصمیم خودش را درباره وضعیت کاربر بگیرد، خیلی زود با رفتارهای ناهماهنگ روبه‌رو می‌شوید؛ مثلاً صفحه کاربر را واردشده نشان می‌دهد، اما API پاسخ unauthorized برمی‌گرداند.

راه بهتر این است که یک مدل روشن داشته باشید:

  • session باید روی سرور قابل بررسی باشد.
  • صفحه‌های حساس باید قبل از render شدن وضعیت کاربر را بدانند.
  • API باید بدون اعتماد به state مرورگر، کاربر را اعتبارسنجی کند.
  • redirect باید قابل پیش‌بینی و امن باشد.
  • secretها هرگز نباید وارد bundle کلاینت شوند.

احراز هویت سمت کلاینت

کامپوننت‌های کلاینت برای فرم ورود، نمایش وضعیت کاربر، و تعامل‌هایی مثل خروج مناسب‌اند. اما نباید به تنهایی نقش نگهبان امنیتی را بازی کنند. اگر فقط در مرورگر بررسی کنید که کاربر وارد شده یا نه، کاربر هنوز می‌تواند مستقیماً API را صدا بزند یا صفحه‌ای را درخواست کند که نباید ببیند.

پس کلاینت را برای تجربه کاربری استفاده کنید، نه برای تصمیم امنیتی نهایی. برای مثال، دکمه خروج، پیام خوشامد، و حالت loading در فرم ورود می‌تواند کلاینتی باشد؛ اما تصمیم دسترسی باید روی سرور تأیید شود.

احراز هویت سمت سرور

در App Router معمولاً صفحه‌ها و layoutها می‌توانند Server Component باشند. این یعنی می‌توانید قبل از render شدن صفحه، session را بخوانید و تصمیم بگیرید کاربر باید محتوا را ببیند یا به صفحه ورود منتقل شود.

الگوی ساده این است:

// app/dashboard/page.tsx
import { redirect } from 'next/navigation'
import { getCurrentUser } from '@/lib/auth'

export default async function DashboardPage() {
  const user = await getCurrentUser()

  if (!user) {
    redirect('/login')
  }

  return <Dashboard user={user} />
}

این مدل بهتر از بررسی دیرهنگام در مرورگر است، چون محتوای حساس اصلاً برای کاربر ناشناس تولید نمی‌شود.

محافظت از API

هر route handler یا API که داده خصوصی می‌دهد باید خودش session را بررسی کند. حتی اگر صفحه‌ای که API را صدا می‌زند محافظت‌شده باشد، خود API نباید به آن اعتماد کند.

export async function GET() {
  const user = await getCurrentUser()

  if (!user) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 })
  }

  return Response.json({ user })
}

برای API عمومی هم باید دقیق باشید: اگر پاسخ به وضعیت کاربر وابسته است، caching و headerها را طوری تنظیم کنید که داده یک کاربر برای کاربر دیگر cache نشود.

Middleware را سبک نگه دارید

middleware وسوسه‌انگیز است، چون قبل از route اجرا می‌شود. اما جای query دیتابیس یا تصمیم‌های پیچیده نیست. بهتر است فقط کارهای سبک مثل خواندن کوکی، بررسی وجود token، و redirect اولیه را انجام دهد. بررسی کامل دسترسی، نقش‌ها، و ownership بهتر است در سرور و نزدیک به داده انجام شود.

export function middleware(request: NextRequest) {
  const token = request.cookies.get('session')?.value
  const isProtected = request.nextUrl.pathname.startsWith('/dashboard')

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

  return NextResponse.next()
}

انتخاب ابزار احراز هویت

برای Next.js چند مسیر رایج دارید:

  • Auth.js برای کنترل بیشتر و جریان‌های استاندارد OAuth.
  • Clerk برای راه‌اندازی سریع، UI آماده، و مدیریت کاربر.
  • Supabase Auth برای محصول‌هایی که دیتابیس و auth را کنار هم می‌خواهند.
  • Better Auth برای تیم‌هایی که مدل TypeScript-first و کنترل بیشتر می‌خواهند.
  • پیاده‌سازی اختصاصی فقط وقتی که نیاز امنیتی، سازمانی، یا محصولی خاص دارید.

انتخاب درست به مرحله محصول بستگی دارد. برای MVP، زمان راه‌اندازی مهم است. برای محصول بالغ، audit، کنترل session، ساختار سازمانی، و migration اهمیت بیشتری پیدا می‌کند.

چک‌لیست عملی

قبل از انتشار، این موارد را بررسی کنید:

  1. کوکی session با HttpOnly، Secure، و SameSite مناسب تنظیم شده است.
  2. routeهای خصوصی روی سرور محافظت می‌شوند.
  3. APIها به صورت مستقل session را بررسی می‌کنند.
  4. secretها با NEXT_PUBLIC_ شروع نمی‌شوند.
  5. redirect بعد از ورود در برابر open redirect محافظت شده است.
  6. حالت‌های loading، expired session، و unauthorized برای کاربر واضح‌اند.

جمع‌بندی

احراز هویت خوب در Next.js یعنی یک مرز روشن بین تجربه کاربری و تصمیم امنیتی. فرم‌ها و تعامل‌ها می‌توانند کلاینتی باشند، اما منبع حقیقت باید روی سرور بماند. هر چه زودتر این مرز را در معماری پروژه مشخص کنید، بعداً کمتر مجبور می‌شوید کد auth را در کل برنامه دنبال کنید.

منبع

#nextjs#authentication#security#paas