إنتقل إلى المحتوى الرئيسي

التحقق من الـ token

الخلفية​

على Shukah كان التطبيق يستدعي GET /maeda/api/verify-token للتحقق من الـ token المحفوظ وتحميل الملف الشخصي. تُرجع خدمة المصادقة (auth service) الملف الشخصي نفسه بأسماء حقول Shukah نفسها، لذا فالتحوّل في معظمه تغيير في base URL وبادئة المسار. وثلاثة أمور أخرى تستحق العناية: أنه يقبل JWT من Locate فقط، وأن body الخاص بـ 401 مختلف، وأنه لا يرسل headers إصدار التطبيق (app-version headers) التي يقرؤها التطبيق من هذا الاستدعاء.

الميزة​

أرسل الـ JWT من Locate الصادر عن exchange-token واحصل على الملف الشخصي للعميل. الاستدعاء للقراءة فقط: فلا يجدّد الـ token ولا يغيّر أي شيء. ويجيب بـ 401 عندما يكون الـ token مفقودًا أو غير صالح، أو عندما لا يكون حساب العميل نشطًا.

في نسخة التطبيق اللحظية بتاريخ 2026-02-26، يستدعيه التطبيق في كل مرة يبدأ فيها، للتحقق من الـ token المحفوظ وتحميل الملف الشخصي، ومرة أخرى من الشاشة الرئيسية (Home). وعندما تكون الإجابة 401، يحذف التطبيق الـ token المخزَّن.

GET /api/v1/clients/verify-token​

يستبدل ShukahGET /maeda/api/verify-token✅ تم الاستبدال
يحتاج تعديل التطبيق2 breaking · 2 للمراقبة
  • 🔴 يحصل token من Shukah على 401 "Unauthorization"؛ أرسل JWT من Locate (من exchange-token؛ واحتفظ بـ token من Shukah).
  • 🔴 عند هذا الـ 401 يحذف التطبيق الـ token المخزَّن (auth.service.ts:101)، لذا استبدل الـ token أولًا.
  • 🟡 body الـ 401 في Locate هو {status,message} وليس body Laravel؛ ويقرأ التطبيق message فقط.
  • 🟡 لا ترسل Locate رؤوس إصدار التطبيق (new_version وغيرها)؛ ورؤوس Shukah غير معروفة (لم تُسجَّل رؤوس الاستجابة).

الطلب (Request)​

الحقلالموضعالنوعمطلوبالقيودالوصف
AuthorizationheaderstringنعمBearer <locate-jwt>. ويُرفض token Shukah بـ 401.الـ JWT من Locate. ويحصل عليه التطبيق اليوم عبر exchange-token ويحتفظ بـ token Shukah؛ ويُرجع verify-otp في Locate واحدًا أيضًا، لكن تسجيل الدخول يبقى على Shukah حاليًا.
languagequerystringلايُقبل ويُتجاهل.يُبقى حتى تظل سلسلة query القديمة لـ Shukah تعمل.
أي مفتاح query أو header آخرquery، header—لايُقبل ويُتجاهل، مثل x-from-application.—

الاستجابة (Response)​

200 مع الملف الشخصي ككائن مجرد (بلا غلاف status أو data):

