依 autoflow-agent workspace v2 設計把 PRD / 設計 / 架構 / 交付類 共享文件從個人層 .autoflow/(ignored)搬到 docs/autoflow/(進 git), 讓團隊可共享產品與架構文件,個人層只留 progress / review / testing 等 per-branch 筆記。 - 02-prd/ 21 個檔(PRD、features、market-analysis 等) - 03-design/ 18 個檔(design-spec、wireframes、flows 等) - 04-architecture/ 31 個檔(TDD、design-doc、ADR×14、API 規格等) - 07-delivery/ 3 個檔(project-summary、phase-0.6-handover、stage-deployment-setup) 合計 73 檔。原檔已從 .autoflow/ 移除(migration 工具執行 git mv, 但因 .autoflow/ 在 .gitignore 中、git 將此操作視為新增、無 rename history)。
430 lines
17 KiB
Markdown
430 lines
17 KiB
Markdown
# 登入 / 註冊流程 — visionA Cloud
|
||
|
||
> **Phase 0 雛形範圍**:僅做低保真 stub(表單 + 按鈕 + 路由),**不接後端認證**。使用者送出登入 / 註冊表單後直接跳轉到目標頁即可。
|
||
>
|
||
> 完整 auth 流程(OAuth、2FA、密碼重設、Email 驗證、ToS / Privacy Policy、rate limit 等)**Phase 1+ 再設計**。
|
||
>
|
||
> 本檔記錄 Phase 0 雛形規格 + Phase 1+ TODO。
|
||
|
||
---
|
||
|
||
## 1. 雛形範圍決策
|
||
|
||
| 項目 | Phase 0 | Phase 1 TODO |
|
||
|------|---------|-------------|
|
||
| 登入表單 UI | ✅ 做 | 完整化 |
|
||
| 註冊表單 UI | ✅ 做 | 完整化 |
|
||
| 登入 API 串接 | ❌ 不做(直接跳轉)| 做 |
|
||
| 註冊 API 串接 | ❌ 不做 | 做 |
|
||
| OAuth(Google / GitHub)| ❌ 不做 | 做 |
|
||
| 密碼強度驗證 | ❌ 不做 | 做 |
|
||
| Email 驗證 | ❌ 不做 | 做 |
|
||
| 密碼重設 | ❌ 不做 | 做 |
|
||
| 2FA | ❌ 不做 | 做(Phase 2) |
|
||
| ToS / Privacy Policy 勾選 | ❌ 不做 | 做 |
|
||
| 社群登入 | ❌ 不做 | 做(Phase 2) |
|
||
| Rate Limit 提示 | ❌ 不做 | 做 |
|
||
| 帳號鎖定機制 | ❌ 不做 | 做 |
|
||
|
||
---
|
||
|
||
## 1.1 雛形 Auth 行為(三方一致)
|
||
|
||
Phase 0 **不接後端認證**,三方已對齊行為如下(對應 Reviewer C3):
|
||
|
||
| 項目 | Phase 0 行為 |
|
||
|------|-------------|
|
||
| 登入表單送出 | HTML5 驗證通過後 → `router.push('/')`,**不**打 API,**不**做身分驗證 |
|
||
| 註冊表單送出 | 同上 → `router.push('/')` |
|
||
| Session / Token | 不存(前端不發、不讀);重新整理依然可進任何頁面 |
|
||
| 登出按鈕 | 點擊 → `router.push('/login')`,不清 session(本來就沒存) |
|
||
| 受保護路由 | 雛形期所有 `/*` 路由對匿名使用者開放,**不做**導向 /login |
|
||
|
||
### 1.2 雛形 Banner(全域)
|
||
|
||
為了避免 demo 被誤認為正式版本,所有已登入的主畫面(`(main)/layout.tsx`)頂部顯示常駐 Banner:
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 🚧 雛形版本 · 登入僅為 UI 示意,未實作身分驗證;資料皆為假資料 │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**規格:**
|
||
- 位置:`Header` 正下方,`sticky top-14 z-30`(在 `NetworkErrorBanner z-40` 之下,以利網路錯誤優先)
|
||
- 樣式:`bg-amber-100 dark:bg-amber-900/40 text-amber-900 dark:text-amber-100 border-b border-amber-300`
|
||
- 文字:`text-xs font-medium text-center py-1.5`
|
||
- 圖示:`Construction` / `Wrench`(Lucide)`h-3.5 w-3.5`
|
||
- 可關閉?**不可關閉**(雛形階段要讓使用者持續意識到)
|
||
- `role="status"` + `aria-label="雛形版本提示"`
|
||
- 響應式:Mobile 文字改為「🚧 雛形版本(demo)」縮短版
|
||
|
||
### 1.3 i18n key(雛形 Banner)
|
||
|
||
```
|
||
prototype.banner.label → 雛形版本 · 登入僅為 UI 示意,未實作身分驗證;資料皆為假資料
|
||
prototype.banner.short → 雛形版本(demo)
|
||
prototype.banner.ariaLabel → 雛形版本提示
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 路由與 Layout
|
||
|
||
```
|
||
app/
|
||
├── (auth)/
|
||
│ ├── layout.tsx ← 無 Sidebar / Header 的獨立 layout
|
||
│ ├── login/page.tsx ← /login
|
||
│ └── register/page.tsx ← /register
|
||
├── (main)/
|
||
│ ├── layout.tsx ← 有 Sidebar / Header 的主 layout(既有)
|
||
│ ├── page.tsx ← /
|
||
│ ├── devices/...
|
||
│ └── ...
|
||
└── layout.tsx ← root layout(ThemeSync / LangSync / Toaster)
|
||
```
|
||
|
||
**`(auth)/layout.tsx` 規格:**
|
||
- 無 Sidebar、無 Header
|
||
- 全螢幕 `min-h-screen`,`bg-background`
|
||
- 內容置中:`flex items-center justify-center`
|
||
- 無 padding wrapper(頁面自己處理)
|
||
|
||
---
|
||
|
||
## 3. `/login` — 登入頁
|
||
|
||
### 3.1 視覺(Phase 0)
|
||
|
||
```
|
||
┌────────────────────────────────────────────────────────┐
|
||
│ │
|
||
│ │
|
||
│ [Logo] visionA Cloud │
|
||
│ Edge AI Platform │
|
||
│ │
|
||
│ ┌──────────────────────────────────────┐ │
|
||
│ │ Email │ │
|
||
│ │ ┌──────────────────────────────────┐ │ │
|
||
│ │ │ you@example.com │ │ │
|
||
│ │ └──────────────────────────────────┘ │ │
|
||
│ │ │ │
|
||
│ │ 密碼 │ │
|
||
│ │ ┌──────────────────────────────────┐ │ │
|
||
│ │ │ •••••••• │ │ │
|
||
│ │ └──────────────────────────────────┘ │ │
|
||
│ │ │ │
|
||
│ │ [ 登入 ] │ │
|
||
│ │ │ │
|
||
│ │ ──────── 還沒有帳號? ───────── │ │
|
||
│ │ │ │
|
||
│ │ [ 建立新帳號 ] │ │
|
||
│ └──────────────────────────────────────┘ │
|
||
│ │
|
||
│ 忘記密碼? | 語言:繁中 ▾ │
|
||
│ │
|
||
└────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 3.2 規格
|
||
|
||
- **容器**:`max-w-md w-full mx-auto px-4`
|
||
- **品牌區**:
|
||
- Logo:`h-12 w-12 rounded-lg`
|
||
- 產品名:`text-2xl font-bold`
|
||
- 副標題:`text-sm text-muted-foreground`
|
||
- 區塊間距:`space-y-2 mb-8`
|
||
- **Card**:既有 `Card` + `CardContent`,`py-8 px-6`
|
||
- **表單區塊**:`space-y-4`
|
||
- **Input**:既有 shadcn,`type="email"` / `type="password"` 搭配 `autoComplete`
|
||
- **登入按鈕**:`w-full` + `variant=default` + `size=default`
|
||
- **分隔線 + 建立帳號**:
|
||
- `Separator` + 中間文字(使用 Tailwind 的 flex center hack)
|
||
- 建立帳號 `Button variant=outline w-full`
|
||
- **底部**:
|
||
- 「忘記密碼?」:`Button variant=link size=sm`(disabled)
|
||
- 語言切換:輕量化 Select(僅 2 選項)
|
||
|
||
### 3.3 行為(Phase 0 簡化)
|
||
|
||
```tsx
|
||
async function handleLogin(e: FormEvent) {
|
||
e.preventDefault();
|
||
// Phase 0: 不接 API,直接跳 dashboard
|
||
// Phase 1: const res = await api.post('/auth/login', { email, password })
|
||
router.push('/');
|
||
}
|
||
```
|
||
|
||
- 表單用 HTML5 `required`(Phase 0)
|
||
- 無 loading state(Phase 1+)
|
||
- 無 error state(Phase 1+)
|
||
- 送出直接跳 `/`
|
||
|
||
### 3.4 鍵盤行為
|
||
|
||
- Tab 順序:Email → Password → 登入 → 建立新帳號 → 忘記密碼 → 語言
|
||
- Email 或 Password focused 時 Enter → 提交表單
|
||
- 「建立新帳號」:Enter / Space 導航到 `/register`
|
||
- 語言切換:Space 開啟 Select,Arrow 選擇,Enter 確認
|
||
|
||
### 3.5 響應式
|
||
|
||
- Mobile:`px-4` 確保左右留白;其他結構不變
|
||
- Tablet / Desktop:置中,`max-w-md` 限寬
|
||
|
||
### 3.6 i18n key
|
||
|
||
```
|
||
auth.login.title → 登入
|
||
auth.login.subtitle → 歡迎回到 visionA Cloud
|
||
auth.login.email → Email
|
||
auth.login.emailPlaceholder → you@example.com
|
||
auth.login.password → 密碼
|
||
auth.login.submit → 登入
|
||
auth.login.submitting → 登入中...
|
||
auth.login.forgotPassword → 忘記密碼?
|
||
auth.login.noAccount → 還沒有帳號?
|
||
auth.login.createAccount → 建立新帳號
|
||
```
|
||
|
||
---
|
||
|
||
## 4. `/register` — 註冊頁
|
||
|
||
### 4.1 視覺(Phase 0)
|
||
|
||
```
|
||
┌────────────────────────────────────────────────────────┐
|
||
│ │
|
||
│ [Logo] visionA Cloud │
|
||
│ Edge AI Platform │
|
||
│ │
|
||
│ ┌──────────────────────────────────────┐ │
|
||
│ │ 建立帳號 │ │
|
||
│ │ │ │
|
||
│ │ Email │ │
|
||
│ │ [____________________________________] │ │
|
||
│ │ │ │
|
||
│ │ 密碼 │ │
|
||
│ │ [____________________________________] │ │
|
||
│ │ │ │
|
||
│ │ 確認密碼 │ │
|
||
│ │ [____________________________________] │ │
|
||
│ │ │ │
|
||
│ │ [ 建立帳號 ] │ │
|
||
│ │ │ │
|
||
│ │ ──────── 已經有帳號? ────────── │ │
|
||
│ │ │ │
|
||
│ │ [ 登入 ] │ │
|
||
│ └──────────────────────────────────────┘ │
|
||
│ │
|
||
│ 語言:繁中 ▾ │
|
||
│ │
|
||
└────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 4.2 規格差異(相對 /login)
|
||
|
||
- 新增「確認密碼」欄位
|
||
- 「建立帳號」按鈕取代「登入」
|
||
- 底部連結改為「已經有帳號? → 登入」
|
||
|
||
### 4.3 驗證規則(Phase 0 最小化)
|
||
|
||
- Email:HTML5 `type="email" required`
|
||
- 密碼:`required`,**不驗證強度**
|
||
- 確認密碼:失去焦點時比對(client-side),不符則顯示紅框 + 下方文字
|
||
|
||
**簡單驗證範例:**
|
||
|
||
```tsx
|
||
const [confirmError, setConfirmError] = useState('');
|
||
|
||
function handleConfirmBlur() {
|
||
if (password && confirm && password !== confirm) {
|
||
setConfirmError(t('auth.register.passwordMismatch'));
|
||
} else {
|
||
setConfirmError('');
|
||
}
|
||
}
|
||
```
|
||
|
||
顯示:`<Input aria-invalid={!!confirmError}>` + `<p className="text-xs text-destructive">{confirmError}</p>`
|
||
|
||
### 4.4 行為(Phase 0 簡化)
|
||
|
||
```tsx
|
||
async function handleRegister(e: FormEvent) {
|
||
e.preventDefault();
|
||
if (password !== confirm) {
|
||
setConfirmError(t('auth.register.passwordMismatch'));
|
||
return;
|
||
}
|
||
// Phase 0: 不接 API,直接引導到 pairing
|
||
router.push('/devices/pair');
|
||
}
|
||
```
|
||
|
||
**註冊成功後的導航**:跳 `/devices/pair` 引導第一次配對(避免使用者看到空白 Dashboard)。
|
||
|
||
### 4.5 i18n key
|
||
|
||
```
|
||
auth.register.title → 建立帳號
|
||
auth.register.subtitle → 開始使用 visionA Cloud
|
||
auth.register.email → Email
|
||
auth.register.password → 密碼
|
||
auth.register.confirmPassword → 確認密碼
|
||
auth.register.passwordMismatch → 兩次輸入的密碼不一致
|
||
auth.register.submit → 建立帳號
|
||
auth.register.submitting → 建立中...
|
||
auth.register.hasAccount → 已經有帳號?
|
||
auth.register.signIn → 登入
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 登入狀態管理(Phase 0 雛形)
|
||
|
||
### 5.1 `useAuthStore`(Zustand)
|
||
|
||
**目的**:Phase 0 讓前端能在「未登入 / 已登入」兩個狀態間切換,即使後端沒接。
|
||
|
||
```typescript
|
||
interface AuthStore {
|
||
user: { email: string; displayName?: string } | null;
|
||
isAuthenticated: boolean;
|
||
login: (email: string) => void; // Phase 0: 只存 localStorage
|
||
logout: () => void;
|
||
}
|
||
```
|
||
|
||
**Phase 0 簡化實作**:
|
||
- `login(email)`:設 `user = { email }`、`isAuthenticated = true`、存 `localStorage`
|
||
- `logout()`:清空、跳 `/login`
|
||
- Hydration:頁面載入從 `localStorage` 恢復狀態
|
||
|
||
**Phase 1 升級**:改為真正的 JWT / Session cookie;`login` 會呼叫 API、存 token;`logout` 會呼叫 API 使 token 失效。
|
||
|
||
### 5.2 路由保護(Phase 0 Middleware)
|
||
|
||
**最簡版**:在 `(main)/layout.tsx` 或 Root 層加 hook:
|
||
|
||
```tsx
|
||
'use client';
|
||
function MainLayout({ children }) {
|
||
const { isAuthenticated } = useAuthStore();
|
||
const router = useRouter();
|
||
useEffect(() => {
|
||
if (!isAuthenticated) router.replace('/login');
|
||
}, [isAuthenticated]);
|
||
// ... 顯示 Sidebar + Header + children
|
||
}
|
||
```
|
||
|
||
**注意**:Phase 0 保護邏輯很弱(只是 client-side redirect),**不是真正的安全機制**。任何直接 API 請求都應該由後端驗證。
|
||
|
||
---
|
||
|
||
## 6. 登出流程
|
||
|
||
**觸發點**:
|
||
- Sidebar 底部 `UserMenu` → 「登出」
|
||
- `/account` 頁面(未來)
|
||
- Session 過期自動觸發(Phase 1+)
|
||
|
||
**行為(Phase 0)**:
|
||
1. `useAuthStore.logout()`
|
||
2. 清空 localStorage 的 `user`
|
||
3. 顯示 toast「已登出」
|
||
4. `router.push('/login')`
|
||
|
||
**Phase 1+**:
|
||
- 呼叫 `POST /api/auth/logout` 讓 token 失效
|
||
- 清除所有 cookies
|
||
- 若有未儲存的資料,顯示確認 Dialog
|
||
|
||
---
|
||
|
||
## 7. 首次登入的引導
|
||
|
||
**使用者第一次登入完成後**(有個好方法:**從註冊頁進來的**):
|
||
|
||
- 註冊後跳 `/devices/pair`(引導第一次配對)
|
||
- Dashboard 的 `OnboardingDialog`(local-tool 既有版本假設 USB Dongle,**Phase 0 雲端版暫時 disable**)
|
||
- Phase 1 做新的雲端版 Onboarding(「歡迎來到 visionA Cloud — 讓我們先配對你的第一台裝置」→ 跳 `/devices/pair`)
|
||
|
||
---
|
||
|
||
## 8. 錯誤情境(Phase 1 再實作)
|
||
|
||
雛形不做,但在這裡列出 Phase 1 要處理的情境:
|
||
|
||
| 情境 | UI 回饋 |
|
||
|------|---------|
|
||
| 密碼錯誤 | Input 下方紅字「Email 或密碼不正確」(不明說哪個錯,避免 enumeration attack) |
|
||
| Email 不存在 | 同上 |
|
||
| 帳號被鎖(短時間內失敗太多次)| 顯示「此帳號暫時被鎖定,請 15 分鐘後再試」 |
|
||
| Email 已被註冊(註冊時)| Input 下方紅字「此 Email 已被使用」 |
|
||
| 密碼強度不足 | 密碼下方即時顯示強度指示器 |
|
||
| 網路錯誤 | Toast「連線失敗,請稍後再試」 |
|
||
| ToS 未勾選 | Button disabled + 紅字提示 |
|
||
|
||
---
|
||
|
||
## 9. 安全考量(給 Architect)
|
||
|
||
雛形不做,但 Phase 1 必須:
|
||
|
||
- 密碼 hash:bcrypt / argon2
|
||
- Token:JWT(short-lived access + refresh)或 session cookie(httpOnly + secure + SameSite)
|
||
- CSRF 保護
|
||
- XSS 防護(Content-Security-Policy header)
|
||
- Rate Limit(失敗登入 N 次鎖定 X 分鐘)
|
||
- 密碼政策(最少長度、需字母 + 數字等,視法規要求)
|
||
- 記錄登入來源 IP / User-Agent(/account/sessions 頁面顯示)
|
||
|
||
---
|
||
|
||
## 10. 無障礙
|
||
|
||
- 所有表單 `<label htmlFor>` 與 `<input id>` 對應
|
||
- `autoComplete` 正確設定(`email` / `current-password` / `new-password`)
|
||
- 錯誤訊息用 `aria-describedby` 指向 input
|
||
- Tab 順序符合視覺順序
|
||
- Enter 可提交表單
|
||
- Focus ring 清晰可見(shadcn 預設達標)
|
||
- 密碼欄不使用 `aria-live`(避免 SR 念密碼)
|
||
|
||
---
|
||
|
||
## 11. 設計示意(給 Frontend Agent 參考既有 Shadcn Auth 模板)
|
||
|
||
Shadcn 官方有 `auth-01` / `auth-02` 等區塊範例。可參考:
|
||
- https://ui.shadcn.com/blocks(Authentication 分類)
|
||
|
||
**選擇 `auth-01` 或 `auth-04` 風格**(左側表單 + 右側品牌區或全螢幕置中)。雛形建議**全螢幕置中**(更簡潔)。
|
||
|
||
Phase 1 可升級為左右分欄 + 右側行銷訊息 / 圖片。
|
||
|
||
---
|
||
|
||
## 12. Phase 1+ TODO(完整清單)
|
||
|
||
| 項目 | 重要性 |
|
||
|------|-------|
|
||
| OAuth(Google、GitHub、Microsoft) | 高 |
|
||
| Email 驗證流程 | 高 |
|
||
| 密碼重設(Email 連結 → 重設頁)| 高 |
|
||
| 密碼強度指示器 | 高 |
|
||
| ToS / Privacy Policy 勾選 | 高(法規) |
|
||
| Remember me 選項 | 中 |
|
||
| 防機器人(reCAPTCHA / Turnstile) | 中 |
|
||
| Rate limit 提示 UI | 中 |
|
||
| 2FA(TOTP / WebAuthn / SMS)| 中(Phase 2)|
|
||
| SSO for 企業(SAML)| 低(Phase 2+)|
|
||
| Magic link(passwordless)| 低(Phase 2)|
|
||
| 登入行為分析 / 異常偵測 | 低(Phase 2+)|
|