البرمجة متوسط 14 دقيقة

كيفية إضافة المصادقة لتطبيق Next.js باستخدام Auth.js

Auth.js (المعروف سابقًا بـ NextAuth.js) الإصدار 5 هو الطريقة المعيارية لإضافة المصادقة إلى مشاريع Next.js بـ App Router. يتولى عنك إدارة OAuth، وإدارة الجلسات، وحماية CSRF، وتجديد الـ tokens — كل الكود المتكرر الذي كنت ستكتبه بنفسك.

يشرح هذا الدليل الإعداد الكامل باستخدام Google OAuth (تسجيل الدخول الاجتماعي) أو Credentials provider (بريد إلكتروني وكلمة مرور). ستحمي Server Components، وتضيف وصولًا للجلسة من جانب العميل عبر useSession()، وتغلق مجموعات مسارات كاملة بـ middleware — كل ذلك دون خدمة مصادقة خارجية.

الخطوات

  1. 1

    ثبّت Auth.js الإصدار 5

    Auth.js v5 منشور باسم next-auth@beta. ثبّته وأنشئ سرًا — هذا السر يوقّع token الجلسة ويجب أن يبقى خارج نظام التحكم بالإصدارات.

    bash
    npm install next-auth@beta
    
    # أنشئ AUTH_SECRET عشوائيًا واكتبه في .env.local
    npx auth secret
    # يضيف تلقائيًا: AUTH_SECRET="<random>" إلى .env.local
  2. 2

    أنشئ ملف إعداد auth.ts

    أنشئ auth.ts في جذر المشروع (بجانب package.json). هذا الملف هو المصدر الوحيد للحقيقة — يصدّر auth وsignIn وsignOut وhandlers. كل شيء آخر يستورد منه مباشرةً لا من next-auth.

    typescript
    // auth.ts
    import NextAuth from "next-auth"
    import Google from "next-auth/providers/google"
    
    export const { handlers, signIn, signOut, auth } = NextAuth({
      providers: [
        Google({
          clientId: process.env.AUTH_GOOGLE_ID,
          clientSecret: process.env.AUTH_GOOGLE_SECRET,
        }),
      ],
    })
  3. 3

    اربط معالج مسار API

    يحتاج Auth.js إلى مسار catch-all على /api/auth/[...nextauth] لمعالجة OAuth callbacks وتسجيل الدخول والخروج ونقاط نهاية الجلسة. أنشئ الملف وأعد تصدير الـ handlers من ملف الإعداد.

    typescript
    // app/api/auth/[...nextauth]/route.ts
    import { handlers } from "@/auth"
    export const { GET, POST } = handlers
  4. 4

    أضف متغيرات البيئة المطلوبة

    أضف هذه المتغيرات إلى .env.local. للحصول على بيانات Google، اذهب إلى Google Cloud Console تحت APIs & Services → Credentials → OAuth 2.0 Client IDs. اضبط redirect URI المصرّح به على http://localhost:3000/api/auth/callback/google للتطوير (وأضف رابط الإنتاج بشكل منفصل).

    bash
    # .env.local
    AUTH_SECRET="generated-by-npx-auth-secret"
    AUTH_GOOGLE_ID="your-google-client-id.apps.googleusercontent.com"
    AUTH_GOOGLE_SECRET="your-google-client-secret"
    
    # NEXTAUTH_URL غير مطلوب في v5 — Auth.js يستنتجه من الطلب
  5. 5

    احمِ Server Components بـ auth()

    في أي Server Component أو Server Action، استدعِ auth() للحصول على الجلسة. تُعيد null عندما لا يكون المستخدم مسجلًا دخوله. أعِد التوجيه أو اعرض بوابة دخول بناءً على ذلك.

    typescript
    // app/dashboard/page.tsx
    import { auth } from "@/auth"
    import { redirect } from "next/navigation"
    
    export default async function DashboardPage() {
      const session = await auth()
    
      if (!session) {
        redirect("/api/auth/signin")
      }
    
      return (
        <main>
          <h1>Welcome, {session.user?.name}</h1>
        </main>
      )
    }
  6. 6

    أضف SessionProvider للمكونات العميلة

    المكونات العميلة لا تستطيع استدعاء auth() — تستخدم بدلًا من ذلك hook باسم useSession(). لفّ layout الجذر بـ SessionProvider حتى يعمل الـ hook في أي مكان في شجرة العميل.

    typescript
    // app/layout.tsx
    import { SessionProvider } from "next-auth/react"
    import { auth } from "@/auth"
    
    export default async function RootLayout({ children }: { children: React.ReactNode }) {
      const session = await auth()
      return (
        <html>
          <body>
            <SessionProvider session={session}>
              {children}
            </SessionProvider>
          </body>
        </html>
      )
    }
    
    // في أي مكون عميل:
    "use client"
    import { useSession } from "next-auth/react"
    
    export function UserAvatar() {
      const { data: session, status } = useSession()
      if (status === "loading") return <span>Loading…</span>
      if (!session) return <a href="/api/auth/signin">Sign in</a>
      return <img src={session.user?.image ?? ""} alt={session.user?.name ?? ""} />
    }
  7. 7

    أضف أزرار تسجيل الدخول والخروج

    استورد signIn وsignOut من ملف auth.ts واستدعهما من Server Actions داخل <form>. هذا يمنع كشف بيانات الاعتماد للعميل ويعمل حتى بدون JavaScript.

    typescript
    // components/auth-buttons.tsx
    import { signIn, signOut } from "@/auth"
    
    export function SignInButton() {
      return (
        <form action={async () => { "use server"; await signIn("google") }}>
          <button type="submit">Sign in with Google</button>
        </form>
      )
    }
    
    export function SignOutButton() {
      return (
        <form action={async () => { "use server"; await signOut() }}>
          <button type="submit">Sign out</button>
        </form>
      )
    }
  8. 8

    احمِ المسارات بـ middleware

    أنشئ ملف middleware.ts في جذر المشروع. صدّر دالة auth كـ middleware — ستعترض كل طلب يطابق إعداد matcher وتعيد توجيه المستخدمين غير الموثّقين إلى صفحة تسجيل الدخول قبل أن يُنفَّذ معالج المسار.

    typescript
    // middleware.ts
    export { auth as middleware } from "@/auth"
    
    export const config = {
      matcher: [
        // احمِ كل شيء تحت /dashboard و/account
        "/dashboard/:path*",
        "/account/:path*",
        // تخطَّ الملفات الثابتة ومسارات API التي تتولى المصادقة بنفسها
        "/((?!api|_next/static|_next/image|favicon.ico).*)",
      ],
    }
  9. 9

    استخدم Credentials provider لبريد/كلمة مرور

    إذا احتجت تسجيل الدخول بالبريد الإلكتروني وكلمة المرور بدلًا من OAuth أو إلى جانبه، أضف Credentials provider إلى auth.ts. قارن كلمة المرور بقيمة مُجزّأة من قاعدة البيانات — لا تخزن كلمات المرور كنص صريح أبدًا.

    typescript
    // auth.ts (إضافة Credentials provider)
    import Credentials from "next-auth/providers/credentials"
    import bcrypt from "bcryptjs"
    import { db } from "@/lib/db" // عميل قاعدة البيانات
    
    export const { handlers, signIn, signOut, auth } = NextAuth({
      providers: [
        Credentials({
          credentials: {
            email: { label: "Email", type: "email" },
            password: { label: "Password", type: "password" },
          },
          async authorize(credentials) {
            if (!credentials?.email || !credentials?.password) return null
    
            const user = await db.user.findUnique({
              where: { email: credentials.email as string },
            })
            if (!user) return null
    
            const passwordMatch = await bcrypt.compare(
              credentials.password as string,
              user.passwordHash
            )
            return passwordMatch ? user : null
          },
        }),
      ],
    })

