POST /users/login_once.json
快速結論
- 狀態:已補 legacy parity guard / fallback 與 PHPUnit;已做 staging happy / consumed / DB side effect 實測,完整 staging full diff 待補。
- 正式機 access log 統計見
API 路由與遷移/API Migration/pro360_api_82/api_migration/users.md:POST /users/login_once.json 63663 筆 200,OPTIONS 37191 筆。
- 這支用
security_hashes 建立一次性 session;會寫 api_sessions、security_hash_use_logs、user_logins。
- 2026-06-29 已補 active user guard、hash consumed guard、nested
User.device_id fallback;只改 Users::login_once(),未改 shared SecurityHash model。
Legacy 對照
| 檔案 | 行號 | 行為 |
|---|
/Users/mattsu/Documents/Site/get-lancer-php56/app/Controller/UsersController.php | 2417-2435 | 先找 SecurityHash.model = Session,找不到再找 User;找不到回 error=2 |
| 同上 | 2438-2445 | users.id = user_id AND is_active = 1,不符合回 error=2 |
| 同上 | 2446-2464 | Session hash 14 天前或已 fulfilled 視為 consumed;已 fulfilled 的 ibonHyperlinkLogin / OrderLoginUrl 也 consumed |
| 同上 | 2466-2468 | device id 優先 User.device_id,再 fallback flat device_id,最後 my-device-id |
| 同上 | 2470-2495 | 建 session、fulfill Session 或 count User click、寫 use log、寫 channel 2 user login |
New 對照
| 檔案 | 行號 | 行為 |
|---|
Endpoint/V1/Users.php | 478-529 | Users::login_once() 主流程 |
Endpoint/V1/Users.php | 492-499 | active user guard 與 hash consumed guard |
Endpoint/V1/Users.php | 501-524 | 建 session、fulfill / count click、寫 use log、寫 channel 2 user login |
Endpoint/V1/Users.php | 531-567 | login_once 專用 legacy helper:active user、consumed hash、device id fallback |
2026-06-29 差異清單
| 項目 | Legacy | New 目前 | 判斷 |
|---|
| active user guard | users.id = user_id AND is_active = 1,不符合回 invalid hash | 已補 | 已對齊 |
| Session hash 14 天 / fulfilled guard | Session created <= 14 days ago 或 fulfilled 回 consumed | 已補 | 已對齊 |
| fulfilled ibon / order scene | fulfilled 且 scene 是 ibonHyperlinkLogin / OrderLoginUrl 回 consumed | 已補 | 已對齊 |
| device id fallback | User.device_id -> flat device_id -> my-device-id | 已補 | 已對齊 |
測試結果
docker exec -w /project-data cd63f9147e8d php -l Endpoint/V1/Users.php
docker exec -w /project-data cd63f9147e8d php -l tests/UsersLoginOnceTest.php
docker exec -w /project-data cd63f9147e8d vendor/bin/phpunit tests/UsersLoginOnceTest.php
OK (5 tests, 50 assertions)
docker exec -w /project-data cd63f9147e8d vendor/bin/phpunit tests/UsersForgotPasswordTest.php
OK (3 tests, 39 assertions)
docker exec -w /project-data cd63f9147e8d vendor/bin/phpunit tests/UsersSignUpTest.php
OK (11 tests, 129 assertions)
Staging 實測
2026-06-29 staging 已用 hash = 4f48b7e16e1e8c1420e93be77ffc3bab、user_id = 15801 驗證:
- 第一次呼叫回
{"error":0,"session_id":"6h9rmece3ac0pgemn5h4r53sgm"}。
- 第二次同 hash 回
{"error":1,"message":"hash was consumed"}。
- DB 確認
security_hashes.is_fulfilled = 1、api_sessions 建立、security_hash_use_logs 寫入、user_logins.channel = 2。
UsersLoginOnceTest.php 覆蓋:
- 成功建立 session,consume Session hash,寫
security_hash_use_logs / user_logins,並使用 nested User.device_id。
- inactive user 回 invalid hash 且不建立 session。
- expired Session hash 回 consumed 且不建立 session。
- fulfilled regular Session hash 回 consumed 且不建立 session。
- fulfilled
ibonHyperlinkLogin / OrderLoginUrl 回 consumed 且不建立 session。
待確認
- 尚未做完整 staging full diff;目前只驗證 happy path、二次 consumed 與主要 DB side effect。
SecurityHash::getByHash('User') 的時間條件 new 是 created > threshold,legacy 是 created >= threshold;14 天邊界差異極小,暫未改動。