تفاصيل المستخدم
الخلفية
GET /api/v1/users هو route خدمة المصادقة العام لسؤال «لمن هذا الـ token؟». كُتب قبل
الترحيل، ولا توجد واجهة من Shukah تقابله. ومع x-auth-from: SHUKAH يفحص token ويُرجع
الملف الشخصي للعميل.
وهو مهم للترحيل لأن خدمات backend أخرى تستخدمه. فـ order-v2 وخدمة النقاط تستدعيانه لتحويل الـ token الذي استلمتاه إلى عميل. وقد يكون ذلك الـ token من Shukah أو JWT من Locate، لذا يقرّر هذا الـ route أي tokens تقبلها routes النقاط في order-v2.
الميزة
أرسل الـ token في Authorization وحدّد أي backend يتحقق منه في x-auth-from. المستدعون
يرسلون SHUKAH.
- order-v2: حارس الملف الشخصي (profile guard) لديه، الذي يحمي كثيرًا من routes العميل مثل الطلبات وبطاقات الهدايا، يستدعي
هذا الـ route مع
x-auth-from: SHUKAH. - خدمة النقاط: يستدعي الـ client middleware فيها هذا الـ route لمعرفة هوية المستدعي. وهو يرسل
x-auth-fromمن إعداده الخاصAUTH_VERIFY_APPLICATION(SHUKAHافتراضيًا)، ويقرأ معرّف العميل من الإجابة، ويعطي مستدعيَه401عندما لا يجيب هذا الـ route بـ2xx. - مستدعون آخرون: غير موثّق بعد.
التطبيق نفسه لا يستدعي هذا الـ route: فنسخة التطبيق اللحظية بتاريخ 2026-02-26 لا تحتوي أي استدعاء له. ولا يحتاج
التطبيق إلى إرسال x-auth-from إلى routes النقاط في order-v2، لأن order-v2 وخدمة النقاط
تضبطانه بنفسيهما عندما تستدعيان هذا الـ route.
وأي tokens تعمل يعتمد على إعداد العميل auth_source (shukah أو locate)، الذي يملكه locate-go.
وإذا تعذّر على الخدمة قراءة الإعداد، تستخدم locate.
| الـ Token | auth_source ليس locate | auth_source هو locate |
|---|---|---|
| token من Shukah | يعمل. تطلب الخدمة من Shukah فحصه (GET /maeda/api/verify-token) وتُرجع إجابة Shukah دون تغيير. | يعمل عندما يكون الـ token أحد tokens الخدمة النشطة المخزَّنة. لم يُلتقط. |
| JWT من Locate | يعمل فقط إذا كانت Shukah تعرف الـ JWT، لأن الخدمة ما زالت تسأل Shukah. | لا يعمل أبدًا في الشيفرة المثبَّتة (pinned): فهو يحصل دائمًا على 401. وهذا عيب (defect). |
على dev، في 2026-10-01، حصل token من Shukah وJWT من Locate كلاهما على 200 بالـ body نفسه. وفي
2026-10-02 تغيّر ذلك. فقد كان auth_source على dev هو shukah (مقروءًا من إعدادات العميل العامة)، لذا
يمرّر هذا الـ route الـ token إلى Shukah على dev. أما routes النقاط في order-v2، التي تستدعي هذا الـ route، فأعطت
JWT من Locate 401 وtoken من Shukah 200: فلم تعد Shukah على dev تقبل الـ JWT (استنتاج؛ لم يُستدعَ
هذا الـ route نفسه مع x-auth-from في ذلك اليوم). ومع ضبط auth_source على locate سيفشل الـ JWT
أيضًا، بسبب العيب المذكور في الجدول. فعلى dev اليوم لا يعمل هنا إلا token من Shukah،
ولا يجعل أي إعداد وحده JWT من Locate يعمل.
GET /api/v1/users
الطلب (Request)
| الحقل | الموضع | النوع | مطلوب | القيود | الوصف |
|---|---|---|---|---|---|
Authorization | header | string | نعم، إلا في MERCHANT | Bearer <token>. والجزء الثاني بعد المسافة هو الـ token. والمفقود يعطي 401. | الـ token المراد فحصه. وعلى dev اليوم لا يمر إلا token من Shukah (2026-10-02). |
x-auth-from | header | string | نعم | واحدة من SHUKAH أو LOCATE_V2 أو ADMIN_SHUKAH أو ADMIN_LOCATE أو MERCHANT. وهي حساسة لحالة الأحرف: فمع وجود Authorization، تعطي shukah الخطأ 422. يستخدم المستدعون SHUKAH، وبقية هذه الصفحة عنها. | أي backend يتحقق من الـ token. |
access-token وclient وuid | header | string | MERCHANT فقط | الثلاثة كلها مطلوبة لـ MERCHANT. | جلسة تاجر (merchant). |
الاستجابة (Response)
مع x-auth-from: SHUKAH، 200 مع الملف الشخصي للعميل ككائن مجرد. ويعتمد الـ body على كيفية
فحص الخدمة للـ token:
| كيف فُحص الـ token | Body |
|---|---|
تمرير (Relay): auth_source ليس locate. | body الخاص بـ verify-token من Shukah نفسه، دون تغيير. وعلى dev كانت له الحقول الـ 13 نفسها في التحقق من الـ token: status هو active وsex هو 0 أو 1 أو 2، والأعلام 0 أو 1. |
محليًا، token من Shukah: auth_source هو locate. | شكل مختلف: status هو ACTIVE الخام، وsex هو MALE أو FEMALE أو UNKNOWN، والأعلام الأربعة قيم منطقية (boolean)، ويُحذف odoo_id عندما لا يكون للعميل واحد. لم يُلتقط. |
محليًا، JWT من Locate: auth_source هو locate. | لا نجاح: 401 في كل مرة في الشيفرة المثبَّتة. |
{
"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)، مرة مع token من Shukah ومرة مع JWT من Locate: وأعاد الاستدعاءان هذا الـ body. واستُبدلت الحقول الشخصية. وفي 2026-10-02 حصل JWT من Locate على 401 من routes النقاط التي تستدعي هذا الـ route، فهذا الـ body هو ما يحصل عليه token من Shukah اليوم.
الأخطاء
| HTTP | Body | متى |
|---|---|---|
401 | {"status":401,"message":"Unauthorization"} | لا يوجد header باسم Authorization (إلا في MERCHANT). أو رفضت Shukah الـ token، أو الـ token ليس بين tokens المخزَّنة. أو أي استثناء أثناء فحص الـ token. أو auth_source هو locate والـ token هو JWT من Locate. |
422 | {"status":422,"message":"Your application is not support"} | Authorization موجود، لكن x-auth-from مفقود أو ليس من القيم الخمس. |
404 | {"status":404,"message":"User not found"} | مع x-auth-from: LOCATE_V2 فقط، عندما يكون المستخدم القديم (legacy) مفقودًا. |
500 | {"status":500,"message":"Internal server errors"} | يُلقي الـ handler استثناءً، أو يفشل الـ upstream الخاص بـ MERCHANT بأي شيء غير 401. |
التُقط body الخاص بـ 401 على dev في 2026-10-01 دون token. والتُقط body الخاص بـ 422
Your application is not support على dev في 2026-10-02، مع JWT من Locate ودون
x-auth-from. وأما الأجسام الأخرى فمن الشيفرة.
جرّبه
export AUTH_BASE_URL=https://dev.authentication.locate.sa
export SHUKAH_TOKEN='<shukah-token>' # a Locate JWT is expected to get 401 here on dev today (inference); see Feature
curl -s "$AUTH_BASE_URL/api/v1/users" -H "Authorization: Bearer $SHUKAH_TOKEN" -H "x-auth-from: SHUKAH"
المتوقع على dev (2026-10-02، استنتاج: لم يُستدعَ هذا الـ route مع x-auth-from في ذلك اليوم، بل
استُدعيت routes النقاط التي تستدعيه): 200 والملف الشخصي المبيّن تحت «الاستجابة» لـ token من Shukah،
و401 لـ JWT من Locate، إذ يمرّره dev إلى Shukah التي لم تعد تقبله. وفي 2026-10-01 حصل الـ token
كلاهما على 200. وبدون x-auth-from تحصل على 422 المبيّن تحت «الأخطاء».
تنبيه
- يُتوقَّع أن يحصل JWT من Locate على
401هنا على dev اليوم (استنتاج من routes النقاط، 2026-10-02)، وفي الشيفرة المثبَّتة يحدث ذلك دائمًا عندما يكونauth_sourceهوlocate، فلا تَعِد بدعم JWT من Locate على هذا الـ route. x-auth-fromحساس لحالة الأحرف:SHUKAHيعمل، وshukahيحصل على422. وإحدى وثائق الخدمة تكتبه بأحرف صغيرة.- في وضع التمرير (relay) يذهب كل استدعاء إلى Shukah أيضًا ويحدّث سجل مستخدم قديم (legacy)، ولا يعمل JWT من Locate إلا إذا كانت Shukah تعرفه.
التفاصيل (للـ backend)
فُحص dev فقط: الـ host https://dev.authentication.locate.sa والبادئة /api/v1. أما الـ host والبادئة في UAT
وproduction: غير موثّق بعد.
المستدعون
- خدمة النقاط: يحتاج
clientAuthMiddlewareإلىAuthorization: Bearer <token>، ثم يستدعيGET {AUTH_SERVICE_URL}/api/v1/usersبذلك الـ header، مع ضبطx-auth-fromعلىAUTH_VERIFY_APPLICATION(الافتراضيSHUKAH) وAccept: application/json. والإجابة غير2xxتصبح401. ومعرّف العميل هو أول قيمة غير null منclient_idوuser_idوidفي الإجابة، ويجب أن يكون عددًا صحيحًا موجبًا. ولا يُمرَّرx-auth-fromالخاص بالمستدعي أبدًا. - order-v2: يجري
UserProfileGuardالاستدعاء نفسه بالـ headerAuthorizationالذي استلمه وx-auth-from: SHUKAH. ويُطبَّق بـ@UseGuards(UserProfileGuard)على كثير من controllers العميل، مثل الطلبات وبطاقات الهدايا.
قيم x-auth-from الخمس
ترتيب الفحوص: MERCHANT أولًا، ثم غياب Authorization (401)، ثم قيمة
x-auth-from (422)، ثم switch. والمقارنة حساسة لحالة الأحرف
(src/middlewares/user.js:13-44، src/shared/constant/auth.js:1-7).
x-auth-from | ما يحدث | body النجاح | المصدر |
|---|---|---|---|
SHUKAH | يُفحص الـ token كما في الجدول التالي. | الملف الشخصي. | src/middlewares/user.js:36-37,100-187 |
LOCATE_V2 (الحالة الافتراضية في الـ switch) | يُبحث عن الـ token في مجموعة users القديمة (legacy). والمستخدم القديم بحالة DEACTIVATE يعطي 401. ثم يُرجع الـ handler وثيقة المستخدم المخزَّنة، أو 404. | وثيقة المستخدم القديم. | src/middlewares/user.js:42-43,201-228؛ src/controllers/users/detail/handler.js:26-36 |
ADMIN_SHUKAH | يجب أن يساوي الـ token token المسؤول المضبوط، وإلا 401. | 200 فارغ. | src/middlewares/user.js:38-39,189-199؛ src/controllers/users/detail/handler.js:15-17 |
ADMIN_LOCATE | GET {LOCATE_GO_BASE_URL}/api/admin/api/v1/profile مع Bearer token. وأي فشل يعطي 401. | الملف الشخصي من الـ upstream. | src/middlewares/user.js:40-41,250-275 |
MERCHANT | يحتاج الـ headers access-token وclient وuid. وغياب أحدها يعطي 422 {"status":422,"message":"Unauthorization"}. ثم GET {MERCHANT_BASE_URL}/api/v1/users/profile. و401 من الـ upstream يعطي 401، وغيرها من حالات الفشل تعطي 500. | الملف الشخصي من الـ upstream. | src/middlewares/user.js:54-98 |
مفقودة أو أي قيمة أخرى، مع وجود Authorization | 422 {"status":422,"message":"Your application is not support"}. | — | src/middlewares/user.js:28-33؛ src/shared/constant/error-message.js:62 |
كيف يفحص SHUKAH الـ token
auth_source | JWT من Locate (ثلاثة أجزاء تفصلها نقاط تمامًا) | token معتم (Opaque) | المصدر |
|---|---|---|---|
locate | يستدعي verifyClientToken الدالة ClientService.findByToken، فتُلقي TypeError: Client.findByToken(...).cacheQuery is not a function. فالدالة static في النموذج هي async، فتُرجع Promise، وليس للـ Promise cacheQuery. ويحوّلها الـ catch إلى 401، في كل مرة. فحص دون اتصال بـ JWT صالح والـ middleware الحقيقي. | يجد صف token_bridge نشطًا بحسب الـ token الخام، ثم العميل. وعدم العثور عليه يعطي 401. والعثور عليه يضبط الملف الشخصي من toShukahClientProfile (أنواع خام). | src/middlewares/user.js:104-130,230-248؛ src/services/client.js:12-35؛ src/models/client.js:163-175 |
أي قيمة أخرى (عادةً shukah) | تمرير (Relay): GET {SHUKAH_HOST}/maeda/api/verify-token مع Authorization: Bearer <token>. و200 مع body يصبح الملف الشخصي، كما أرسلته Shukah. وأي فشل يعطي 401. | التمرير نفسه. | src/middlewares/user.js:132-186 |
تحسم getAuthSource() القيمة بهذا الترتيب: متغير البيئة AUTH_SOURCE، ثم
auth_source المخزَّن مؤقتًا في مفتاح settings:client في Redis، ثم GET {LOCATE_API_URL}/settings/client،
ثم القيمة الافتراضية locate (src/services/setting.js:12-51،
docs/plans/2026-08-25-point-ledger-shared-write-contract.md:408-421). وتحدّث رسالة Kafka من locate-go
الـ cache (src/events/settingConsumer.js:14-33،
docs/plans/2026-07-28-settings-broker-consumer.md:43-68).
- لا يفحص أي من فرعَي
locateحالة الحساب. فـClientService.findOneيرشّح بـclient_idفقط، وClient.findByTokenيرشّح بـis_deleted: falseوهاتف الـ token فقط. لذا لا يفرض هذا الـ route شرط «النشط فقط» كما يفعل الـ middleware المسمّىclient(قراءة شيفرة،src/services/client.js:5-17،src/models/client.js:163-175). - في فرع التمرير (relay) يكتب كل استدعاء في مجموعة
usersالقديمة: عملية upsert من حمولة Shukah. فالحقلstatusبالقيمةactiveيصبحACTIVE، وأي قيمة أخرىDEACTIVATE، وsexبقيمة0يصبحMALE، وأي قيمة أخرىFEMALE. وقاعدةsexهذه تختلف عن ترميز الملف الشخصي نفسه (1ذكر،2أنثى) (src/middlewares/user.js:145-172). - يقبل التمرير JWT من Locate فقط إذا كانت Shukah تعرفه. وهذا يعتمد على دفع JWTs الجديدة من Locate
إلى Shukah (انظر استبدال الـ token)، لذا فالعميل الذي فشل دفعه أو
تُخطّي (لا معرّف له في Shukah) سيحصل على
401هنا ما دامauth_sourceهوshukah(قراءة شيفرة). - التوقيت على dev: 630 ms لـ token من Shukah و1061 ms لـ JWT من Locate، مقابل 161 إلى 331 ms للاستدعاءات التي تبقى داخل الخدمة. وهذا يتسق مع قفزة (hop) إلى upstream.
ما لا تفسّره الالتقاطتان
أعاد الاستدعاءان الملتقطان body بتنسيق Shukah (status بأحرف صغيرة، وsex عدد صحيح، وأعلام 0 و1)
وكانا بطيئين. وفي الشيفرة المثبَّتة لا يستطيع إنتاج هذا الـ body لـ JWT من Locate سوى فرع التمرير. فإما
أن auth_source على dev لم يكن locate في 2026-10-01، وقبلت Shukah الـ JWT من Locate
(وهذا يتسق مع كون استدعاء exchange-token الذي أنشأه قد دفعه إلى Shukah، إن كان الدفع مفعّلًا
على dev)، وإما أن dev لا يشغّل 6757aa4. ولا الشيفرة ولا الوثائق تقول أيهما: غير موثّق
بعد. وهذا استنتاج، لا حقيقة.
للتمييز بينهما، قارن مدة تشغيل العملية (uptime) من route /health غير المصادَق (دون بادئة)
مع دمج 6757aa4 في 2026-10-01 11:57 +0700، واقرأ auth_source من إعدادات العميل
(src/routes/health.js:8، src/controllers/health/handler.js:11-19).
أُجيب عنه في 2026-10-02. أعادت إعدادات العميل العامة
(GET https://dev.go.locate.sa/api/v1/settings/client، وهو الـ host في SETTINGS_BASE_URL بالتطبيق)
القيمة "auth_source":"shukah". وقد يتجاوزها متغير البيئة AUTH_SOURCE ولا يمكن
قراءته من الخارج، فهذا مرجَّح جدًّا وليس مثبتًا. ومدة التشغيل في /health وهي 90,815 s تعني أن
الخدمة بدأت نحو 2026-10-01 06:43 UTC، أي بعد دمج 6757aa4 (04:57 UTC) بساعة و46 دقيقة، فالأرجح أن dev
يشغّل 6757aa4 أو ما بعده. فالتفسير الأول هو الأرجح: ففي 2026-10-01 عمل فرع
الـ relay وقبلت Shukah على dev الـ JWT من Locate. وفي 2026-10-02 رفضه الـ relay نفسه، ولهذا تعطي
routes النقاط في order-v2 لـ JWT من Locate 401 Invalid or expired token
(locate-point-service: services/api/src/services/client-auth.service.ts:17,22-29). ولماذا
توقفت Shukah على dev عن قبول الـ JWT: غير موثّق بعد. والسبب المرجَّح أن دفع الـ token من Locate إلى Shukah
(src/config/envConfig.js:22-35) لم يصل، أو لم يعد موجودًا في نسخة Laravel من Shukah على dev
(استنتاج).
وعيب locate + JWT موجود أيضًا في origin/production عند 4f3bc96 (2026-09-21)، الذي يملك
src/services/client.js وsrc/models/client.js نفسيهما كما في 6757aa4. وهل يعمل أي شيء من
ذلك الـ ref، وهل يُخطَّط لإصلاح قبل أن ينقلب auth_source: غير موثّق بعد.
حيث تختلف الوثائق عن الشيفرة (القول للشيفرة)
- تكتب وثيقة تصميم قيمة الـ header بالشكل
x-auth-from: shukah(docs/plans/2026-08-25-point-ledger-shared-write-contract.md:418). أما الشيفرة فتقارن بـSHUKAHتمامًا، والقيمة بأحرف صغيرة تحصل على422. - تقول وثيقة تصميم إن JWTs من Locate "short-circuit to
verifyClientToken… and were never broken" (docs/plans/2026-07-27-shukah-opaque-token-auth-fix.md:51-52). وكان ذلك صحيحًا في 2026-07-27 وصار قديمًا منذ commit3396fd1(2026-07-28): فمع ضبطauth_sourceعلىlocate، يعطي فرع JWT دائمًا401. - 2026-10-02: قالت هذه الصفحة إن token من Shukah وJWT من Locate حصلا كلاهما على
200على dev (2026-10-01)، وإن «جرّبه» فيها يتوقع200لأي من الـ token. وفي 2026-10-02 كانauth_sourceعلى dev هوshukahولم تعد Shukah على dev تقبل الـ JWT من Locate، فلا يعمل إلا token من Shukah؛ ومع ضبطauth_sourceعلىlocateيحصل كل JWT على401لأنsrc/services/client.js:12-17يستدعي.cacheQueryعلى الـ Promise الذي تُرجعهClient.findByTokenمن نوعasync(src/models/client.js:163).
Sources
locate-authentication-service@6757aa4:src/routes/user.js:5-12,src/middlewares/user.js:13-275،src/controllers/users/detail/handler.js:9-45,src/services/client.js:5-35،src/models/client.js:163-175,src/services/setting.js:12-51،src/events/settingConsumer.js:14-33,src/shared/constant/auth.js:1-7،src/shared/constant/error-message.js:59,62,77,src/routes/health.js:8،src/controllers/health/handler.js:11-19- وثائق
locate-authentication-service@6757aa4:docs/plans/2026-08-25-point-ledger-shared-write-contract.md:408-421،docs/plans/2026-07-28-settings-broker-consumer.md:43-68،docs/plans/2026-07-27-shukah-opaque-token-auth-fix.md:51-52 locate-point-service@2c44ce3:services/api/src/middleware/client-auth.ts:7-18،services/api/src/services/client-auth.service.ts:15-37locate-order-v2-api@d9e4e5f6:src/auth/guards/user-profile-auth.guard.ts:58-72،src/modules/client/orders/orders.controller.ts:84،src/modules/client/gift-card-code/gift-card-code.controller.ts:10،src/modules/client/point/point.controller.ts:17-45(ليس لـ routes النقاط حارس وهي تمرّرAuthorizationإلى خدمة النقاط)- نسخة الجوال اللحظية
mobile/locate-client@e960c3a(الفرعdevelopment، 2026-02-26؛ شيفرة التطبيق نفسه، وليست من Locate): البحث في الشجرة كاملة عنapi/v1/usersلا يجد شيئًا locate-authentication-service@6757aa4:src/config/envConfig.js:22-35- التقاطات dev، 2026-10-02 (parity audit):
user-detail--locate-jwtوpoints--locate-jwtوpoints--locate-st؛ وإعدادات العميل على dev ومدة تشغيل/healthلخدمة المصادقة، وكلتاهما قُرئتا في 2026-10-02 - التقاطات dev، 2026-10-01:
locate-users-shukah-tokenوlocate-users-locate-tokenوprobe-locate-usersوshukah-verify-token