الحقلالنوعيقبل nullالصيغة / القيودالوصف
client_idintegerلاالرقم نفسه كما في Shukah للعميل المرحَّل (356259 لعميل الاختبار على dev). والعملاء المسجَّلون في Locate يحصلون على معرّفات من 1,000,000,001 فصاعدًا.معرّف العميل. وهو داخل الـ JWT أيضًا.
odoo_idstringنعمسلسلة رقمية لعميل الاختبار؛ وnull عندما لا يكون للعميل واحد.معرّف العميل في Odoo (نظام ERP).
contact_phonestringنعمالهاتف تمامًا كما خُزِّن؛ ولا تعيد الخدمة تنسيقه. فالهواتف المسجَّلة من التطبيق تُخزَّن كما أرسلها التطبيق، والهواتف المرحَّلة تحتفظ بتنسيق Shukah (تُحذف المسافات في الحالتين).هاتف العميل.
statusstringلاactive أو pending أو suspended أو blocked أو expired. ويُرجع هذا الـ route القيمة active فقط، لأن كل حالة أخرى تحصل على 401.حالة الحساب.
email_addressstringنعملا فحص للصيغة. قد يكون "" بعد تحديث الملف الشخصي بقيمة "".بريد العميل الإلكتروني.
first_namestringنعمتُحذف المسافات الزائدة، وبحد أقصى 100 حرف عند الكتابة. قد يكون "" بعد تحديث الملف الشخصي بقيمة "".الاسم الأول.
last_namestringنعمتُحذف المسافات الزائدة، وبحد أقصى 100 حرف عند الكتابة. قد يكون "" بعد تحديث الملف الشخصي بقيمة "".اسم العائلة.
sexintegerلا0 غير معروف أو غير مُدخَل، 1 ذكر، 2 أنثى.الجنس.
birth_datestringنعمYYYY-MM-DD: تاريخ UTC للقيمة التي كُتبت.تاريخ الميلاد.
receive_offersintegerلا0 أو 1.هل يتلقى العميل العروض.
push_enabledintegerلا0 أو 1.هل إشعارات push مفعّلة.
is_food_truck_userintegerلا0 أو 1.علامة (flag) مستخدم عربة الطعام (food-truck).
is_cod_disabledintegerلا0 أو 1.1 عندما يكون الدفع عند الاستلام معطّلًا للعميل.
{
"client_id": 356259,
"odoo_id": "1342170",
"contact_phone": "+966500000000",
"status": "active",
"email_address": "user@example.com",
"first_name": "Test",
"last_name": "User",
"sex": 1,
"birth_date": "1990-01-01",
"receive_offers": 0,
"push_enabled": 0,
"is_food_truck_user": 1,
"is_cod_disabled": 0
}

التُقط على dev، 2026-10-01 (العميل 356259)؛ واستُبدلت الحقول الشخصية.

الأخطاء​

HTTPBodyمتى
401{"status":401,"message":"Unauthorization"}لا يوجد header باسم Authorization، أو لا يحتوي على جزء الـ token.
401الـ body نفسه.الـ token ليس JWT من Locate: token من Shukah، أو token مشوَّه، أو ذو توقيع خاطئ.
401الـ body نفسه.الـ token صالح لكن العميل المرتبط به غير موجود، أو حالة العميل ليست active (معلّق أو موقوف أو محظور أو منتهٍ).
500{"status":500,"message":"Internal server error"}فشل غير متوقع داخل الخدمة. لم يُلتقط. وخارج production تكون الرسالة نص الخطأ الخام ويُضاف حقل stack.

التُقط body الخاص بـ 401 على dev في 2026-10-01 دون token، ومرة أخرى مع token من Shukah.

جرّبه​

export AUTH_BASE_URL=https://dev.authentication.locate.sa
export TOKEN='<locate-jwt>' # see "Getting a test token" on the module overview
curl -s "$AUTH_BASE_URL/api/v1/clients/verify-token" -H "Authorization: Bearer $TOKEN"

المتوقع: 200 والملف الشخصي المبيّن تحت «الاستجابة». ومع token من Shukah في $TOKEN تحصل على 401 المبيّن تحت «الأخطاء».

الفروق عن Shukah​

body الخاص بـ 200 هو نفسه: ففي 2026-10-01 أرجعت الخدمتان المفاتيح الـ 13 والأنواع والقيم نفسها للعميل 356259. وتغطي الصفوف أدناه الـ token وURL وbody الخطأ وheaders إصدار التطبيق.

