Chuyển tới nội dung chính

Verify token

Bối cảnh​

Trên Shukah, app gọi GET /maeda/api/verify-token để kiểm tra token đã lưu và tải hồ sơ. Auth service trả về cùng hồ sơ đó với cùng tên field của Shukah, nên việc chuyển phần lớn chỉ là đổi base URL và path prefix. Có ba điều khác cần chú ý: nó chỉ chấp nhận JWT của Locate, body 401 của nó khác, và nó không gửi các header app-version mà app đọc từ lời gọi này.

Tính năng​

Gửi JWT của Locate lấy từ exchange-token và nhận lại hồ sơ của client. Lời gọi chỉ đọc: nó không làm mới token hay thay đổi gì. Nó trả 401 khi token thiếu hoặc không hợp lệ, hoặc khi tài khoản của client không ở trạng thái active.

Trong snapshot app ngày 2026-02-26, app gọi nó mỗi lần khởi động, để kiểm tra token đã lưu và tải hồ sơ, và gọi lại từ màn hình Home. Khi câu trả lời là 401, app xóa token đã lưu.

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

Thay thế ShukahGET /maeda/api/verify-token✅ Đã thay thế
Cần sửa app2 breaking · 2 cần lưu ý
  • 🔴 Token Shukah nhận 401 "Unauthorization"; hãy gửi JWT của Locate (lấy từ exchange-token; giữ lại token Shukah).
  • 🔴 Khi gặp 401 này, app xóa token đã lưu (auth.service.ts:101), nên cần exchange token trước.
  • 🟡 Body 401 của Locate là {status,message}, không phải body Laravel; app chỉ đọc message.
  • 🟡 Locate không gửi header phiên bản app (new_version, v.v.); chưa rõ của Shukah (chưa ghi lại response header).

Request​

FieldVị tríKiểuBắt buộcRàng buộcMô tả
AuthorizationheaderstringcóBearer <locate-jwt>. Token Shukah bị từ chối với 401.JWT của Locate. Hiện nay app lấy nó bằng exchange-token và giữ token Shukah; verify-otp của Locate cũng trả JWT, nhưng tạm thời vẫn đăng nhập trên Shukah.
languagequerystringkhôngĐược chấp nhận và bỏ qua.Giữ lại để query string Shukah cũ vẫn chạy.
Bất kỳ query key hoặc header nào khácquery, header—khôngĐược chấp nhận và bỏ qua, ví dụ x-from-application.—

Response​

200 với hồ sơ là một object trần (không có wrapper status hay data):

FieldKiểuNullableĐịnh dạng / ràng buộcMô tả
client_idintegerkhôngCùng số như trên Shukah với client đã migrate (356259 cho client test trên dev). Client đăng ký trong Locate nhận id từ 1,000,000,001 trở lên.Id của client. Nó cũng nằm trong JWT.
odoo_idstringcóChuỗi số trên client test; null khi client không có.Id Odoo (ERP) của client.
contact_phonestringcóSố điện thoại đúng như đã lưu; service không định dạng lại. Số đăng ký qua app được lưu đúng như app gửi và số đã migrate giữ định dạng của Shukah (cả hai đều đã bỏ khoảng trắng).Số điện thoại của client.
statusstringkhôngactive, pending, suspended, blocked hoặc expired. Route này chỉ trả active, vì mọi status khác đều nhận 401.Trạng thái tài khoản.
email_addressstringcóKhông kiểm tra định dạng. Có thể là "" sau khi cập nhật hồ sơ với "".Email của client.
first_namestringcóĐã trim, tối đa 100 ký tự khi ghi. Có thể là "" sau khi cập nhật hồ sơ với "".Tên.
last_namestringcóĐã trim, tối đa 100 ký tự khi ghi. Có thể là "" sau khi cập nhật hồ sơ với "".Họ.
sexintegerkhông0 không rõ hoặc không cung cấp, 1 nam, 2 nữ.Giới tính.
birth_datestringcóYYYY-MM-DD: ngày UTC của giá trị đã được ghi.Ngày sinh.
receive_offersintegerkhông0 hoặc 1.Client có nhận ưu đãi hay không.
push_enabledintegerkhông0 hoặc 1.Push notification có đang bật hay không.
is_food_truck_userintegerkhông0 hoặc 1.Flag user food-truck.
is_cod_disabledintegerkhông0 hoặc 1.1 khi thanh toán khi nhận hàng bị tắt cho client.
{
"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
}

Capture trên dev, 2026-10-01 (client 356259); các field cá nhân đã được thay thế.

Lỗi​

