من أكتر الأخطاء تكرارًا بواجهات REST APIs: إرجاع 401 بمكان 403 أو العكس. الفرق بسيط نظريًا، بس كتير مطورين بيتجاهلوه بالتطبيق العملي — وهاد بيربك عملاء الـ API ويصعّب تشخيص المشاكل.
401 Unauthorized — "ما بعرف مين إنت"
401 معناها الخادم ما قدر يتحقق من هويتك أصلًا. إما:
- ما أرسلت أي رمز مصادقة (Authorization header) أصلًا.
- الرمز يلي أرسلته منتهي الصلاحية أو غير صحيح.
- بيانات تسجيل الدخول خاطئة.
GET /api/orders/9
(بدون Authorization header)
→ 401 Unauthorized
WWW-Authenticate: Bearer
حسب مواصفة HTTP الرسمية RFC 9110، رد 401 لازم يرافقه ترويسة WWW-Authenticate توضّح للعميل آلية المصادقة المطلوبة (Bearer, Basic...) — بمعنى تاني: "جرّب تسجّل دخول من جديد، وهاد كيف".
403 Forbidden — "بعرف مين إنت، بس ممنوع"
403 معناها الخادم فهم هويتك تمامًا — الرمز صحيح ومصادق عليه — بس ما إلك صلاحية توصل للمورد المطلوب أو تنفّذ هاد الإجراء.
GET /api/admin/users
Authorization: Bearer <رمز صحيح لمستخدم عادي>
→ 403 Forbidden
هون المستخدم مسجّل دخول بنجاح، بس دوره "مستخدم عادي" مش "أدمن" — والمورد محصور بالأدمن فقط. لا علاقة لهاد الموقف بصحة الرمز؛ العلاقة كلها بالصلاحيات.
جدول المقارنة
| المعيار | 401 Unauthorized | 403 Forbidden |
|---|---|---|
| المعنى | هوية غير معروفة أو غير صالحة | هوية معروفة، صلاحية غير كافية |
| السؤال يلي بيجاوب عليه | "مين إنت؟" | "مسموحلك تعمل هاد؟" |
| الحل من ناحية العميل | سجّل دخول / جدّد الرمز | تواصل مع الأدمن لصلاحية إضافية — إعادة تسجيل الدخول ما بتفيد |
| ترويسة مرافقة شائعة | WWW-Authenticate | لا يوجد معيار مرافق ثابت |
| المصطلح الدقيق | Authentication (مصادقة) | Authorization (تفويض/صلاحيات) |
💡 لو مصطلح "Authentication" و"Authorization" لسا مربكين إلك، فيه درس كامل عن الفرق بينهم بمسار REST APIs — القاعدة الذهبية: Authentication بتثبت مين إنت، Authorization بتحدد شو مسموحلك تعمل.
الخطأ الشائع
كتير APIs بترجع 401 بكل موقف رفض — سواء المستخدم مش مسجّل دخول أو مسجّل دخول بس صلاحيته ناقصة. هاد بيربك عميل الـ API: هل المشكلة برمز المصادقة (لازم يسجّل دخول من جديد)، أو بالصلاحيات (تسجيل دخول جديد ما رح يغيّر شي)؟
// ❌ غير دقيق — نفس الرد بغض النظر عن السبب
if (!req.user) return res.status(401).json({ error: "غير مسموح" });
if (req.user.role !== "admin") return res.status(401).json({ error: "غير مسموح" });
// ✅ دقيق — كل حالة بكودها الصحيح
if (!req.user) return res.status(401).json({ error: "سجّل دخول أولًا" });
if (req.user.role !== "admin") return res.status(403).json({ error: "صلاحياتك لا تسمح بهاد الإجراء" });
الفرق مش تفصيل نظري بس — عميل الواجهة (تطبيق موبايل أو واجهة ويب) بيقدر يبني سلوك مختلف تلقائيًا: 401 بتوجّه المستخدم لصفحة تسجيل الدخول، 403 بتعرض رسالة "ما إلك صلاحية" بدون ما تطرده من حسابه.
خلاصة
- 401 = المصادقة فشلت أو غير موجودة — "ما بعرف مين إنت".
- 403 = المصادقة نجحت، الصلاحية ناقصة — "بعرفك، بس ممنوع".
- استخدم كل كود بموضعه الصحيح — عميل واجهتك (وفريقك أثناء التصحيح) رح يشكرك.