المصادقة
لتنفيذ المصادقة في Next.js، يجب أن تتعرف على ثلاثة مفاهيم أساسية:
- المصادقة تتحقق مما إذا كان المستخدم هو من يدعي أنه هو. تتطلب من المستخدم إثبات هويته بشيء يمتلكه، مثل اسم المستخدم وكلمة المرور.
- إدارة الجلسة تتابع حالة المستخدم (مثل تسجيل الدخول) عبر طلبات متعددة.
- التفويض يحدد أجزاء التطبيق المسموح للمستخدم بالوصول إليها.
توضح هذه الصفحة كيفية استخدام ميزات Next.js لتنفيذ أنماط شائعة للمصادقة والتفويض وإدارة الجلسات، بحيث يمكنك اختيار أفضل الحلول بناءً على احتياجات تطبيقك.
المصادقة
المصادقة تتحقق من هوية المستخدم. تحدث عندما يقوم المستخدم بتسجيل الدخول، إما باستخدام اسم المستخدم وكلمة المرور أو من خلال خدمة مثل جوجل. الهدف هو التأكد من أن المستخدمين هم بالفعل من يدعون أنهم، مما يحمي بيانات المستخدم والتطبيق من الوصول غير المصرح به أو الأنشطة الاحتيالية.
استراتيجيات المصادقة
تستخدم تطبيقات الويب الحديثة عدة استراتيجيات للمصادقة:
- OAuth/OpenID Connect (OIDC): تتيح الوصول من طرف ثالث دون مشاركة بيانات اعتماد المستخدم. مثالية لتسجيلات الدخول عبر وسائل التواصل الاجتماعي وحلول Single Sign-On (SSO). تضيف طبقة هوية مع OpenID Connect.
- تسجيل الدخول ببيانات الاعتماد (البريد الإلكتروني + كلمة المرور): خيار قياسي لتطبيقات الويب، حيث يقوم المستخدمون بتسجيل الدخول باستخدام البريد الإلكتروني وكلمة المرور. مألوف وسهل التنفيذ، ولكنه يتطلب إجراءات أمنية قوية ضد التهديدات مثل التصيد الاحتيالي.
- المصادقة بدون كلمة مرور/قائمة على الرموز: تستخدم روابط سحرية عبر البريد الإلكتروني أو رموز لمرة واحدة عبر الرسائل القصيرة للوصول الآمن بدون كلمة مرور. مشهور لراحته وأمانه المعزز، يساعد هذا الأسلوب في تقليل إرهاق كلمة المرور. قيده هو الاعتماد على توفر البريد الإلكتروني أو الهاتف للمستخدم.
- مفاتيح الوصول/WebAuthn: تستخدم بيانات اعتماد تشفيرية فريدة لكل موقع، مما يوفر أمانًا عاليًا ضد التصيد الاحتيالي. آمن ولكنه جديد، قد يكون صعب التنفيذ.
يجب أن يتم اختيار استراتيجية المصادقة بما يتوافق مع متطلبات تطبيقك المحددة، واعتبارات واجهة المستخدم، والأهداف الأمنية.
تنفيذ المصادقة
في هذا القسم، سنستكشف عملية إضافة مصادقة أساسية بالبريد الإلكتروني وكلمة المرور إلى تطبيق ويب. بينما توفر هذه الطريقة مستوى أساسيًا من الأمان، يجدر النظر في خيارات أكثر تقدمًا مثل OAuth أو تسجيلات الدخول بدون كلمة مرور لمزيد من الحماية ضد التهديدات الأمنية الشائعة. تدفق المصادقة الذي سنناقشه هو كما يلي:
- يقوم المستخدم بإرسال بيانات اعتماده عبر نموذج تسجيل الدخول.
- يرسل النموذج طلبًا يتم معالجته بواسطة مسار API.
- عند التحقق الناجح، تكتمل العملية، مما يشير إلى نجاح مصادقة المستخدم.
- إذا فشل التحقق، يتم عرض رسالة خطأ.
ضع في اعتبارك نموذج تسجيل دخول حيث يمكن للمستخدمين إدخال بيانات اعتمادهم:
import { FormEvent } from 'react'
import { useRouter } from 'next/router'
export default function LoginPage() {
const router = useRouter()
async function handleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
const formData = new FormData(event.currentTarget)
const email = formData.get('email')
const password = formData.get('password')
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
})
if (response.ok) {
router.push('/profile')
} else {
// Handle errors
}
}
return (
<form onSubmit={handleSubmit}>
<input type="email" name="email" placeholder="Email" required />
<input type="password" name="password" placeholder="Password" required />
<button type="submit">Login</button>
</form>
)
}
import { FormEvent } from 'react'
import { useRouter } from 'next/router'
export default function LoginPage() {
const router = useRouter()
async function handleSubmit(event) {
event.preventDefault()
const formData = new FormData(event.currentTarget)
const email = formData.get('email')
const password = formData.get('password')
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
})
if (response.ok) {
router.push('/profile')
} else {
// Handle errors
}
}
return (
<form onSubmit={handleSubmit}>
<input type="email" name="email" placeholder="Email" required />
<input type="password" name="password" placeholder="Password" required />
<button type="submit">Login</button>
</form>
)
}
يحتوي النموذج أعلاه على حقلين إدخال لجمع بريد المستخدم الإلكتروني وكلمة المرور. عند الإرسال، يقوم بتشغيل وظيفة ترسل طلب POST إلى مسار API (/api/auth/login
).
يمكنك بعد ذلك استدعاء واجهة برمجة التطبيقات (API) لموفر المصادقة في مسار API لمعالجة المصادقة:
import { NextApiRequest, NextApiResponse } from 'next'
import { signIn } from '@/auth'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const { email, password } = req.body
await signIn('credentials', { email, password })
res.status(200).json({ success: true })
} catch (error) {
if (error.type === 'CredentialsSignin') {
res.status(401).json({ error: 'Invalid credentials.' })
} else {
res.status(500).json({ error: 'Something went wrong.' })
}
}
}
import { signIn } from '@/auth'
export default async function handler(req, res) {
try {
const { email, password } = req.body
await signIn('credentials', { email, password })
res.status(200).json({ success: true })
} catch (error) {
if (error.type === 'CredentialsSignin') {
res.status(401).json({ error: 'Invalid credentials.' })
} else {
res.status(500).json({ error: 'Something went wrong.' })
}
}
}
في هذا الكود، تتحقق طريقة signIn
من بيانات الاعتماد مقابل بيانات المستخدم المخزنة.
بعد معالجة موفر المصادقة لبيانات الاعتماد، هناك نتيجتان محتملتان:
- المصادقة الناجحة: تعني هذه النتيجة أن تسجيل الدخول كان ناجحًا. يمكن بعد ذلك بدء إجراءات إضافية، مثل الوصول إلى المسارات المحمية وجلب معلومات المستخدم.
- المصادقة الفاشلة: في الحالات التي تكون فيها بيانات الاعتماد غير صحيحة أو يتم مواجهة خطأ، تقوم الوظيفة بإرجاع رسالة خطأ مقابلة للإشارة إلى فشل المصادقة.
لإعداد مصادقة مبسطة في مشاريع Next.js، خاصة عند تقديم طرق متعددة لتسجيل الدخول، فكر في استخدام حل مصادقة شامل.
التفويض
بمجرد مصادقة المستخدم، ستحتاج إلى التأكد من أن المستخدم مسموح له بزيارة مسارات معينة، وأداء عمليات مثل تعديل البيانات باستخدام إجراءات الخادم واستدعاء معالجات المسار.
حماية المسارات باستخدام Middleware
الوسيط (Middleware) في Next.js يساعدك في التحكم في من يمكنه الوصول إلى أجزاء مختلفة من موقعك. هذا مهم للحفاظ على مناطق مثل لوحة تحكم المستخدم محمية بينما تكون صفحات أخرى مثل صفحات التسويق عامة. يوصى بتطبيق Middleware على جميع المسارات وتحديد استثناءات للوصول العام.
إليك كيفية تنفيذ Middleware للمصادقة في Next.js:
إعداد Middleware:
- أنشئ ملف
middleware.ts
أو.js
في الدليل الجذر لمشروعك. - أضف منطقًا للتحقق من صلاحيات وصول المستخدم، مثل التحقق من وجود رمز مصادقة.
تحديد المسارات المحمية:
- ليس كل المسارات تتطلب تفويضًا. استخدم خيار
matcher
في Middleware لتحديد أي مسارات لا تتطلب فحص التفويض.
منطق Middleware:
- اكتب منطقًا للتحقق مما إذا كان المستخدم مصادقًا عليه. تحقق من أدوار المستخدمين أو الصلاحيات لتخويل المسار.
التعامل مع الوصول غير المصرح به:
- أعد توجيه المستخدمين غير المصرح لهم إلى صفحة تسجيل دخول أو صفحة خطأ حسب الاقتضاء.
مثال لملف Middleware:
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const currentUser = request.cookies.get('currentUser')?.value
if (currentUser && !request.nextUrl.pathname.startsWith('/dashboard')) {
return Response.redirect(new URL('/dashboard', request.url))
}
if (!currentUser && !request.nextUrl.pathname.startsWith('/login')) {
return Response.redirect(new URL('/login', request.url))
}
}
export const config = {
matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
}
export function middleware(request) {
const currentUser = request.cookies.get('currentUser')?.value
if (currentUser && !request.nextUrl.pathname.startsWith('/dashboard')) {
return Response.redirect(new URL('/dashboard', request.url))
}
if (!currentUser && !request.nextUrl.pathname.startsWith('/login')) {
return Response.redirect(new URL('/login', request.url))
}
}
export const config = {
matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
}
يستخدم هذا المثال Response.redirect
للتعامل مع إعادة التوجيه مبكرًا في خط الطلب، مما يجعله فعالًا ومركزيًا للتحكم في الوصول.
بعد المصادقة الناجحة، من المهم إدارة تنقل المستخدم بناءً على أدواره. على سبيل المثال، قد يتم إعادة توجيه مستخدم مسؤول إلى لوحة تحكم المسؤول، بينما يتم إرسال مستخدم عادي إلى صفحة مختلفة. هذا مهم للتجارب الخاصة بالأدوار والتنقل المشروط، مثل مطالبة المستخدمين بإكمال ملفهم الشخصي إذا لزم الأمر.
عند إعداد التفويض، من المهم التأكد من أن الفحوصات الأمنية الرئيسية تحدث حيث يصل تطبيقك إلى البيانات أو يغيرها. بينما يمكن أن يكون Middleware مفيدًا للتحقق الأولي، لا يجب أن يكون خط الدفاع الوحيد في حماية بياناتك. يجب تنفيذ الجزء الأكبر من الفحوصات الأمنية في طبقة الوصول إلى البيانات (DAL).
حماية مسارات واجهة برمجة التطبيقات (API Routes)
تُعد مسارات واجهة برمجة التطبيقات (API Routes) في Next.js ضرورية للتعامل مع المنطق من جانب الخادم وإدارة البيانات. من الأهمية بمكان تأمين هذه المسارات لضمان أن المستخدمين المصرح لهم فقط يمكنهم الوصول إلى الوظائف المحددة. يتضمن ذلك عادةً التحقق من حالة مصادقة المستخدم وأذوناته المستندة إلى الأدوار.
إليك مثالًا لتأمين مسار واجهة برمجة التطبيقات:
import { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const session = await getSession(req)
// التحقق مما إذا كان المستخدم مصادقًا عليه
if (!session) {
res.status(401).json({
error: 'User is not authenticated',
})
return
}
// التحقق مما إذا كان للمستخدم دور 'admin'
if (session.user.role !== 'admin') {
res.status(401).json({
error: 'Unauthorized access: User does not have admin privileges.',
})
return
}
// المتابعة مع المسار للمستخدمين المصرح لهم
// ... تنفيذ مسار واجهة برمجة التطبيقات
}
export default async function handler(req, res) {
const session = await getSession(req)
// التحقق مما إذا كان المستخدم مصادقًا عليه
if (!session) {
res.status(401).json({
error: 'User is not authenticated',
})
return
}
// التحقق مما إذا كان للمستخدم دور 'admin'
if (session.user.role !== 'admin') {
res.status(401).json({
error: 'Unauthorized access: User does not have admin privileges.',
})
return
}
// المتابعة مع المسار للمستخدمين المصرح لهم
// ... تنفيذ مسار واجهة برمجة التطبيقات
}
يُظهر هذا مثالًا لمسار واجهة برمجة التطبيقات مع فحص أمني من مستويين للمصادقة والتفويض. يقوم أولاً بالتحقق من وجود جلسة نشطة، ثم يتأكد مما إذا كان المستخدم المسجل لديه دور 'admin'. يضمن هذا النهج وصولاً آمنًا، محدودًا بالمستخدمين المصادق عليهم والمصرح لهم، مما يحافظ على أمان قوي لمعالجة الطلبات.
أفضل الممارسات
- إدارة الجلسات الآمنة: أولوية لأمان بيانات الجلسة لمنع الوصول غير المصرح به وخرق البيانات. استخدم التشفير وممارسات التخزين الآمن.
- إدارة الأدوار الديناميكية: استخدم نظامًا مرنًا لأدوار المستخدمين لتسهيل التكيف مع التغييرات في الأذونات والأدوار، وتجنب الأدوار الثابتة.
- نهج الأمان أولاً: في جميع جوانب منطق التفويض، أعط الأولوية للأمان لحماية بيانات المستخدم والحفاظ على سلامة تطبيقك. يتضمن ذلك اختبارًا شاملًا والنظر في نقاط الضعف الأمنية المحتملة.
إدارة الجلسات
تشمل إدارة الجلسات تتبع وإدارة تفاعل المستخدم مع التطبيق بمرور الوقت، مما يضمن الحفاظ على حالته المصادق عليها عبر أجزاء مختلفة من التطبيق.
يمنع هذا الحاجة إلى تسجيلات الدخول المتكررة، مما يعزز كلًا من الأمان وراحة المستخدم. هناك طريقتان رئيسيتان تُستخدمان لإدارة الجلسات: الجلسات المعتمدة على ملفات تعريف الارتباط (Cookies) وجلسات قاعدة البيانات.
الجلسات المعتمدة على ملفات تعريف الارتباط (Cookie-Based Sessions)
🎥 شاهد: تعلم المزيد عن الجلسات المعتمدة على ملفات تعريف الارتباط والمصادقة مع Next.js → YouTube (11 دقيقة).
تدير الجلسات المعتمدة على ملفات تعريف الارتباط بيانات المستخدم عن طريق تخزين معلومات الجلسة المشفرة مباشرة في ملفات تعريف الارتباط بالمتصفح. عند تسجيل دخول المستخدم، يتم تخزين هذه البيانات المشفرة في ملف تعريف الارتباط. يتضمن كل طلب لاحق للخادم هذا الملف، مما يقلل الحاجة إلى استعلامات الخادم المتكررة ويعزز كفاءة جانب العميل.
ومع ذلك، تتطلب هذه الطريقة تشفيرًا دقيقًا لحماية البيانات الحساسة، حيث أن ملفات تعريف الارتباط عرضة لمخاطر الأمان من جانب العميل. يعد تشفير بيانات الجلسة في ملفات تعريف الارتباط مفتاحًا لحماية معلومات المستخدم من الوصول غير المصرح به. يضمن ذلك أنه حتى إذا تم سرقة ملف تعريف الارتباط، تظل البيانات بداخله غير قابلة للقراءة.
بالإضافة إلى ذلك، بينما تكون ملفات تعريف الارتباط الفردية محدودة في الحجم (عادة حوالي 4 كيلوبايت)، يمكن لتقنيات مثل تقسيم ملفات تعريف الارتباط (cookie-chunking) التغلب على هذا القيد عن طريق تقسيم بيانات الجلسة الكبيرة إلى ملفات تعريف ارتباط متعددة.
قد يبدو تعيين ملف تعريف الارتباط في مشروع Next.js كما يلي:
تعيين ملف تعريف الارتباط على الخادم:
import { serialize } from 'cookie'
import type { NextApiRequest, NextApiResponse } from 'next'
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const sessionData = req.body
const encryptedSessionData = encrypt(sessionData)
const cookie = serialize('session', encryptedSessionData, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 60 * 24 * 7, // أسبوع واحد
path: '/',
})
res.setHeader('Set-Cookie', cookie)
res.status(200).json({ message: 'Successfully set cookie!' })
}
import { serialize } from 'cookie'
export default function handler(req, res) {
const sessionData = req.body
const encryptedSessionData = encrypt(sessionData)
const cookie = serialize('session', encryptedSessionData, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 60 * 24 * 7, // أسبوع واحد
path: '/',
})
res.setHeader('Set-Cookie', cookie)
res.status(200).json({ message: 'Successfully set cookie!' })
}
جلسات قاعدة البيانات
تتضمن إدارة جلسات قاعدة البيانات تخزين بيانات الجلسة على الخادم، مع استلام متصفح المستخدم فقط لمعرف الجلسة. يشير هذا المعرف إلى بيانات الجلسة المخزنة على جانب الخادم، دون احتوائه على البيانات نفسها. تعزز هذه الطريقة الأمان، حيث تحتفظ ببيانات الجلسة الحساسة بعيدًا عن بيئة جانب العميل، مما يقلل من خطر التعرض لهجمات جانب العميل. كما أن جلسات قاعدة البيانات أكثر قابلية للتوسع، حيث تستوعب احتياجات تخزين بيانات أكبر.
ومع ذلك، لهذا النهج مقايضاته. يمكن أن يزيد من الحمل على الأداء بسبب الحاجة إلى عمليات بحث في قاعدة البيانات في كل تفاعل للمستخدم. يمكن لاستراتيجيات مثل تخزين بيانات الجلسة مؤقتًا المساعدة في التخفيف من ذلك. بالإضافة إلى ذلك، يعني الاعتماد على قاعدة البيانات أن إدارة الجلسات تكون موثوقة مثل أداء قاعدة البيانات وتوفرها.
إليك مثالًا مبسطًا لتنفيذ جلسات قاعدة البيانات في تطبيق Next.js:
إنشاء جلسة على الخادم:
import db from '../../lib/db'
import { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const user = req.body
const sessionId = generateSessionId()
await db.insertSession({
sessionId,
userId: user.id,
createdAt: new Date(),
})
res.status(200).json({ sessionId })
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' })
}
}
import db from '../../lib/db'
export default async function handler(req, res) {
try {
const user = req.body
const sessionId = generateSessionId()
await db.insertSession({
sessionId,
userId: user.id,
createdAt: new Date(),
})
res.status(200).json({ sessionId })
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' })
}
}
اختيار إدارة الجلسات في Next.js
يعتمد الاختيار بين جلسات تعتمد على ملفات تعريف الارتباط (cookies) وجلسات تعتمد على قواعد البيانات في Next.js على احتياجات تطبيقك. جلسات ملفات تعريف الارتباط أبسط وتناسب التطبيقات الصغيرة ذات الحمل الخفيف على الخادم، ولكنها قد توفر حماية أقل. بينما جلسات قواعد البيانات، رغم تعقيدها، توفر أمانًا أفضل وقابلية للتوسع، مما يجعلها مثالية للتطبيقات الكبيرة الحساسة للبيانات.
مع حلول المصادقة مثل NextAuth.js، تصبح إدارة الجلسات أكثر كفاءة، سواء باستخدام ملفات تعريف الارتباط أو تخزين قاعدة البيانات. هذا الأتمتة يبسط عملية التطوير، ولكن من المهم فهم طريقة إدارة الجلسات المستخدمة من قبل الحل الذي تختاره. تأكد من أنها تتماشى مع متطلبات الأمان والأداء لتطبيقك.
بغض النظر عن اختيارك، ضع الأمان في أولوية استراتيجية إدارة الجلسات. بالنسبة لجلسات ملفات تعريف الارتباط، يعد استخدام ملفات تعريف ارتباط آمنة ومخصصة لبروتوكول HTTP فقط أمرًا بالغ الأهمية لحماية بيانات الجلسة. أما بالنسبة لجلسات قواعد البيانات، فإن النسخ الاحتياطي المنتظم والتعامل الآمن مع بيانات الجلسة أمران أساسيان. تنفيذ آليات انتهاء صلاحية الجلسة وتنظيفها ضروري في كلا النهجين لمنع الوصول غير المصرح به والحفاظ على أداء وموثوقية التطبيق.
أمثلة
فيما يلي حلول مصادقة متوافقة مع Next.js، يرجى الرجوع إلى أدلة البدء السريع أدناه لمعرفة كيفية تكوينها في تطبيق Next.js الخاص بك:
قراءة إضافية
لمواصلة التعلم عن المصادقة والأمان، تحقق من الموارد التالية: