2026/10/30 更新:公司訂單 API 已串接,改由新會員註冊觸發同步。明細仍需可信會員授權綁定。完整公司同步與商品配對文件;下方早期公司未接通的描述以此更新為準。

← 手機會員登入

有間 手機會員與登入 API

已上線 v1.0 · 2026 年 10 月 30 日 · 主機 youroom.store

本文件的 /api/auth 端點已實作並接上 PostgreSQL。原本的公司訂單 /v1 契約仍是草案,尚未接通 qi-shoppy。購物、配送與款項介面仍沿用瀏覽器測試紀錄。

登入與註冊流程

  1. 使用台灣手機號碼註冊,填寫稱呼及 15–128 字元密碼。接受 09、+8869 或 008869 格式,資料庫統一保存為 E.164,例如 +8869xxxxxxxx。
  2. 註冊成功即建立伺服器會員與會話,回應一次性顯示的帳戶救援碼。密碼以獨立鹽值 scrypt 雜湊保存,救援碼以伺服器密鑰 HMAC 保存。
  3. 瀏覽器保存 HttpOnly Secure SameSite=Lax 的 __Host-youroom_session Cookie。JavaScript 不讀取會話 Cookie,也不將密碼、會話 Token 或救援碼放進 localStorage。
  4. 重新載入時使用 GET /api/auth/session 還原登入。預設最長 12 小時,保持登入最長 14 天;連續 7 天未使用也須重新登入。
  5. 忘記密碼時使用手機加救援碼重設。成功後所有舊會話撤銷、舊救援碼失效,系統顯示新的救援碼,再使用新密碼登入。若連救援碼也遺失,目前沒有自動替代驗證管道。

目前沒有簡訊供應商,phoneVerified 為 false。註冊手機作為登入名稱,不等於已證明手機持有人身分,不能據此提供公司後台訂單。啟用簡訊或其他可信身分綁定後才能開放來源訂單查詢。

已實作端點

方法與路徑內容驗證
GET /api/auth/config密碼與註冊能力,目前簡訊驗證停用。公開
POST /api/auth/registerphone name password remember同來源 JSON
POST /api/auth/loginphone password remember同來源 JSON
GET /api/auth/sessionauthenticated、目前會員、csrfToken、expiresAt;訪客為 authenticated:false。Cookie 自動帶入
POST /api/auth/logout撤銷目前會話並清除 Cookie。Cookie 與 CSRF
POST /api/auth/recoverphone recoveryCode password;回應新救援碼,需重新登入。同來源 JSON 與救援碼
POST /api/auth/passwordcurrentPassword password;其他會話撤銷,本裝置取得新會話。Cookie 與 CSRF
POST /api/auth/recovery-codecurrentPassword;重新產生救援碼,舊碼失效。Cookie 與 CSRF
GET /api/me/order-identity伺服器從目前會員取出 memberId、phoneE164、phoneVerified。忽略客戶端另外提供的 phone。Cookie
GET /api/me/company-orders目前回 503 integration_not_connected,沒有公司訂單資料。Cookie
GET /api/health驗證認證資料庫連線,另明確回 ordersBackendConnected:false。公開,不回傳會員資料

呼叫方式

瀏覽器使用同網域請求與 credentials:same-origin。POST 要求 Origin:https://youroom.store 與 Content-Type:application/json。登入後的敏感操作另帶入從 session 回應取得的 X-CSRF-Token。沒有開放跨來源 CORS,也沒有提供可交給前端的公司系統服務密鑰。

// 在 youroom.store 頁面內
const state = await fetch('/api/auth/session', {
  credentials: 'same-origin', cache: 'no-store'
}).then(r => r.json());

// 會員身分由 Cookie 決定,不傳入查詢目標的手機號碼
const identity = await fetch('/api/me/order-identity', {
  credentials: 'same-origin', cache: 'no-store'
}).then(r => r.json());

// 登出
await fetch('/api/auth/logout', {
  method: 'POST', credentials: 'same-origin',
  headers: {'Content-Type':'application/json', 'X-CSRF-Token': state.csrfToken},
  body: '{}'
});

會員識別與未來訂單串接

正式會員主鍵為伺服器產生的 UUID,手機有唯一索引。之後後端應依已驗證的會員身分取得 phoneE164,再呼叫公司 API;不要讓瀏覽器以任意手機號碼取得訂單。來源會員與商城 UUID 應有明確的綁定與稽核,來源訂單需以唯一鍵避免重複匯入。

為相容既有瀏覽器測試帳本,前端暫用「會員 [email protected]」作為內部分區鍵。這不是會員電子信箱,不是 API 的會員識別,也不會拿來寄信。舊暱稱或電子信箱登入資料不會自動合併到新手機會員;公司訂單及舊瀏覽器資料沒有自動移轉。

錯誤與保護

錯誤回應為 {error,message}。400 輸入不正確、401 尚未登入或憑證錯誤、403 來源或 CSRF 不符、409 無法註冊、429 嘗試過多、503 服務忙碌或公司串接尚未完成。登入錯誤不區分手機不存在或密碼不正確。

登入同手機每 15 分鐘最多 8 次嘗試;註冊每來源每小時 10 次;救援同手機每小時 5 次;安全設定每會員每小時 5 次。另有來源整體頻率限制。客戶端 IP 只在 Caddy 回源 IP 屬 Cloudflare 已知範圍時才信任 CF 訪客標頭;直接連來源主機不能用自填標頭冒充其他訪客。

認證端點與回應禁止快取,資料庫僅監聽本機。服務使用獨立非 root 系統帳號與受限資料庫角色。會話只保存 Token 雜湊。救援碼與密碼不寫入稽核內容,手機不放 URL 查詢參數。所有測試使用獨立臨時資料庫,測試資料不納入正式會員。

原公司訂單 API 草案 · OWASP 認證指引 · Node.js 密碼學 API

會員本機訂單雲端保存

GET /api/me/order-archives:依會員會話讀取自己的保存紀錄,每頁50筆、nextCursor接續;可換裝置取得完整訂單快照、收貨門市/地址、配送情境歷程。POST同路徑要求會員Cookie、同源JSON、X-CSRF-Token。可傳單筆{kind,expectedRevision,snapshot,messages},或{archives:[最多10筆]};批次回傳results逐筆結果。

kind只接受local、test、preview,不能匯入company。以登入會員ID+類型+訂單編號唯一保存;snapshot含id、有效createdAt、totalMinor或amountMinor、商品與配送資料,單筆上限256KiB。訂單金額及日期建檔後不可變更;相同快照重送回原版本,實際變更以expectedRevision作版本檢查,過期回409。每次變更保存歷史版本。server會強制clientSupplied與simulation,實際收款為0,不建立外部交易。

依本機訂單建立或沿用已保存的生成對話,匯入的舊對話保留原文並標記非真人、未發送,不覆寫原訊息。會員隔離由RLS保護;銀行與憑證欄位不隨訂單上傳。原裝置必須登入並開啟網站才能上傳尚在本機的資料,離線失敗會保留本機並重試;版本衝突不自動覆蓋,可保存本機備份後讀雲端版。

管理員更正登入手機

若登入手機已由管理員更正,請使用新手機號碼與原密碼登入。原裝置須重新登入;會員ID、既有訂單、配送與對話保留。手機修改不代表完成簡訊驗證,也不會改動公司後台的手機資料。