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
GET /maeda/api/verify-token✅ Đã thay thế- 🔴 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
| Field | Vị trí | Kiểu | Bắt buộc | Ràng buộc | Mô tả |
|---|---|---|---|---|---|
Authorization | header | string | có | 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. |
language | query | string | khô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ác | query, 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):
| Field | Kiểu | Nullable | Định dạng / ràng buộc | Mô tả |
|---|---|---|---|---|
client_id | integer | không | Cù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_id | string | có | Chuỗi số trên client test; null khi client không có. | Id Odoo (ERP) của client. |
contact_phone | string | có | 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. |
status | string | không | active, 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_address | string | có | 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_name | string | có | Đã 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_name | string | có | Đã trim, tối đa 100 ký tự khi ghi. Có thể là "" sau khi cập nhật hồ sơ với "". | Họ. |
sex | integer | không | 0 không rõ hoặc không cung cấp, 1 nam, 2 nữ. | Giới tính. |
birth_date | string | có | YYYY-MM-DD: ngày UTC của giá trị đã được ghi. | Ngày sinh. |
receive_offers | integer | không | 0 hoặc 1. | Client có nhận ưu đãi hay không. |
push_enabled | integer | không | 0 hoặc 1. | Push notification có đang bật hay không. |
is_food_truck_user | integer | không | 0 hoặc 1. | Flag user food-truck. |
is_cod_disabled | integer | không | 0 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
| HTTP | Body | Khi nào |
|---|---|---|
401 | {"status":401,"message":"Unauthorization"} | Không có header Authorization, hoặc nó không có phần token. |
401 | Cùng body. | Token không phải JWT của Locate: token Shukah, token sai định dạng hoặc sai chữ ký. |
401 | Cù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ạnh | Shukah | Locate | Tác động lên app |
|---|---|---|---|
| Token | Token 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. |
| URL | GET $SHUKAH_BASE_URL/maeda/api/verify-token | GET $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-version | App đọ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
401và app xóa token đã lưu khi gặp401, 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
401nghĩa là token sai hoặc tài khoản không active, không bao giờ là timeout. - Body
401là{"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à cho401(src/middlewares/client.js:15-22,38-41). - Token sai định dạng, sai chữ ký hoặc token Shukah làm
jwt.verifythrow, và guard trả401(src/middlewares/client.js:22,38-41). - Guard tải client theo
client_idtrong token. Client không tồn tại cho401(src/middlewares/client.js:27-33). - Không được kiểm tra: cờ soft-delete,
phonecủa token so với phone đã lưu, các dòngtoken_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).
statuslà 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ànhpending(src/controllers/client/setting/mapper.js:5-11,28). Sync ánh xạ text status của Shukah sang status đã lưu trongsrc/sync/lib/casts.js:24-33.sexđến từ gender đã lưu:MALElà1,FEMALElà2, mọi giá trị khác là0(src/controllers/client/setting/mapper.js:14-18). Bốn flag là0hoặc1(src/controllers/client/setting/mapper.js:20).contact_phonelà 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_namevàlast_nameđược ghi sau khi trim. Một lần cập nhật hồ sơ chấp nhậnnullhoặc một chuỗi tối đa 100 ký tự sau khi trim, và trả400nế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ặcnull, 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ỗiYYYY-MM-DD, ngày UTC của giá trị được ghi. Cập nhật hồ sơ trả400cho giá trị mànew Date()không đọc được, vànullxó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ànullkhi 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/settingvàPUT /api/v1/clients/:client_id.GET /api/v1/userstrả 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ận1,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
statuslà"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(branchdevelopment, 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ặp401ở: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_versionvàis_required_updatetronglocate-authentication-service@6757aa4,locate-point-service@2c44ce3vàlocate-order-v2-api@d9e4e5f6khô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