المصادقة
يستخدم أمل مصادقة Bearer JWT بلا ملفات تعريف ارتباط للجلسات. يُقرَن رمز وصول قصير العمر برمز تجديد طويل العمر ومتدوّر.
| الرمز | العمر | يُخزَّن أين | يتدوّر؟ |
|---|---|---|---|
| رمز الوصول | 15 دقيقة (expiresIn: 900) | لدى العميل فقط؛ بلا حالة | لا |
| رمز التجديد | 7 أيام | على الخادم | نعم، عند كل استخدام |
التسجيل
POST /api/auth/register للتمهيد فقط. يُنشئ الاستدعاء الأول مسؤولًا بنطاق SUPER ويعيد زوج
رموز. بعد ذلك يُغلَق التسجيل: أي استدعاء لاحق يعيد 403 مع code: "PERMISSION_DENIED".
organizationId مطلوب لكل دور عدا المسؤول الأول بنطاق SUPER الذي يحذفه. ويُنشأ كل المستخدمين
اللاحقين عبر إنشاء المستخدمين، لا عبر التسجيل.
تسجيل الدخول
POST /api/auth/login بجسم { email, password } ← 200 AuthTokensResponse
({ accessToken, refreshToken, expiresIn, tokenType }). تعيد بيانات الاعتماد الخاطئة 401.
حماية القوة الغاشمة تعيد 401 + Retry-After، لا 429. بعد 5 محاولات فاشلة لكل بريد خلال 15
دقيقة تُقفَل نقطة النهاية وتستجيب بـ 401 وترويسة Retry-After (عدد ثواني الانتظار). تظهر ترويسات
X-RateLimit-* بعد أول محاولة فاشلة. لا تتوقّع 429 صريحًا من هذه الواجهة.
يوجد طلب دخول كامل مع استجابته (curl و TypeScript) في البدء السريع ← الخطوة 2.
استخدام رمز الوصول
أرسل رمز الوصول مع كل طلب محمي:
curl https://localhost:3000/api/auth/me \
-H "Authorization: Bearer <accessToken>"يعيد GET /api/auth/me هويتك إضافةً إلى مصفوفة permissions[] التي تحكم كل نقطة نهاية (MeResponse).
التجديد والتدوير
POST /api/auth/refresh بجسم { refreshToken } ← AuthTokensResponse جديد. رموز التجديد تتدوّر:
يُلغى الرمز القديم عند الاستخدام. إعادة استخدام رمز تجديد مُدوَّر تعيد 401 وتُلغي الجلسة (كشف
سرقة). خزّن دائمًا أحدث رمز تجديد من آخر استجابة.
تسجيل الخروج
POST /api/auth/logout (يتطلّب رمز وصول صالحًا) يُلغي الجلسة على الخادم. رموز الوصول غير قابلة للإلغاء
الفردي؛ فهي قصيرة العمر بحسب التصميم، لذا تنتهي ببساطة.
إعادة تعيين كلمة المرور
تدفّق من خطوتين، آمن ضد التعداد:
POST /api/auth/password-resetبجسم{ email }← دائمًا200(لا يكشف أبدًا إن كان البريد موجودًا) ← يُرسَل رابط سحري (صلاحية ساعة واحدة).POST /api/auth/password-reset/confirmبجسم{ token, newPassword }← يعيد تعيين كلمة المرور ويُلغي كل الجلسات القائمة.
دخول الطالب بالرمز السريع
لسهولة الاستخدام في الصف الأول، يسجّل الطلاب الدخول بلا بريد أو كلمة مرور:
curl -X POST https://localhost:3000/api/auth/student/quickcode \
-H "Content-Type: application/json" \
-d '{ "quickCode": "472", "classId": "clx4z2k0u0000xyz1234abcde" }'POST /api/auth/student/quickcode برمز سريع من رقمين (01–99) + classCode رمز الصف من 6 أرقام
يعيد زوج رموز بنطاق الطالب (QuickCodeLoginResponse مع tokens وstudentId وclassId). الرمزان
زوج اعتماد واحد: رمز الصف يصل داخل الرابط الذي يدفعه المعلّم (/student-login?class=<classCode>)،
ويكتب الطالب رمزه الخاص فقط.
- رمز الطالب لكل صف (فريد بين طلاب الصف الفعّالين، فيتّسع الصف لـ 99 طالبًا كحدّ أقصى) ولا ينتهي أبدًا؛ وهو أيضًا كلمة مرور الطالب في الدخول المنزلي.
- رمز الصف صالح سنة كاملة (365 يومًا) من إصداره. ويمكن تجديده يدويًا من لوحة رابط الصف إذا وصل الرابط إلى غير المعنيّين (أو
POST /api/classes/{classId}/login-code/rotate)، ونسخ رابط منتهٍ يجدّده تلقائيًا. يتوقف الرابط القديم فورًا، ولا تتغيّر رموز الطلاب عند تجديد رمز الصف. - رمز غير صالح أو منتهٍ (للطالب أو للصف) يعيد
401معcode: "QUICKCODE_INVALID_OR_EXPIRED"؛ وكثرة المحاولات تعيد401معcode: "QUICKCODE_RATE_LIMIT". GET /api/auth/student/class-preview?classCode=(عام ومحدود المعدّل) يعيد اسمَي الصف والمدرسة لصفحة الدخول؛ ورمز صف منتهٍ يعيد410معcode: "CLASS_CODE_EXPIRED".- صيغة الطلب المباشرة بـ
classIdمرفوضة في الإنتاج.
تسجيل الدخول عبر Google
يستطيع المستخدمون المستوردون من نظام إدارة التعلم تسجيل الدخول بحساباتهم على Google. يستخدم التدفق PKCE ورمز تسليم قصير العمر:
- البدء:
GET /api/auth/google/start?loginHint=<email>يعيد{ redirectUrl }. وجِّه متصفّح المستخدم إلىredirectUrl. - إعادة التوجيه: يعيد Google التوجيه إلى
GET /api/auth/google/callback. يُصدر الخادم معاملَ?code=لمرة واحدة لمدة 60 ثانية ويعيد توجيه المتصفح إلى التطبيق. - التبادل: يُرسل التطبيق فورًا
POSTبـ{ code }إلىPOST /api/auth/google/exchangeويستقبلAuthTokensResponseمعياريًا.
GET /api/auth/google/start يعيد 503 عندما لا يكون GOOGLE_CLIENT_ID مُضبَّطًا في البيئة.
تتحكّم قائمة النطاقات المسموح بها (allowedGoogleDomains على سجل المؤسسة) في حسابات Google
المقبولة.
ما الموجود (وغير الموجود) في الرمز
يحمل الـ JWT الـ userId (sub) والـ organizationId (orgId) ورمز الدور (role) ومعرّف الجلسة
(sid)، إضافةً إلى مطالبتَي iat/exp المعيارية. لا يُدرَج اسم أو بريد أو صف. اقرأ هذه من
GET /api/auth/me. لا يوجد تدوير لسرّ الرمز / مطالبة sv في الموجة الأولى (مؤجَّل لموجة لاحقة)،
لا تبنِ على وجوده.