OTP login
Bối cảnh
Trên Shukah, app đăng nhập bằng hai lời gọi. GET /maeda/api/reset-otp gửi mã qua SMS, và
GET /maeda/api/verify-otp đổi mã lấy token. Chính lời gọi reset-otp đó cũng đăng ký một
client mới. Auth service có cả hai route với cùng tên query như 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ú ý: verify-otp trả về JWT của Locate thay vì
token Shukah, lỗi của nó có body khác, và nó bỏ qua fcm_token và platform mà app gửi. reset-otp
cũng diễn đạt lỗi thiếu phone khác đi.
Tính năng
Đăng nhập và đăng ký dùng cùng hai lời gọi.
- App gọi reset-otp với số điện thoại. Nếu một client active có phone đó, service
gửi mã 4 chữ số qua SMS và trả
{"status":true}. - Nếu không client active nào có phone đó và lời gọi không mang các field đăng ký, reset-otp
trả
400vớiLast Name cannot be blank.. Message đó là tín hiệu để app mở màn hình đăng ký. App sau đó gọi lại reset-otp với các field đăng ký, và service tạo client rồi gửi mã. - App gọi verify-otp với phone và mã. Nó nhận một JWT Locate và hồ sơ của client,
và gửi JWT đó dưới dạng
Authorization: Bearer <token>trên mọi lời gọi sau.
Chưa chuyển đăng nhập sang Locate. Đăng nhập Locate chỉ trả về JWT, mà các route points của order-v2
hiện từ chối JWT. Cho đến khi order-v2 chấp nhận nó, hãy vẫn đăng nhập trên Shukah, lấy JWT bằng
exchange-token (target_platform: locate), và giữ cả hai token:
JWT cho auth service, token Shukah cho order-v2 (xem
Danh sách chuyển đổi).
Trong snapshot app ngày 2026-02-26, đăng nhập gửi phone đã bỏ dấu +, còn đăng ký
thêm tên và email, và tùy chọn sex, birth_date và receive_offers. App mở
màn hình đăng ký khi reset-otp trả 400 với message có chứa Last Name, và nó
lưu access_token mà verify-otp trả về.
Các quy tắc của mã:
| Quy tắc | Giá trị |
|---|---|
| Độ dài | 4 chữ số. Có thể bắt đầu bằng 0. |
| Hiệu lực | 3 phút. Chỉ dùng được một lần. |
| Gửi lại | Khi một mã đang chờ, gọi lại reset-otp không gửi gì và vẫn trả {"status":true}. Chỉ có thể có mã mới sau khi mã cũ hết hạn hoặc đã dùng. |
| Số lần thử | Không giới hạn số lần nhập sai mã và không có rate limit cho cả hai lời gọi. |
| Test mode | Tài khoản test-mode luôn nhận mã 2022, và không gửi SMS. Số nào trên dev là test-mode: Chưa được tài liệu hóa. |
GET /api/v1/clients/reset-otp
GET /maeda/api/reset-otp✅ Đã thay thế- 🟡 Lỗi 400 thiếu số điện thoại trên Locate là tiếng Anh (Yii), trên Shukah là tiếng Ả Rập (Laravel); app hiển thị message nên chữ thay đổi.
- 🟡 Số điện thoại chưa tồn tại nhận 400 "Last Name cannot be blank." trên Locate (theo code); nội dung hiện tại của Shukah chưa được ghi lại.
- 🟡 App chỉ mở màn hình đăng ký nếu message của 400 chứa "Last Name"; hãy xác nhận Shukah và Locate khớp nhau.
Một route phục vụ cả đăng nhập lẫn đăng ký, giống Shukah; trên Locate, số điện thoại chưa tồn tại nhận 400 "Last Name cannot be blank." (tín hiệu để app mở màn hình đăng ký; theo code, còn nội dung hiện tại của Shukah chưa được tài liệu hóa) và receive_offers không được đọc.
Request
Không cần token. Khi phone thuộc về một client active, các field đăng ký bị bỏ qua.
| Field | Vị trí | Kiểu | Bắt buộc | Ràng buộc | Mô tả |
|---|---|---|---|---|---|
contact_phone | query | string | có | Khoảng trắng bị bỏ. Nó được so khớp với phone đã lưu có hoặc không có dấu + ở đầu (+966… bằng 966…, nhưng 05… không bằng +9665…). Không kiểm tra độ dài hay định dạng. Trống hoặc thiếu cho 400. | Phone để đăng nhập hoặc đăng ký. |
last_name | query | string | chỉ khi đăng ký | Cần khi không client active nào có phone. Trống cho 400 Last Name cannot be blank. (kiểm tra đầu tiên). Tối đa 100 ký tự. | Họ của client mới. |
first_name | query | string | chỉ khi đăng ký | Trống cho 400 First Name cannot be blank. (kiểm tra thứ hai). Tối đa 100 ký tự. | Tên của client mới. |
email_address | query | string | chỉ khi đăng ký | Trống cho 400 Email Address cannot be blank. (kiểm tra thứ ba). Không kiểm tra định dạng email. | Email của client mới. |
birth_date | query | string | không | Được đọc như một ngày và lưu dạng YYYY-MM-DD. Giá trị không phải ngày sẽ lặng lẽ được lưu rỗng. | Ngày sinh của client mới. |
sex | query | 1 hoặc 2 | không | 1 là nam và 2 là nữ. Mọi giá trị khác được lưu là không rõ. | Giới tính của client mới. |
language | query | string | không | Lưu phần trước dấu -, chữ thường (ar-SA thành ar). Thiếu thì thành ar. Nó không đổi ngôn ngữ SMS, luôn là tiếng Ả Rập. | Ngôn ngữ của client mới. |
| Bất kỳ query key hoặc header nào khác | query, header | — | không | Bị bỏ qua, ví dụ key. receive_offers cũng không được đọc: client mới bắt đầu với giá trị tắt. | — |
Response
200 với cùng một body cho đăng nhập và đăng ký:
| Field | Kiểu | Nullable | Định dạng / ràng buộc | Mô tả |
|---|---|---|---|---|
status | boolean | không | Luôn là true. | Một mã đã được cấp, hoặc đang có một mã chờ. Nó không xác nhận SMS đã được gửi. |
{
"status": true
}
Được capture trên dev, 2026-10-02, với một phone đã biết, trên Locate và trên Shukah: cả hai trả lời body này.
Lời gọi đã gửi một SMS. Cùng body này có trong docs/api/client-auth-and-address-book.md §1
(locate-authentication-service@6757aa4, tài liệu thay đổi lần cuối 2026-07-27).
Lỗi
| HTTP | Body | Khi nào |
|---|---|---|
400 | {"name":"Bad Request","message":"Phone number cannot be blank.","code":0,"status":400,"type":"yii\\web\\BadRequestHttpException"} | contact_phone thiếu hoặc trống. |
400 | Cùng shape, với message là Last Name cannot be blank., First Name cannot be blank. hoặc Email Address cannot be blank.. | Không client active nào có phone, và field đăng ký trống đầu tiên (theo thứ tự đó) là field được nêu tên. Một lời gọi đăng nhập trần cho phone không tồn tại nhận Last Name cannot be blank.. |
500 | {"status":500,"message":"Internal server error"} | Lỗi không mong đợi, ví dụ tên dài hơn 100 ký tự khi đăng ký, lỗi database, hoặc SMS gateway không trả HTTP response. Chưa capture. Ngoài production, message là text lỗi gốc và có thêm field stack. |
Body Phone number cannot be blank. được capture trên dev vào 2026-10-01 (lời gọi không có tham số
nào). Body Last Name cannot be blank. lấy từ docs/api/client-auth-and-address-book.md
§1 (2026-07-27): lần chạy capture không gọi với số điện thoại nào, vì một lời gọi có thể gửi SMS.
Thử ngay
Một lời gọi thành công gửi SMS thật trừ khi số thuộc về một tài khoản test-mode. Hãy dùng một số test-mode mà bạn kiểm soát. Số nào là test-mode trên dev: Chưa được tài liệu hóa.
export AUTH_BASE_URL=https://dev.authentication.locate.sa
export PHONE='<test-mode-phone>' # placeholder: replace it with a test-mode number
curl -s -G "$AUTH_BASE_URL/api/v1/clients/reset-otp" --data-urlencode "contact_phone=$PHONE"
Kỳ vọng: 200 và {"status":true}. Không có phone bạn nhận 400 như ở mục Lỗi.
Khác biệt so với Shukah
| Khía cạnh | Shukah | Locate | Tác động lên app |
|---|---|---|---|
| URL | GET $SHUKAH_BASE_URL/maeda/api/reset-otp | GET $AUTH_BASE_URL/api/v1/clients/reset-otp. Locate bỏ qua key, mọi query key lạ khác và mọi header. | Đổi base URL và path. App có thể tiếp tục gửi key và các header của nó. |
| Lỗi thiếu phone | {"name":"Bad Request","message":"حقل contact phone مطلوب.","code":0,"status":400,"type":"Illuminate\\Validation\\ValidationException"} (capture trên dev, 2026-10-01): message tiếng Ả Rập và type của Laravel. | {"name":"Bad Request","message":"Phone number cannot be blank.","code":0,"status":400,"type":"yii\\web\\BadRequestHttpException"} (capture trên dev, 2026-10-01): message tiếng Anh và type của Yii. | Các key giống nhau, text và type khác nhau. Đừng so khớp theo text này. |
| Phone không tồn tại, không có field đăng ký | 400 với Last Name cannot be blank. trong body kiểu Yii, trong capture tháng 7 năm 2026 lưu ở draft doc của auth service (xem Sources ở mục Chi tiết). Text của Shukah hiện nay: Chưa được tài liệu hóa. | 400 với Last Name cannot be blank. trong body kiểu Yii. | Tín hiệu đăng ký tiếp tục chạy nếu hiện nay Shukah vẫn gửi cùng text. Hãy xác nhận text hiện tại của Shukah trước khi dựa vào nó. |
| Body thành công | {"status":true} (capture trên dev, 2026-10-02). | {"status":true} (capture trên dev, 2026-10-02). | Không có. |
GET /api/v1/clients/verify-otp
GET /maeda/api/verify-otp✅ Đã thay thế- 🔴 Chưa chuyển đăng nhập sang Locate: đăng nhập Locate chỉ trả về JWT, mà các route points của order-v2 hiện từ chối JWT này.
- 🟡 Body 400 khi sai mã là {status,message}; Shukah thêm name/code/type (Laravel); app chỉ đọc message.
- 🟡 fcm_token và platform bị bỏ qua trên Locate; chưa thấy Shukah xử lý chúng thế nào trong response của nó.
- 🟡 Gọi thành công trên dev (2026-10-02): cùng {access_token,data} và hồ sơ 13 key ở cả hai phía; session cũ vẫn hợp lệ.
Trả về access_token và profile trong data, trong đó token là một JWT của Locate (body thành công của Shukah có cùng shape, capture 2026-10-02), còn fcm_token và platform bị bỏ qua.
Request
Không cần token.
| Field | Vị trí | Kiểu | Bắt buộc | Ràng buộc | Mô tả |
|---|---|---|---|---|---|
contact_phone | query | string | có | Khoảng trắng bị bỏ. Nó được so khớp với phone mà mã được gửi tới, có hoặc không có dấu + ở đầu. Thiếu cho cùng 400 như mã sai. | Phone đã dùng trong reset-otp. |
code | query | string | có | Được so sánh dạng text với mã 4 chữ số, nên số 0 ở đầu có ý nghĩa. | Mã từ SMS. |
language, platform, fcm_token và mọi query key khác | query | string | không | Bị bỏ qua. | — |
Response
200 kèm token và hồ sơ:
| Field | Kiểu | Nullable | Định dạng / ràng buộc | Mô tả |
|---|---|---|---|---|
access_token | string | không | Một JWT Locate: ba phần ngăn cách bằng dấu chấm, ký bằng HS256. Nó không bao giờ hết hạn. | Gửi nó dưới dạng Authorization: Bearer <access_token> trên mọi lời gọi sau. |
data | object | không | Hồ sơ của client: 13 field được liệt kê ở Verify token. | Client đang đăng nhập. |
{
"access_token": "<locate-jwt>",
"data": {
"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
}
}
Shape từ docs/api/client-auth-and-address-book.md §2 (locate-authentication-service@6757aa4, tài liệu thay đổi lần cuối 2026-07-27), và đã gọi live trên dev ngày 2026-10-02 (200, cùng key và giá trị như trên; xem Khác biệt so với Shukah). Giá trị token đã bị che lúc capture nên loại token không được ghi lại ở đây: nhận định JWT lấy từ code. Các giá trị hồ sơ là giá trị minh họa dùng trên toàn site.
Lỗi
| HTTP | Body | Khi nào |
|---|---|---|
400 | {"status":400,"message":"Sorry, but your verification code is incorrect"} | Không có mã nào chưa dùng, chưa hết hạn khớp với phone và mã (sai, hết hạn, đã dùng, hoặc thiếu tham số), hoặc không client active nào có phone. Một message duy nhất bao phủ tất cả, có chủ đích, để lời gọi không tiết lộ phone nào có tài khoản. |
400 | Cùng shape, với text lỗi gốc làm message. | Bất kỳ lỗi nào khác bên trong handler, ví dụ lỗi database hoặc ghi token thất bại. Chưa capture. |
Body đầu tiên được capture trên dev vào 2026-10-01 (lời gọi không có tham số nào), và lại vào 2026-10-02 với một mã sai. Nó không có shape Yii, khác với các lỗi của reset-otp.
Thử ngay
Một lời gọi thành công dùng hết mã và tạo một token Locate mới. Các token trước đó vẫn hợp lệ.
export AUTH_BASE_URL=https://dev.authentication.locate.sa
export PHONE='<test-mode-phone>' # the number you used in reset-otp
export CODE='<4-digit-code>' # 2022 for a test-mode account
curl -s -G "$AUTH_BASE_URL/api/v1/clients/verify-otp" --data-urlencode "contact_phone=$PHONE" --data-urlencode "code=$CODE"
Kỳ vọng: 200 kèm access_token và data. Mã sai hoặc hết hạn cho 400 như ở mục Lỗi.
Khác biệt so với Shukah
| Khía cạnh | Shukah | Locate | Tác động lên app |
|---|---|---|---|
| URL | GET $SHUKAH_BASE_URL/maeda/api/verify-otp | GET $AUTH_BASE_URL/api/v1/clients/verify-otp | Đổi base URL và path. |
| Token | Một token opaque của Shukah. | Một JWT Locate trong access_token. | Mọi lời gọi /api/v1/clients/* sau đó đều cần JWT này, nhưng các route points của order-v2 hiện từ chối nó, nên tạm thời vẫn đăng nhập trên Shukah. |
| Query key thêm | App cũng gửi fcm_token (push token của nó, bỏ qua khi app không lấy được) và platform (Android, iOS hoặc Huawei). Shukah làm gì với chúng: Chưa được tài liệu hóa. | Locate chỉ đọc contact_phone và code, nên bỏ fcm_token và platform. | Lời gọi vẫn chạy. Nếu Shukah có dùng một trong hai giá trị đó, nó sẽ mất trên Locate: hãy xác nhận Shukah đã làm gì với chúng. |
| Lỗi mã sai | {"name":"Bad Request","message":"Sorry, but your verification code is incorrect","code":0,"status":400,"type":"Symfony\\Component\\HttpKernel\\Exception\\HttpException"} (capture trên dev, 2026-10-02). Capture tháng 7 năm 2026 lưu ở draft doc của auth service có cùng message với type kiểu Yii yii\\web\\BadRequestHttpException (xem Sources ở mục Chi tiết). | {"status":400,"message":"Sorry, but your verification code is incorrect"} (capture trên dev, 2026-10-01 và 2026-10-02). | message và status có ở cả hai, với cùng text. name, code và type không còn, nên đừng đọc chúng. |
| Body thành công | 200 {access_token, data}, với data là hồ sơ 13 field (capture trên dev, 2026-10-02, cùng phone và mã với Locate). | 200 {access_token, data}, với data là cùng 13 field và giá trị bằng nhau (capture trên dev, 2026-10-02). | Shape và hồ sơ giống nhau nên phần parse của app không cần đổi. Giá trị access_token đã bị che lúc capture, nên loại token ở mỗi phía không được ghi lại ở đây (dòng Token lấy từ code). Một lần đăng nhập mới không kết thúc các session trước đó ở cả hai phía. |
Lưu ý
- Lời gọi reset-otp thứ hai trong vòng 3 phút không gửi SMS mới nhưng vẫn trả
{"status":true}, nêntruekhông có nghĩa là SMS đã được gửi. - Client không active (suspended, blocked, pending hoặc expired) được coi như phone không tồn tại, nên bị đưa tới đăng ký.
- Body lỗi khác nhau: lỗi của reset-otp có shape Yii (
name,message,code,status,type), lỗi của verify-otp chỉ cóstatusvàmessage.
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).
Quy tắc OTP
| Quy tắc | Sự kiện | Nguồn |
|---|---|---|
| Độ dài | 4 ký tự, chữ số thập phân, có thể bắt đầu bằng 0: Math.random().toString().slice(2, 6). | src/services/client-otp.js:7-8 |
| Hiệu lực | 3 phút kể từ lúc tạo. Verify cần expired_at là bây giờ hoặc muộn hơn. | src/services/client-otp.js:5,37; src/controllers/client/verify-otp/handler.js:27 |
| Dùng một lần | Verify set verified: true và verified_at, và mã đã dùng không còn khớp. | src/controllers/client/verify-otp/handler.js:26,38-41 |
| Gửi lại | Nếu có một mã chưa verify, chưa hết hạn cho phone đã lưu, không có gì được tạo hay gửi và câu trả lời vẫn là {"status":true}. | src/services/client-otp.js:20-23 |
| Số lần thử và rate limit | Không có: không có bộ đếm, lockout hay rate limiter. | docs/plans/2026-07-28-k6-load-test-suite.md:9 |
Mã cố định 2022, tài khoản test-mode | account_mode TEST cho 2022 và không có SMS. | src/services/client-otp.js:7-8,40-42; src/shared/constant/user.js:27-30 |
Mã cố định 2022, hai phone | Một phone đã lưu đúng là +966500000000 hoặc 966500000000 nhận 2022 ở mọi chế độ, và SMS vẫn được gửi trừ khi tài khoản là test-mode. Thêm vào 2026-07-14 (commit 7aad3af). Phone mẫu dùng trên trang này là một trong hai số đó. | src/services/client-otp.js:25-27 |
| Cách đặt test mode | account_mode mặc định là STANDARD. Không API nào set nó: nó được ghi thẳng vào database. | src/models/client.js:87-90; docs/plans/2026-07-28-k6-load-test-suite.md:18 |
| Số test | Không có số nào được tài liệu hóa cho dev. Bộ load-test dành riêng các phone test-mode gồm 9665000 cộng sáu chữ số (client id từ 9,000,000,000), được seed thẳng vào database. Các tài khoản như vậy có tồn tại trên database dev dùng chung hay không: Chưa được tài liệu hóa. | docs/plans/2026-07-28-k6-load-test-suite.md:19,33-40; k6/seed/seed-test-clients.js:23-24,40-58; k6/README.md:13-23 |
| Tra cứu tài khoản | reset-otp, verify-otp và client lookup tìm client theo phone đã lưu với + tùy chọn, chưa bị xóa, status ACTIVE, client_id mới nhất trước, không cache. Một client không active là vô hình với cả ba. | src/controllers/client/reset-otp/handler.js:24-30; src/shared/helpers/filter.js:1-4; docs/api/client-auth-and-address-book.md:52-54 |
| SMS | POST ${SMS_API}/v1/messages với Bearer ${SMS_TOKEN}. Nội dung là tiếng Ả Rập bất kể language nói gì. Người nhận 0XXXXXXXXX thành 966XXXXXXXXX, nếu không thì phone đã lưu được gửi dưới dạng số. Response của gateway được lưu trên dòng OTP. | src/services/client-otp.js:11-14,30,41; src/shared/helpers/sms.js:4-24 |
| Logging | Mã được sinh ra được ghi vào server log. | src/services/client-otp.js:29 |
Đăng ký qua reset-otp
Khi không client active nào có phone và ba field bắt buộc đều có mặt, handler tạo
một client rồi cấp mã (src/controllers/client/reset-otp/handler.js:48-73).
- Client mới nhận
client_id = 1,000,000,000 + counter,source_systemvàchange_originđặt làlocate,from_locate: true, các Shukah id rỗng,client_language, và các mặc định của model: statusACTIVE, modeSTANDARD, push bật, offers tắt (src/controllers/client/reset-otp/handler.js:51-69,src/models/counter.js:7,17-24,src/models/client.js:83-90,110-111). Các tên được lưu đã trim và model cho phép 100 ký tự cho mỗi tên (src/models/client.js:45-52). - Sau khi mã được cấp, service đẩy client sang Shukah:
POST {SHUKAH_HOST}/maeda/client, một upsert theo phone. Nó là best effort và được await, và nó lưu id mà Shukah trả về làmshukah_id. Nó không làm gì khi outbound sync tắt hoặc host hay admin token chưa được set (src/controllers/client/reset-otp/handler.js:71-73,src/services/shukah-sync.js:21-30,184-234). - Một phone không active hành xử như phone không tồn tại. Model không khai báo unique index trên
phone, nên với đủ các field đăng ký handler sẽ insert một client thứ hai cùng phone, và client đó sẽ thắng ở lookup (client_idcao nhất). Đọc code, chưa test live. Điều này có chủ đích hay không: Chưa được tài liệu hóa (src/models/client.js:147-156). - Request chờ SMS gateway (
await sendMessage, không có timeout). Một câu trả lời lỗi HTTP của gateway được lưu trên dòng OTP và bị bỏ qua, nên câu trả lời vẫn là{"status":true}. Một lỗi gateway không có HTTP response sẽ throw trong catch và cho500, sau khi dòng OTP đã được tạo, nên một lần thử lại trong 3 phút không gửi gì (src/shared/helpers/sms.js:18-23; đọc code).
Verify mã
Thứ tự thao tác (src/controllers/client/verify-otp/handler.js:20-46): tìm mã, tìm
client active, mint JWT, insert dòng token_bridge, đẩy JWT sang Shukah, đánh dấu mã đã dùng,
trả lời. Mã chỉ bị tiêu thụ sau khi việc ghi token và push đã hoàn tất.
- Shape thành công
{access_token, data}được chốt bằng một unit test (src/controllers/client/__tests__/verify-otp.test.js:77-94). - Mỗi lần thành công mint một token mới. Các token trước vẫn hợp lệ vì không gì thu hồi chúng
(
src/shared/helpers/jwt.js:4-10). Đã xác nhận live trên dev ngày 2026-10-02: sau một lần đăng nhập mới ở mỗi phía, hai token phát hành trước đó vẫn dùng được. - Dòng
token_bridgecósource_system: 'merchant', và token raw chỉ khiNODE_ENVkhông phảiproduction(src/services/merchant-token-bridge.js:6-16). - Push sang Shukah là
POST {SHUKAH_HOST}/maeda/client/add-tokenvới{access_token, client_id: <shukah_id>}, best effort và được await. Nó không làm gì khi client không cóshukah_idhoặc sync tắt (src/services/shukah-sync.js:13-75). Đây là cách Shukah biết các JWT Locate (docs/plans/2026-07-16-shukah-token-outbound-push.md:10-28). Push có được bật trên dev hay không, và API Shukah nào chấp nhận JWT Locate sau đó: Chưa được tài liệu hóa. - Bất kỳ exception nào trong handler (lỗi database, ghi token thất bại, token hash trùng) đều được
catch và trả
400với message gốc (src/controllers/client/verify-otp/handler.js:47-53; đọc code). - Hai lời gọi verify cho cùng một client trong cùng một giây sẽ mint một JWT giống hệt từng byte và đụng
index
token_hashunique, cho400đó. Đọc code và một offline determinism check, chưa tái hiện: Chưa được tài liệu hóa là trên dev có xảy ra hay không. - Không có bộ đếm số lần thử: mã 4 chữ số có thể được thử lặp lại trong 3 phút của nó.
Khi tài liệu và code mâu thuẫn (code thắng)
- Sơ đồ flow kết thúc của tài liệu service nói verify-otp trả
{id, token}(docs/api/client-auth-and-address-book.md:542). Code, và chính tài liệu đó ở dòng 110-131, nói{access_token, data}. - Tài liệu service nói verify "deletes all OTPs of the phone"
(
docs/api/client-auth-and-address-book.md:135). Code chỉ đánh dấu mã khớp là đã dùng (commita5a5315, 2026-07-17). - Một design doc nói mã sai cho
400kiểu Yii (docs/plans/2026-07-06-shukah-auth-api-local.md:101-104). Code và tài liệu API cho{"status":400,"message":...}. - Mã test-mode là
2022trong code. Unit test (src/services/__tests__/client-otp.test.js:6) và một design doc (docs/plans/2026-07-06-shukah-auth-api-local.md:155,213) nói8888, và unit test đó fail với code. - Một design doc nói verify-otp set
last_login_atvà lưu token trên client (docs/plans/2026-07-06-shukah-auth-api-local.md:106-108). Handler không làm cả hai. - Design doc 2026-07-06 và flow chart nháp viết path là
/maeda/api/*. Từ commitd837492(2026-07-10) mọi route client nằm dướiPREFIX_API_V1/clients, và host auth trả404cho/maeda/*. - 2026-10-02: trang này nói một reset-otp thành công chưa được capture. Vào 2026-10-02 cả Shukah và
Locate đều trả lời
200 {"status":true}cho một phone đã biết. - 2026-10-02: trang này không có body Shukah hiện tại cho một mã verify-otp sai. Shukah dev trả lời
body Laravel với một
typecủa Symfony, hiển thị ở mục Khác biệt so với Shukah. - 2026-10-02: trang này nói một verify-otp thành công chưa được capture live, và body thành công của Shukah chưa được tài liệu hóa. Vào 2026-10-02 cả Shukah và Locate đều trả lời
200 {access_token, data}với cùng hồ sơ 13 key và giá trị bằng nhau, và các token trước đó vẫn dùng được sau lần đăng nhập mới.
Sources
locate-authentication-service@6757aa4:src/routes/client.js:11-12,src/controllers/client/reset-otp/handler.js:11-30,34-74,src/controllers/client/verify-otp/handler.js:14-53,src/controllers/client/__tests__/verify-otp.test.js:77-94,src/services/client-otp.js:5-45,src/shared/helpers/sms.js:4-24,src/shared/helpers/filter.js:1-4,src/sync/lib/casts.js:2-18,35-41,src/shared/errors/yii-bad-request-error.js:12-25,src/middlewares/error-handler.js:10-35,src/shared/constant/user.js:14,27-30,src/shared/helpers/jwt.js:4-10,src/services/merchant-token-bridge.js:6-16,src/services/shukah-sync.js:13-75,184-234,src/models/client.js:45-52,83-90,110-111,147-156,src/models/counter.js:7,17-24,src/services/__tests__/client-otp.test.js:6,k6/seed/seed-test-clients.js:23-24,40-58,k6/README.md:13-23- Tài liệu của
locate-authentication-service@6757aa4:docs/api/client-auth-and-address-book.md§1 và §2 (dòng 45-146, 542; thay đổi lần cuối 2026-07-27),docs/draft/api-auth-draft.md:5-31,90,109-116(2026-07-07),docs/draft/flow-chart-shukah2locate.md:60,140-142(2026-07-08),docs/plans/2026-07-06-shukah-auth-api-local.md:8-11,26,101-108,155,213,237-239,docs/plans/2026-07-16-shukah-token-outbound-push.md:10-28,docs/plans/2026-07-28-k6-load-test-suite.md:9,18-19,33-40 - Snapshot mobile
mobile/locate-client@e960c3a(branchdevelopment, 2026-02-26; code của chính app, không phải Locate):src/entities/user/model/api/services/auth.service.ts:22-31,45-54,65-74,src/entities/user/model/api/dto/auth.dto.ts:43-88,103-122,src/shared/lib/helpers/phoneNumber/getClearPhoneNumber.ts:1-3,src/shared/lib/helpers/phone/getPlatform.ts:4-14,src/features/SignInForm/model/services/enterPhoneAuth.ts:48,53-67,src/shared/api/reqError.ts:125-127,src/features/RegisterNewUserForm/model/services/registerNewUser.ts:105,src/entities/user/model/services/sendCode/sendCode.ts:28-35,src/features/VerificationCodeForm/model/services/sendCode.ts:46-57 - Capture dev, 2026-10-02 (parity audit):
reset-otp--shukah,reset-otp--locate,reset-otp-missing--shukah,reset-otp-missing--locate,verify-otp-wrong--shukah,verify-otp-wrong--locate - Capture dev, 2026-10-01:
probe-locate-reset-otp,probe-locate-verify-otp,probe-shukah-reset-otp,probe-auth-host-maeda-prefix