نصائح ومحاذير

  • استخدم JWT sessions (الافتراضي) للـ APIs عديمة الحالة. انتقل إلى جلسات قاعدة البيانات فقط عندما تحتاج إلغاء الجلسات من جانب الخادم (مثل حظر الحسابات).
  • وسّع كائن الجلسة بحقول مخصصة (معرف المستخدم، الدور) باستخدام `callbacks.session` و`callbacks.jwt` في `auth.ts` — هذا يتجنب رحلات إضافية إلى قاعدة البيانات مع كل طلب.
  • في Google Cloud Console، أضف دائمًا عناوين callback الخاصة بالتطوير (`localhost`) والإنتاج إلى قائمة redirect URIs المصرّح بها قبل الإطلاق.
  • لا تستدعِ `signIn()` أو `signOut()` من Client Component مباشرةً — لفّهما في Server Action أو استخدم رابط `/api/auth/signin` المدمج.

خاتمة

يختزل Auth.js v5 كود المصادقة المتكرر في ملف إعداد ومسار API واحد وتصدير middleware. بمجرد إعداده، إضافة مزودين جدد تصبح سطرًا واحدًا، وحماية مسارات جديدة تعني إضافة مسار إلى matcher في middleware. الجمع بين auth() من جانب الخادم وuseSession() من جانب العميل يغطي كل أنماط التصيير التي يتيحها App Router.

#Next.js #Auth #NextAuth
العودة إلى جميع الأدلة

هل تحتاج مساعدة في مشروعك؟

احجز استشارة مجانية لمدة 30 دقيقة لمناقشة تحدياتك التقنية واستكشاف الحلول معًا.