diff --git a/CLAUDE.md b/CLAUDE.md index 97c7c59..a44baaa 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -87,12 +87,14 @@ Với mỗi task cụ thể (vd "thêm API tạo Request", "màn hình bàn giao - **Auth (login nội bộ, không phải SSO công ty) xong (2026-07-31)**: `User` Prisma model (`prisma/schema.prisma`), `src/lib/users.ts` (Zod + `createUser()` + `verifyUserCredentials()`, bcryptjs hash), `scripts/create-user.ts` (tạo account bằng CLI, không có self-registration), Auth.js v5 (`next-auth@^5.0.0-beta.32`, Credentials provider, JWT session, không dùng `@auth/prisma-adapter` vì không cần OAuth/DB session) ở `src/auth.ts` + `src/app/api/auth/[...nextauth]/route.ts`, trang login `src/app/login/page.tsx`. Permission model **flat** (bất kỳ User nào login cũng làm mọi thứ được — quyết định đã chốt, không có `role`). **CONFIRMED qua verify độc lập** (test-runner + security review): 16/16 test pass (6 cũ + 10 mới) trên Postgres thật, tsc/lint/build sạch, không lộ password, bcrypt compare đúng chuẩn constant-time, `AUTH_SECRET` fail-closed thật khi thiếu (đã test thực tế, không fallback giá trị đoán được). - **Finding nhỏ (chưa block, cần fix sau)**: timing side-channel trong `verifyUserCredentials` — trả về sớm khi email không tồn tại (bỏ qua bcrypt compare), nên request với email sai vs password sai có thời gian phản hồi khác nhau chút, có thể dò được email nào tồn tại. Fix: luôn chạy `bcrypt.compare()` kể cả khi không tìm thấy user (so với 1 hash giả cố định). - **Finding nhỏ (chấp nhận được, cần track)**: `scripts/create-user.ts --password ` lộ password vào shell history/process list — chấp nhận vì chỉ IT admin tự chạy tay, không phải self-registration công khai, nhưng nên cải thiện sau (đọc từ prompt/stdin thay vì argv). - - **Gap quan trọng, không phải bug**: Auth đã wire xong nhưng **chưa có gì trong app thực sự yêu cầu login** — `/api/requests` và mọi route khác vẫn hoàn toàn không có gate, chưa có `middleware.ts`. Cần 1 task riêng để thêm route-gating trước khi hệ thống này có tác dụng kiểm soát truy cập thật. - **Note triển khai (chưa cần ngay, nhớ trước khi deploy)**: chạy `next start` (production) cần thêm `AUTH_TRUST_HOST=true` hoặc `AUTH_URL` trong env, nếu không sẽ gặp lỗi `UntrustedHost` — chưa thêm vào `.env.example`, cần làm trước khi deploy thật ở nhà. +- **Route-gating xong (2026-07-31)**: `src/middleware.ts` (protect mọi page route theo default-deny, redirect `/login` nếu chưa đăng nhập, trừ `/login` chính nó — tránh vòng lặp redirect), `src/auth.config.ts` (config Auth.js edge-safe, tách ra khỏi `src/auth.ts` — **không** có Credentials provider vì `authorize()` gọi `verifyUserCredentials()` kéo theo `bcryptjs`, là Node API không chạy được trên Edge Runtime mà `middleware.ts` dùng; `src/middleware.ts` tự tạo instance `NextAuth(authConfig)` riêng, nhẹ, chỉ để decode JWT session cookie — xác nhận qua build thật: trước khi tách, `next build` báo warning bcryptjs/jose bundle vào Edge Runtime (middleware 115kB); sau khi tách, build sạch, middleware còn 78.9kB). `src/auth.ts` giờ spread `authConfig` + thêm Credentials provider thật, dùng cho API route/Server Component (chạy Node runtime). + - `config.matcher` của middleware loại trừ toàn bộ `/api` (không chỉ `/api/auth`) — API route tự check `auth()` lấy session ngay trong handler (không dựa hoàn toàn vào middleware, theo đúng khuyến nghị chính thức của Auth.js: verify session càng gần chỗ fetch data càng tốt). `/api/requests` (`GET`/`POST`) đã thêm check `auth()`, trả `401 {"error":"Unauthorized"}` nếu chưa login — xem `src/app/api/requests/route.ts`. + - `/` (home page placeholder) **có** yêu cầu login — chọn theo nguyên tắc "mặc định chặn hết, allowlist rõ ràng cho cái không cần" thay vì opt-in dễ quên route mới. + - Test: `tests/requests.test.ts` mock `@/auth` (`vi.mock`) thay vì gọi `auth()` thật, vì `auth()` không tham số đọc cookie qua `next/headers` — chỉ hoạt động khi chạy trong request-handling thật của Next.js, không phải khi test import gọi thẳng `GET()`/`POST()` (bypass hoàn toàn Next). Test mặc định mock session hợp lệ (`beforeEach`) để không phá test nghiệp vụ cũ, có riêng 2 test set mock về `null` để xác nhận 401. `middleware.ts` bản thân **không** có test tự động (chạy Edge Runtime, không test được qua vitest theo cách hiện tại) — đã verify tay qua `next build` + `next start` thật (curl xác nhận: `GET /` → 307 → `/login`, `GET /login` → 200 không loop, `GET/POST /api/requests` chưa login → 401 JSON, `GET /api/auth/session` → 200 không bị chặn). **CONFIRMED**: `npx tsc --noEmit`, `npm run lint`, `npm test` (18/18, gồm 2 test 401 mới), `npm run build` đều sạch. ## Việc còn dở -- **Wire auth vào route** (`middleware.ts` hoặc `auth()` check từng API route) — Auth hiện không chặn gì cả, đây là việc ưu tiên cao tiếp theo để Auth có tác dụng thật. - Fix timing side-channel ở `verifyUserCredentials` (xem note ở "Trạng thái hiện tại"). - Cải thiện `scripts/create-user.ts` để password không lộ qua CLI arg (đọc từ stdin/prompt thay vì `--password`). - Thêm `AUTH_TRUST_HOST`/`AUTH_URL` vào `.env.example` trước khi deploy production. @@ -106,4 +108,4 @@ Với mỗi task cụ thể (vd "thêm API tạo Request", "màn hình bàn giao - Auth/phân quyền (ai được approve request, ai được xác nhận nhận hàng/bàn giao) --- -Cập nhật lần cuối: 2026-07-30 +Cập nhật lần cuối: 2026-07-31