الجانبShukahLocateالأثر على التطبيق
الـ Tokentoken معتم (opaque) من Shukah.JWT من Locate فقط. وtoken من Shukah يحصل على 401 (التُقط على dev، 2026-10-01).استبدل token Shukah بالـ JWT قبل التحوّل، واحتفظ بـ token Shukah لـ order-v2.
URLGET $SHUKAH_BASE_URL/maeda/api/verify-tokenGET $AUTH_BASE_URL/api/v1/clients/verify-token. لا يخدم host المصادقة /maeda/*: بل يجيب بـ 404.غيّر base URL والمسار.
body الخاص بـ 401{"name":"Unauthorized","message":"Your request was made with invalid credentials.","code":0,"status":401,"type":"Illuminate\\Auth\\AuthenticationException"}{"status":401,"message":"Unauthorization"}يجب تغيير أي شيء يطابق نص الخطأ أو name أو type. وقيمة status هي 401 في الاثنين.
headers إصدار التطبيقيقرأ التطبيق new_version وold_version وhas_new_version وis_required_update من headers الاستجابة لأي URL يحتوي على api/verify-token أو client/points (قائمته الافتراضية)، ويستخدمها في نافذة التحديث المنبثقة. وهل ترسلها Shukah: غير موثّق بعد، لأن الالتقاطات تضم headers الطلب فقط.لا تُرسل: فلا خدمة من Locate تضبط هذه الـ headers. ولا يحتوي URL الخاص بـ Locate وهو /api/v1/clients/verify-token على api/verify-token، فلن يبحث التطبيق عنها هناك.إذا كانت Shukah ترسل الـ headers اليوم، فستتوقف نافذة التحديث عن الظهور بصمت بعد التحوّل.

تنبيه​

  • لا يعمل هنا سوى JWT من Locate. فtoken من Shukah يحصل على 401 ويحذف التطبيق الـ token المخزَّن عند 401، لذا استبدله بالـ JWT أولًا، واحتفظ بـ token Shukah: فما زال order-v2 يحتاجه.
  • token من Locate لا تنتهي صلاحيته ولا يُبطَل أبدًا، لذا فإن 401 تعني token سيئًا أو حسابًا غير نشط، لا انتهاء مهلة أبدًا.
  • body الخاص بـ 401 هو {"status":401,"message":"Unauthorization"} الخاص بـ Locate (بهذا الإملاء)، لا الخاص بـ Shukah.

التفاصيل (للـ backend)​

فُحص dev فقط: الـ host https://dev.authentication.locate.sa والبادئة /api/v1. أما الـ host والبادئة في UAT وproduction: غير موثّق بعد. لا يعمل dev بـ NODE_ENV=production: فأجسام أخطائه تحمل حقل stack (التُقط في 2026-10-01 على أخطاء address-book وprofile وsetting)، والـ handler لا يضيف stack إلا خارج production (src/middlewares/error-handler.js:12-35).

الـ Route والحارس (guard)​

يشغّل GET /clients/verify-token الـ middleware المسمّى client ثم handler من سطر واحد يُرجع العميل الذي حمّله الـ middleware (src/routes/client.js:13، src/controllers/client/verify-token/index.js:7).

  • يقرأ الحارس الجزء الثاني فقط من Authorization (بعد المسافة). والكلمة الأولى (Bearer أو Token أو أي شيء) لا تُفحص. والـ header الذي لا جزء ثانيَ فيه يفشل بالرسالة "jwt must be provided" ويعطي 401 (src/middlewares/client.js:15-22,38-41).
  • الـ token المشوَّه أو ذو التوقيع الخاطئ أو token من Shukah يجعل jwt.verify يُلقي استثناءً، ويجيب الحارس بـ 401 (src/middlewares/client.js:22,38-41).
  • يحمّل الحارس العميل بحسب client_id الموجود في الـ token. والعميل المفقود يعطي 401 (src/middlewares/client.js:27-33).
  • لا يُفحص: علم الحذف الناعم (soft-delete)، ولا phone الموجود في الـ token مقابل الهاتف المخزَّن، ولا صفوف token_bridge، ولا الجهاز ولا IP (src/services/client.js:5-10: الاستعلام هو { client_id } فقط).

قواعد الـ Token​

يُوقَّع JWT من Locate بـ HS256 ويحمل {client_id, phone, iat}. وتوقّع الخدمة وتتحقق دون خيارات، لذا لا يحمل أي token قيمة exp: فلا شيء ينتهي ولا شيء يُبطَل (src/shared/helpers/jwt.js:4-10).

قاعدة الحالة النشطة​

وحده ACTIVE يستطيع المصادقة. قيمة CLIENT_ACTIVE_STATUSES هي [ACTIVE]، لذا فإن PENDING وSUSPENDED وBLOCKED وEXPIRED وغياب الحالة كلها تعطي 401 (src/shared/constant/user.js:4-14، src/middlewares/client.js:27-33).

يُخزَّن البحث عن العميل مؤقتًا (cache) لمدة CLIENT_CACHE_TTL ثانية، والافتراضي 300 (src/services/client.js:5-10). والكتابة عبر save() في Mongoose تمسح الـ cache. أما الكتابة الجماعية في ETL (bulkWrite) فلا تفعل، لذا قد يستغرق تغيير الحالة القادم من مزامنة Shukah حتى هذه المدة ليُطبَّق (src/models/client.js:158-161).

محوّل الملف الشخصي (mapper)​

تبني formatClientProfileForResponse الـ body (src/controllers/client/setting/mapper.js:24-38).

  • status هو الصيغة بأحرف صغيرة للحالة المخزَّنة. والقيمة المخزَّنة من خارج الخمس تصبح pending (src/controllers/client/setting/mapper.js:5-11,28). وتحوّل المزامنة نص حالة Shukah إلى الحالة المخزَّنة في src/sync/lib/casts.js:24-33.
  • sex يأتي من الجنس المخزَّن: MALE هي 1 وFEMALE هي 2 وأي شيء آخر 0 (src/controllers/client/setting/mapper.js:14-18). والأعلام الأربعة قيمتها 0 أو 1 (src/controllers/client/setting/mapper.js:20).
  • contact_phone هو الهاتف المخزَّن. والهاتف المسجَّل عبر reset-otp يُخزَّن كما أُرسل، مع حذف المسافات (src/controllers/client/reset-otp/handler.js:40,56، src/sync/lib/casts.js:40-41). وتمر الهواتف المرحَّلة عبر المطبِّع (normaliser) نفسه الذي يحذف المسافات (src/sync/mappers/client-mapper.js:99).
  • قواعد الكتابة وراء قيود الحقول:
    • يُكتب first_name وlast_name بعد حذف المسافات الزائدة. ويقبل تحديث الملف الشخصي null أو سلسلة بحد أقصى 100 حرف بعد حذف المسافات، ويجيب بـ 400 خلاف ذلك. وتُخزَّن "" كما هي "". ويحدّ النموذج الاسمين أيضًا بـ 100 حرف، لذا يفشل التسجيل باسم أطول (src/controllers/client/update-profile/validate.js:4-6,24-25، src/controllers/client/update-profile/index.js:13-15، src/controllers/client/update-profile/parse-payload.js:5,11-12، src/models/client.js:45-52).
    • email_address: يقبل تحديث الملف الشخصي أي سلسلة أو null، دون فحص للصيغة ودون حد للطول، ويخزّنها بعد حذف المسافات الزائدة، فتبقى "" كما هي "" (src/controllers/client/update-profile/validate.js:26، src/controllers/client/update-profile/parse-payload.js:13، src/models/client.js:57-60).
    • يُحفظ birth_date كسلسلة YYYY-MM-DD، وهي تاريخ UTC للقيمة المكتوبة. ويجيب تحديث الملف الشخصي بـ 400 عن قيمة لا يستطيع new Date() قراءتها، ويمسحها null (src/sync/lib/casts.js:2-11، src/controllers/client/update-profile/validate.js:27-30، src/controllers/client/update-profile/parse-payload.js:14-16). وتعطي وثيقة الخدمة الصيغة YYYY-MM-DD، وnull عند الفراغ (docs/api/client-auth-and-address-book.md:454).
  • يُرجع الكائن نفسه كل من verify-otp (data) وPUT /api/v1/clients/setting و PUT /api/v1/clients/:client_id. أما GET /api/v1/users فيُرجع كائنًا مختلفًا في أحد الأوضاع (انظر تفاصيل المستخدم).
  • المعرّفات: تنسخ المزامنة معرّف Shukah للعميل المرحَّل إلى client_id (src/sync/mappers/client-mapper.js:90). والعملاء المسجَّلون في Locate يحصلون على 1,000,000,000 + sequence (src/models/counter.js:7,23).

حيث تختلف الوثائق عن الشيفرة (القول للشيفرة)​

  • تقول وثيقة الخدمة إن status هو "active" أو "inactive" (docs/api/client-auth-and-address-book.md:177). وتُرجع الشيفرة القيم الخمس أعلاه، وتسردها الوثيقة نفسها في السطر 453.

Sources​

  • locate-authentication-service@6757aa4: src/routes/client.js:13، src/middlewares/client.js:8-42, src/controllers/client/verify-token/index.js:3-7، src/controllers/client/setting/mapper.js:5-38, src/shared/helpers/jwt.js:4-10، src/shared/constant/user.js:4-14, src/services/client.js:5-10, src/models/client.js:158-161، src/models/counter.js:7,23, src/sync/lib/casts.js:2-11,24-33,40-41، src/sync/mappers/client-mapper.js:90,99, src/shared/constant/error-message.js:59، src/middlewares/error-handler.js:12-35, src/models/client.js:45-52,57-60، src/controllers/client/update-profile/validate.js:4-6,24-30، src/controllers/client/update-profile/index.js:13-15، src/controllers/client/update-profile/parse-payload.js:5,11-16، src/controllers/client/reset-otp/handler.js:40,56
  • وثائق locate-authentication-service@6757aa4: docs/api/client-auth-and-address-book.md §3 (الأسطر 150-183، 453، 454؛ آخر تغيير 2026-07-27)، docs/plans/2026-07-14-shukah-verify-token-api-local.md §2 (2026-07-14)
  • نسخة الجوال اللحظية mobile/locate-client@e960c3a (الفرع development، 2026-02-26؛ شيفرة التطبيق نفسه، وليست من Locate): platforms/mobile/products/locate/app/sideEffects/effects/initSecondaryResources.tsx:37-43، platforms/mobile/products/locate/screens/home/mainInner/Home/components/Screen/Home.tsx:53، src/entities/user/model/services/init/init.ts:21، src/entities/user/model/services/fetchUserInfo/fetchUserInfo.ts:50، src/entities/user/model/api/services/auth.service.ts:85-112 (يُحذف الـ token المخزَّن عند 401 في :101-103)، src/shared/api/interceptors/createResponseInterceptor.ts:18-29، src/shared/features/featuresConfigs.ts:41,90,107-110، src/features/AppVersionUpdate/ui/AppVersionUpdatePopup/AppVersionUpdatePopup.tsx:64
  • البحث في locate-authentication-service@6757aa4 وlocate-point-service@2c44ce3 و locate-order-v2-api@d9e4e5f6 عن new_version وold_version وhas_new_version و is_required_update لا يجد أي تطابق (2026-10-01).
  • التقاطات dev، 2026-10-01: locate-verify-token وshukah-verify-token و locate-verify-token-with-shukah-token وprobe-locate-verify-token وprobe-shukah-verify-token و probe-auth-host-maeda-prefix