Files
fitness/backend/AUTHENTICATION_API.md

37 lines
3.8 KiB
Markdown

# مصادقة SportPath — API v1
هذه الواجهات تأسيس أولي لمصادقة رقم الهاتف. لا تشغّل إرسال OTP قبل إعداد مزود حقيقي ومراجعة عقده على بيئة sandbox. لا يخرج الرمز أو المفاتيح في response أو logs.
## إعداد الخادم
ضع القيم في ملف البيئة الخاص خارج مجلد `public/`:
```dotenv
JWT_SIGNING_KEY=<random secret, at least 32 bytes>
JWT_ACCESS_TTL_SECONDS=900
JWT_REFRESH_TTL_DAYS=30
OTP_PROVIDER=generic_json
OTP_API_URL=https://provider.example/api/send
OTP_API_KEY=<server-only token>
OTP_SENDER_ID=<approved sender>
OTP_MESSAGE_TEMPLATE=رمز التحقق الخاص بك هو {code}
OTP_ENABLED=true
OTP_HASH_KEY=<independent random secret, at least 32 bytes>
```
المحول الحالي يرسل JSON بالشكل `{"to":"+...","sender":"...","message":"..."}` مع `Authorization: Bearer ...`، ويعد أي HTTP 2xx نجاحًا. هذا عقد عام مؤقت وليس افتراضًا عن أي مزود؛ عدّل `ConfiguredHttpOtpProvider` ليتوافق مع توثيق المزود الفعلي قبل تفعيل الإنتاج. يرفض endpoint العناوين غير HTTPS. في حال غياب الإعداد يبقى الطلب مغلقًا ويرجع 503.
## المسارات
- `POST /api/v1/auth/request-otp.php`: JSON `{ "phone_e164": "+962…", "purpose": "register|login", "device_uuid": "UUID" }`. يرد `challenge_id` ومدة الصلاحية. حد الإرسال الحالي 5 لكل رقم/ساعة، 20 لكل IP/ساعة، و10 لكل device/ساعة؛ المحاولات لكل تحدٍ 5 خلال 5 دقائق.
- `POST /api/v1/auth/verify-otp.php`: JSON `{ "challenge_id": "UUID", "code": "123456", "display_name": "…", "device_uuid": "UUID", "platform": "ios|android|web" }`. يستهلك التحدي مرة واحدة ويصدر JWT وrefresh token عشوائيًا. يجب حفظ refresh token في مخزن آمن على الجهاز، وعدم تسجيل أي token.
- `POST /api/v1/auth/refresh.php`: JSON `{ "refresh_token": "…" }`. تدوير الرمز يصدر refresh جديدًا؛ إعادة استخدام رمز سبق تدويره تبطل عائلة الجلسة.
- `POST /api/v1/auth/logout.php`: يتطلب `Authorization: Bearer <access-token>` ويبطل الجلسة الحالية.
- `/api/v1/admin/settings.php`: يتطلب bearer token ودور `owner` أو `content_manager`؛ GET للقراءة وPATCH للتعديل مع audit trail.
كل Access Token قصير العمر ويرتبط بجلسة حية في MySQL. الدور وحالة الحساب يعاد التحقق منهما من قاعدة البيانات لكل طلب محمي. لا تُقبل معرفات عتادية ثابتة؛ `device_uuid` معرّف تثبيت عشوائي. ترقية أول مالك تتم يدويًا على قاعدة الخادم بعد التحقق من رقم الهاتف، ولا توجد واجهة bootstrap عامة لمنح دور owner.
## ما لم يكتمل بعد
تطبيق Flutter مرتبط الآن بنقاط OTP والجلسات ويخزن الرموز في secure storage؛ مزامنة التمارين ترسل Bearer وتجدد الجلسة عند الحاجة. نقاط التتبع تقبل HMAC فقط إذا ضُبط `LEGACY_HMAC_ENABLED=true` صراحة لفترة ترحيل عميل قديم، وقيمته في المثال `false`. لم يُختبر اتصال MySQL الفعلي أو إرسال SMS؛ فحوص PHP/Dart حتى الآن ساكنة وتحليلية فقط. نفّذ migrations `001_phone_auth_and_sessions.sql` و`002_workout_idempotency.sql` و`003_training_content.sql` على staging بعد backup، وتحقق من rate limits والتدوير والإبطال مع مزود OTP في sandbox قبل الإنتاج.