HTTPBodyKhi nào
401{"status":401,"message":"Unauthorization"}Không có header Authorization, hoặc nó không có phần token.
401Cùng body.Token không phải JWT của Locate: token Shukah, token sai định dạng hoặc sai chữ ký.
401Cùng body.Token hợp lệ nhưng client của nó không tồn tại, hoặc status của client không phải active (pending, suspended, blocked hoặc expired).
500{"status":500,"message":"Internal server error"}Lỗi không mong đợi bên trong service. Chưa capture. Ngoài production, message là text lỗi gốc và có thêm field stack.

Body 401 được capture trên dev vào 2026-10-01 khi không có token, và lần nữa với một token Shukah.

Thử ngay​

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"

Kỳ vọng: 200 và hồ sơ hiển thị ở mục Response. Với token Shukah trong $TOKEN bạn nhận 401 như ở mục Lỗi.

Khác biệt so với Shukah​

Body 200 giống nhau: vào 2026-10-01 cả hai service trả cùng 13 key, kiểu và giá trị cho client 356259. Các dòng dưới đây nói về token, URL, body lỗi và các header app-version.

Khía cạnhShukahLocateTác động lên app
TokenToken opaque của Shukah.Chỉ JWT của Locate. Token Shukah nhận 401 (capture trên dev, 2026-10-01).Exchange token Shukah lấy JWT trước khi chuyển, và giữ token Shukah cho order-v2.
URLGET $SHUKAH_BASE_URL/maeda/api/verify-tokenGET $AUTH_BASE_URL/api/v1/clients/verify-token. Host auth không phục vụ /maeda/*: nó trả 404.Đổi base URL và path.
Body 401{"name":"Unauthorized","message":"Your request was made with invalid credentials.","code":0,"status":401,"type":"Illuminate\\Auth\\AuthenticationException"}{"status":401,"message":"Unauthorization"}Mọi chỗ so khớp theo text lỗi, name hay type đều phải đổi. status là 401 ở cả hai.
Header app-versionApp đọc new_version, old_version, has_new_version và is_required_update từ response header của bất kỳ URL nào chứa api/verify-token hoặc client/points (danh sách mặc định của nó), và dùng chúng cho popup cập nhật. Shukah có gửi chúng hay không: Chưa được tài liệu hóa, vì các capture chỉ có request header.Không gửi: không Locate service nào set các header này. URL Locate /api/v1/clients/verify-token không chứa api/verify-token, nên app sẽ không tìm chúng ở đó.Nếu hiện nay Shukah gửi các header này, popup cập nhật sẽ lặng lẽ ngừng xuất hiện sau khi chuyển.

Lưu ý​

  • Chỉ JWT của Locate mới chạy ở đây. Token Shukah nhận 401 và app xóa token đã lưu khi gặp 401, nên hãy exchange nó lấy JWT trước, và giữ token Shukah: order-v2 vẫn cần nó.
  • Token Locate không bao giờ hết hạn và không bao giờ bị thu hồi, nên 401 nghĩa là token sai hoặc tài khoản không active, không bao giờ là timeout.
  • Body 401 là {"status":401,"message":"Unauthorization"} của riêng Locate (đúng chính tả như vậy), không phải của Shukah.

Chi tiết (cho backend)​

Chỉ kiểm tra dev: host https://dev.authentication.locate.sa, prefix /api/v1. Host và prefix của UAT và production: Chưa được tài liệu hóa. Dev không chạy với NODE_ENV=production: body lỗi của nó có field stack (capture 2026-10-01 trên các lỗi address-book, profile và setting), và handler chỉ thêm stack ngoài production (src/middlewares/error-handler.js:12-35).

Route và guard​

GET /clients/verify-token chạy middleware client rồi một handler một dòng trả về client mà middleware đã tải (src/routes/client.js:13, src/controllers/client/verify-token/index.js:7).

  • Guard chỉ đọc phần thứ hai (ngăn cách bằng dấu cách) của Authorization. Từ đầu tiên (Bearer, Token, bất cứ gì) không được kiểm tra. Header không có phần thứ hai sẽ lỗi với "jwt must be provided" và cho 401 (src/middlewares/client.js:15-22,38-41).
  • Token sai định dạng, sai chữ ký hoặc token Shukah làm jwt.verify throw, và guard trả 401 (src/middlewares/client.js:22,38-41).
  • Guard tải client theo client_id trong token. Client không tồn tại cho 401 (src/middlewares/client.js:27-33).
  • Không được kiểm tra: cờ soft-delete, phone của token so với phone đã lưu, các dòng token_bridge, thiết bị và IP (src/services/client.js:5-10: query chỉ là { client_id }).

Quy tắc token​

JWT của Locate được ký bằng HS256 và mang {client_id, phone, iat}. Service ký và verify không có option nào, nên không token nào có exp: không gì hết hạn và không gì bị thu hồi (src/shared/helpers/jwt.js:4-10).

Quy tắc trạng thái active​

Chỉ ACTIVE mới được xác thực. CLIENT_ACTIVE_STATUSES là [ACTIVE], nên PENDING, SUSPENDED, BLOCKED, EXPIRED và status bị thiếu đều cho 401 (src/shared/constant/user.js:4-14, src/middlewares/client.js:27-33).

Kết quả tra cứu client được cache trong CLIENT_CACHE_TTL giây, mặc định 300 (src/services/client.js:5-10). Các lần ghi qua Mongoose save() xóa cache. Lần ghi ETL hàng loạt (bulkWrite) thì không, nên một thay đổi status đến từ Shukah sync có thể mất tới chừng đó thời gian mới có hiệu lực (src/models/client.js:158-161).

Profile mapper​

formatClientProfileForResponse dựng body (src/controllers/client/setting/mapper.js:24-38).

  • status là dạng chữ thường của status đã lưu. Giá trị đã lưu nằm ngoài năm giá trị trên sẽ thành pending (src/controllers/client/setting/mapper.js:5-11,28). Sync ánh xạ text status của Shukah sang status đã lưu trong src/sync/lib/casts.js:24-33.
  • sex đến từ gender đã lưu: MALE là 1, FEMALE là 2, mọi giá trị khác là 0 (src/controllers/client/setting/mapper.js:14-18). Bốn flag là 0 hoặc 1 (src/controllers/client/setting/mapper.js:20).
  • contact_phone là phone đã lưu. Phone đăng ký qua reset-otp được lưu đúng như đã gửi, với khoảng trắng bị bỏ (src/controllers/client/reset-otp/handler.js:40,56, src/sync/lib/casts.js:40-41). Phone đã migrate đi qua cùng một normaliser bỏ khoảng trắng (src/sync/mappers/client-mapper.js:99).
  • Các quy tắc ghi đứng sau các ràng buộc của field:
    • first_name và last_name được ghi sau khi trim. Một lần cập nhật hồ sơ chấp nhận null hoặc một chuỗi tối đa 100 ký tự sau khi trim, và trả 400 nếu không. "" được lưu là "". Model cũng giới hạn cả hai tên ở 100 ký tự, nên đăng ký với tên dài hơn sẽ thất bại (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: cập nhật hồ sơ chấp nhận mọi chuỗi hoặc null, không kiểm tra định dạng và không giới hạn độ dài, và lưu nó đã trim, nên "" vẫn là "" (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 được giữ dưới dạng chuỗi YYYY-MM-DD, ngày UTC của giá trị được ghi. Cập nhật hồ sơ trả 400 cho giá trị mà new Date() không đọc được, và null xóa nó (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). Tài liệu của service ghi định dạng là YYYY-MM-DD, và null khi rỗng (docs/api/client-auth-and-address-book.md:454).
  • Cùng object này được trả bởi verify-otp (data), PUT /api/v1/clients/setting và PUT /api/v1/clients/:client_id. GET /api/v1/users trả một object khác ở một chế độ (xem User detail).
  • Id: sync copy Shukah id của client đã migrate vào client_id (src/sync/mappers/client-mapper.js:90). Client đăng ký trong Locate nhận 1,000,000,000 + sequence (src/models/counter.js:7,23).

Khi tài liệu và code mâu thuẫn (code thắng)​

  • Tài liệu của service nói status là "active" hoặc "inactive" (docs/api/client-auth-and-address-book.md:177). Code trả năm giá trị ở trên, và cùng tài liệu đó liệt kê chúng ở dòng 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
  • Tài liệu của locate-authentication-service@6757aa4: docs/api/client-auth-and-address-book.md §3 (dòng 150-183, 453, 454; thay đổi lần cuối 2026-07-27), docs/plans/2026-07-14-shukah-verify-token-api-local.md §2 (2026-07-14)
  • Snapshot mobile mobile/locate-client@e960c3a (branch development, 2026-02-26; code của chính app, không phải 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 đã lưu bị xóa khi gặp 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
  • Tìm new_version, old_version, has_new_version và is_required_update trong locate-authentication-service@6757aa4, locate-point-service@2c44ce3 và locate-order-v2-api@d9e4e5f6 không có kết quả nào (2026-10-01).
  • Capture 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