Next.js
المصادقة في Next.js — دليل شامل 2026
📅 2026-11-18⏱ 9 دقائق قراءة
في المقال السابق، تعلمت Styling. الآن سنتعلم **المصادقة (Authentication)** — وهي **الميزة الأكثر تعقيداً** في أي تطبيق حقيقي.
في هذا الدليل العملي، سنأخذك خطوة بخطوة لفهم المصادقة في Next.js، مع تمارين وحلول.
## ما هي المصادقة؟
**المصادقة (Authentication)** = التحقق من **هوية المستخدم**.
**التفويض (Authorization)** = التحقق من **صلاحياته**.
**مثال:**
- **Authentication:** تسجيل الدخول باسم المستخدم وكلمة المرور.
- **Authorization:** هل هذا المستخدم مدير؟
## أنواع المصادقة
<table>
<thead>
<tr>
<th>النوع</th>
<th>الوصف</th>
<th>الاستخدام</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Sessions</strong></td>
<td>جلسة على السيرفر</td>
<td>تطبيقات تقليدية</td>
</tr>
<tr>
<td><strong>JWT</strong></td>
<td>توكن مشفر</td>
<td>APIs و SPAs</td>
</tr>
<tr>
<td><strong>OAuth</strong></td>
<td>مزود خارجي</td>
<td>Google, GitHub</td>
</tr>
<tr>
<td><strong>Magic Links</strong></td>
<td>رابط بالبريد</td>
<td>تطبيقات حديثة</td>
</tr>
<tr>
<td><strong>2FA</strong></td>
<td>عاملان</td>
<td>أمان عالٍ</td>
</tr>
</tbody>
</table>
## NextAuth.js (Auth.js)
**NextAuth.js** (الآن Auth.js) هو الحل **الرسمي** للمصادقة في Next.js.
### المميزات:
- ✅ يدعم **عدة مزودين** (Google, GitHub, Credentials).
- ✅ **Sessions** و **JWT**.
- ✅ **Middleware** مدمج.
- ✅ **Hooks** جاهزة.
- ✅ **TypeScript**.
- ✅ **قواعد بيانات** متعددة.
## تثبيت NextAuth
```bash
npm install next-auth@beta
```
**⚠️ ملاحظة:** `@beta` لأننا نستخدم Next.js 15 (App Router).
## الإعداد الأساسي
### 1. `.env.local`
```bash
NEXTAUTH_SECRET=your-super-secret-key-here
NEXTAUTH_URL=http://localhost:3000
# لمزود Google
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
# لمزود GitHub
GITHUB_ID=your-github-id
GITHUB_SECRET=your-github-secret
```
**لإنشاء `NEXTAUTH_SECRET`:**
```bash
openssl rand -base64 32
```
### 2. `app/api/auth/[...nextauth]/route.ts`
```tsx
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import GitHubProvider from "next-auth/providers/github";
import CredentialsProvider from "next-auth/providers/credentials";
const handler = NextAuth({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
GitHubProvider({
clientId: process.env.GITHUB_ID!,
clientSecret: process.env.GITHUB_SECRET!,
}),
CredentialsProvider({
name: "Credentials",
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
async authorize(credentials) {
// هنا نتحقق من البيانات
if (credentials?.email === "[email protected]" && credentials?.password === "password") {
return { id: "1", name: "أحمد", email: "[email protected]" };
}
return null;
},
}),
],
pages: {
signIn: "/login",
error: "/login",
},
session: {
strategy: "jwt",
},
callbacks: {
async jwt({ token, user }) {
if (user) {
token.id = user.id;
}
return token;
},
async session({ session, token }) {
if (session.user) {
(session.user as any).id = token.id;
}
return session;
},
},
});
export { handler as GET, handler as POST };
```
### 3. SessionProvider
**`app/providers.tsx`:**
```tsx
"use client";
import { SessionProvider } from "next-auth/react";
export default function Providers({ children }: { children: React.ReactNode }) {
return <SessionProvider>{children}</SessionProvider>;
}
```
### 4. `app/layout.tsx`
```tsx
import Providers from "./providers";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ar" dir="rtl">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
```
## استخدام المصادقة
### 1. في Server Component
```tsx
import { getServerSession } from "next-auth";
import { authOptions } from "@/app/api/auth/[...nextauth]/route";
export default async function ProfilePage() {
const session = await getServerSession(authOptions);
if (!session) {
return <p>يجب تسجيل الدخول</p>;
}
return (
<div>
<h1>مرحباً {session.user?.name}</h1>
<p>{session.user?.email}</p>
</div>
);
}
```
### 2. في Client Component
```tsx
"use client";
import { useSession, signIn, signOut } from "next-auth/react";
export default function AuthButton() {
const { data: session, status } = useSession();
if (status === "loading") {
return <p>جاري التحميل...</p>;
}
if (session) {
return (
<div className="flex items-center gap-4">
<span>مرحباً {session.user?.name}</span>
<button
onClick={() => signOut()}
className="bg-red-600 text-white px-4 py-2 rounded-lg"
>
خروج
</button>
</div>
);
}
return (
<button
onClick={() => signIn()}
className="bg-blue-600 text-white px-4 py-2 rounded-lg"
>
تسجيل الدخول
</button>
);
}
```
## صفحات تسجيل الدخول
### `app/login/page.tsx`
```tsx
"use client";
import { signIn } from "next-auth/react";
import { useState } from "react";
export default function LoginPage() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError("");
const res = await signIn("credentials", {
email,
password,
redirect: false,
});
if (res?.error) {
setError("بيانات الدخول غير صحيحة");
} else {
window.location.href = "/dashboard";
}
};
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<form
onSubmit={handleSubmit}
className="bg-white p-8 rounded-2xl shadow-lg w-full max-w-md"
>
<h1 className="text-2xl font-bold text-center mb-6">تسجيل الدخول</h1>
{error && (
<div className="bg-red-100 text-red-700 p-3 rounded-lg mb-4">
{error}
</div>
)}
<input
type="email"
placeholder="البريد"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="w-full px-4 py-3 border rounded-lg mb-4"
/>
<input
type="password"
placeholder="كلمة المرور"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
className="w-full px-4 py-3 border rounded-lg mb-4"
/>
<button
type="submit"
className="w-full bg-blue-600 text-white py-3 rounded-lg font-bold hover:bg-blue-700 transition mb-4"
>
دخول
</button>
<div className="border-t pt-4">
<p className="text-center text-sm text-gray-500 mb-4">
أو سجل باستخدام
</p>
<div className="flex gap-2">
<button
type="button"
onClick={() => signIn("google", { callbackUrl: "/dashboard" })}
className="flex-1 bg-white border-2 border-gray-200 py-3 rounded-lg hover:border-gray-300 transition"
>
🔵 Google
</button>
<button
type="button"
onClick={() => signIn("github", { callbackUrl: "/dashboard" })}
className="flex-1 bg-gray-900 text-white py-3 rounded-lg hover:bg-gray-800 transition"
>
⚫ GitHub
</button>
</div>
</div>
</form>
</div>
);
}
```
## حماية المسارات (Middleware)
**`middleware.ts`** (في جذر المشروع):
```tsx
import { withAuth } from "next-auth/middleware";
export default withAuth({
pages: {
signIn: "/login",
},
});
export const config = {
matcher: ["/dashboard/:path*", "/profile/:path*", "/admin/:path*"],
};
```
**النتيجة:** أي وصول لـ `/dashboard/*` يتطلب تسجيل دخول.
## حماية المسارات (بدون Middleware)
**`app/dashboard/page.tsx`:**
```tsx
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
import { authOptions } from "@/app/api/auth/[...nextauth]/route";
export default async function DashboardPage() {
const session = await getServerSession(authOptions);
if (!session) {
redirect("/login");
}
return (
<div>
<h1>لوحة التحكم</h1>
<p>مرحباً {session.user?.name}</p>
</div>
);
}
```
## Types (TypeScript)
**`types/next-auth.d.ts`:**
```typescript
import NextAuth from "next-auth";
declare module "next-auth" {
interface Session {
user: {
id: string;
name: string;
email: string;
role?: string;
};
}
interface User {
id: string;
role?: string;
}
}
declare module "next-auth/jwt" {
interface JWT {
id: string;
role?: string;
}
}
```
## نموذج كامل مع قاعدة بيانات
### 1. `lib/auth.ts`
```typescript
import CredentialsProvider from "next-auth/providers/credentials";
import bcrypt from "bcryptjs";
import { prisma } from "./prisma";
export const authOptions = {
providers: [
CredentialsProvider({
name: "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 prisma.user.findUnique({
where: { email: credentials.email },
});
if (!user) return null;
const isValid = await bcrypt.compare(
credentials.password,
user.password
);
if (!isValid) return null;
return {
id: user.id,
name: user.name,
email: user.email,
role: user.role,
};
},
}),
],
session: { strategy: "jwt" as const },
callbacks: {
async jwt({ token, user }: any) {
if (user) {
token.id = user.id;
token.role = user.role;
}
return token;
},
async session({ session, token }: any) {
if (session.user) {
session.user.id = token.id;
session.user.role = token.role;
}
return session;
},
},
pages: {
signIn: "/login",
},
};
```
## استخدام الدوال المساعدة
**`lib/auth-helpers.ts`:**
```typescript
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
import { authOptions } from "./auth";
export async function requireAuth() {
const session = await getServerSession(authOptions);
if (!session) redirect("/login");
return session;
}
export async function requireAdmin() {
const session = await getServerSession(authOptions);
if (!session) redirect("/login");
if (session.user.role !== "admin") redirect("/");
return session;
}
```
**الاستخدام:**
```tsx
export default async function DashboardPage() {
const session = await requireAuth();
return <h1>مرحباً {session.user.name}</h1>;
}
```
## تمارين عملية
### تمرين 1: إعداد NextAuth
أعدّ NextAuth مع Google.
**الحل:**
```tsx
const handler = NextAuth({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
],
});
```
### تمرين 2: زر تسجيل الدخول
أنشئ زر تسجيل دخول.
**الحل:**
```tsx
"use client";
import { signIn } from "next-auth/react";
export default function LoginButton() {
return <button onClick={() => signIn()}>تسجيل الدخول</button>;
}
```
### تمرين 3: عرض Session
اعرض بيانات المستخدم.
**الحل:**
```tsx
"use client";
import { useSession } from "next-auth/react";
export default function Profile() {
const { data: session } = useSession();
if (!session) return <p>غير مسجل</p>;
return <p>مرحباً {session.user?.name}</p>;
}
```
### تمرين 4: حماية صفحة
احمِ `/dashboard`.
**الحل:**
```tsx
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
import { authOptions } from "@/lib/auth";
export default async function Page() {
const session = await getServerSession(authOptions);
if (!session) redirect("/login");
return <h1>محمي</h1>;
}
```
### تمرين 5: Middleware
احمِ `/admin/*`.
**الحل:**
```tsx
import { withAuth } from "next-auth/middleware";
export default withAuth();
export const config = { matcher: ["/admin/:path*"] };
```
### تمرين 6: تسجيل الخروج
أضف زر خروج.
**الحل:**
```tsx
"use client";
import { signOut } from "next-auth/react";
export default function LogoutButton() {
return <button onClick={() => signOut()}>خروج</button>;
}
```
### تمرين 7: صفحة تسجيل دخول مخصصة
أنشئ `/login`.
**الحل:** (راجع المثال أعلاه)
### تمرين 8: تطبيق كامل
ابنِ تطبيق مع مصادقة كاملة.
**الحل:** (راجع الأمثلة أعلاه)
## حل المشاكل الشائعة
### 🔴 المشكلة 1: `NEXTAUTH_SECRET is missing`
**الحل:** أضف `NEXTAUTH_SECRET` في `.env.local`.
### 🔴 المشكلة 2: `signIn` لا يعمل
**السبب:** لم تُضف `SessionProvider`.
**الحل:** أضفها في `layout.tsx`.
### 🔴 المشكلة 3: Session فارغ
**الحل:** تأكد من `getServerSession(authOptions)`.
### 🔴 المشكلة 4: Middleware لا يعمل
**السبب:** `matcher` ناقص.
**الحل:**
```tsx
export const config = {
matcher: ["/dashboard/:path*"],
};
```
### 🔴 المشكلة 5: `useSession` لا يعمل
**السبب:** لم تستخدم `SessionProvider`.
**الحل:** أضفه في `providers.tsx`.
## جدول دوال NextAuth
<table>
<thead>
<tr>
<th>الدالة</th>
<th>الوظيفة</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>signIn()</code></td>
<td>تسجيل الدخول</td>
</tr>
<tr>
<td><code>signOut()</code></td>
<td>تسجيل الخروج</td>
</tr>
<tr>
<td><code>useSession()</code></td>
<td>الحصول على Session</td>
</tr>
<tr>
<td><code>getServerSession()</code></td>
<td>Session على السيرفر</td>
</tr>
<tr>
<td><code>withAuth()</code></td>
<td>Middleware</td>
</tr>
</tbody>
</table>
## قائمة تحقق نهائية
<table>
<thead>
<tr>
<th>المهمة</th>
<th>الحالة</th>
</tr>
</thead>
<tbody>
<tr>
<td>تثبيت NextAuth</td>
<td>⬜</td>
</tr>
<tr>
<td>إعداد المزودين</td>
<td>⬜</td>
</tr>
<tr>
<td>صفحة تسجيل دخول</td>
<td>⬜</td>
</tr>
<tr>
<td>حماية المسارات</td>
<td>⬜</td>
</tr>
<tr>
<td>Middleware</td>
<td>⬜</td>
</tr>
<tr>
<td>TypeScript Types</td>
<td>⬜</td>
</tr>
<tr>
<td>حل التمارين الثمانية</td>
<td>⬜</td>
</tr>
</tbody>
</table>
## ماذا بعد هذا المقال؟
الآن بعد أن أتقنت المصادقة، أنت جاهز للمقال التالي:
1. **Deployment** — النشر.
2. **مشروع كامل** — مدونة متكاملة.
## الخلاصة
في هذا المقال، تعلمت:
- ✅ ما هي المصادقة.
- ✅ NextAuth.js.
- ✅ Credentials و OAuth.
- ✅ Sessions و JWT.
- ✅ حماية المسارات.
- ✅ Middleware.
- ✅ TypeScript Types.
- ✅ حل 8 تمارين عملية.
**تذكر:** المصادقة هي **أعقد ميزة** — لكن NextAuth يجعلها سهلة.