commit
This commit is contained in:
@@ -0,0 +1,946 @@
|
||||
# Prompt: แก้ไข Auth.js ClientFetchError และเพิ่มระบบ Session Inactivity Timeout 5 นาที
|
||||
|
||||
## บทบาทของคุณ
|
||||
|
||||
คุณคือ Senior Full-Stack Developer ที่มีความเชี่ยวชาญด้าน:
|
||||
|
||||
- Next.js 16 App Router
|
||||
- React
|
||||
- TypeScript
|
||||
- Auth.js / NextAuth
|
||||
- Middleware
|
||||
- Session Management
|
||||
- Security และ Authentication Flow
|
||||
- TanStack Query หรือระบบ Client State ที่มีอยู่ในโครงการ
|
||||
- Next.js Turbopack
|
||||
|
||||
ให้ตรวจสอบโครงสร้างและโค้ดเดิมของโครงการก่อนดำเนินการแก้ไข โดยต้องปรับปรุงจากโครงสร้างที่มีอยู่จริง ห้ามสร้างระบบ Authentication ซ้ำซ้อนโดยไม่จำเป็น
|
||||
|
||||
---
|
||||
|
||||
# ปัญหาที่ต้องแก้ไข
|
||||
|
||||
ระบบพบ Error ฝั่ง Client ดังนี้:
|
||||
|
||||
```text
|
||||
Error Type:
|
||||
Console ClientFetchError
|
||||
|
||||
Error Message:
|
||||
Unexpected token '<', "<!DOCTYPE "... is not valid JSON.
|
||||
|
||||
Read more at:
|
||||
https://errors.authjs.dev#autherror
|
||||
|
||||
Next.js version:
|
||||
16.2.6 (Turbopack)
|
||||
```
|
||||
|
||||
Error นี้บ่งชี้ว่า Client ของ Auth.js คาดว่าจะได้รับ JSON แต่ Endpoint เช่น:
|
||||
|
||||
```text
|
||||
/api/auth/session
|
||||
```
|
||||
|
||||
กลับส่ง HTML Response เช่น:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
```
|
||||
|
||||
ซึ่งอาจเกิดจาก:
|
||||
|
||||
- Auth.js Route ไม่ทำงานหรืออยู่ผิดตำแหน่ง
|
||||
- `/api/auth/session` ตอบกลับเป็น 404 หรือ 500
|
||||
- Middleware Redirect `/api/auth/*` ไปหน้าล็อกอิน
|
||||
- Auth.js Configuration Error
|
||||
- Database หรือ Adapter Error
|
||||
- Environment Variable ไม่ครบหรือไม่ถูกต้อง
|
||||
- Custom Error Handler ส่ง HTML แทน JSON
|
||||
- Reverse Proxy หรือ Application Redirect ทำให้ Auth API ถูกเปลี่ยนปลายทาง
|
||||
- Client เรียก `getSession()` หรือ `useSession()` ขณะที่ Auth Route ล้มเหลว
|
||||
- SessionProvider ถูกตั้งค่าผิดตำแหน่ง
|
||||
- Error Boundary หรือ Route Handler จัดการ Error ไม่ถูกต้อง
|
||||
|
||||
---
|
||||
|
||||
# เป้าหมายหลัก
|
||||
|
||||
ดำเนินการให้ครบทั้ง 2 ส่วนดังนี้:
|
||||
|
||||
1. ตรวจสอบและแก้ไข Auth.js `ClientFetchError`
|
||||
2. เพิ่มระบบ Session Inactivity Timeout เมื่อผู้ใช้งานไม่มีการเคลื่อนไหวต่อเนื่อง 5 นาที
|
||||
|
||||
---
|
||||
|
||||
# ส่วนที่ 1: ตรวจสอบและแก้ไข Auth.js ClientFetchError
|
||||
|
||||
## 1. ตรวจสอบโครงสร้าง Auth.js ปัจจุบัน
|
||||
|
||||
ตรวจสอบไฟล์ที่เกี่ยวข้องทั้งหมด เช่น:
|
||||
|
||||
```text
|
||||
auth.ts
|
||||
src/auth.ts
|
||||
app/api/auth/[...nextauth]/route.ts
|
||||
src/app/api/auth/[...nextauth]/route.ts
|
||||
middleware.ts
|
||||
src/middleware.ts
|
||||
proxy.ts
|
||||
src/proxy.ts
|
||||
providers.tsx
|
||||
session-provider.tsx
|
||||
layout.tsx
|
||||
.env
|
||||
.env.local
|
||||
next.config.ts
|
||||
```
|
||||
|
||||
รวมถึงไฟล์ Authentication, Authorization และ Database Adapter ที่เกี่ยวข้อง
|
||||
|
||||
ห้ามสมมติชื่อไฟล์หรือโครงสร้าง ให้ค้นหาจากโครงการจริงก่อน
|
||||
|
||||
---
|
||||
|
||||
## 2. ตรวจสอบ Auth Route
|
||||
|
||||
ตรวจสอบว่า Auth.js Route อยู่ในตำแหน่งที่ถูกต้องสำหรับ App Router เช่น:
|
||||
|
||||
```text
|
||||
src/app/api/auth/[...nextauth]/route.ts
|
||||
```
|
||||
|
||||
และ Export Handler ถูกต้อง เช่น:
|
||||
|
||||
```ts
|
||||
import { handlers } from "@/auth";
|
||||
|
||||
export const { GET, POST } = handlers;
|
||||
```
|
||||
|
||||
ตรวจสอบเพิ่มเติมว่า:
|
||||
|
||||
- Import path ถูกต้อง
|
||||
- `handlers` ถูก Export จาก Auth configuration จริง
|
||||
- ไม่มี Circular Dependency
|
||||
- ไม่มี Route อื่นชนกับ `/api/auth/[...nextauth]`
|
||||
- ไม่มี Rewrite หรือ Redirect ที่กระทบ Auth Route
|
||||
|
||||
---
|
||||
|
||||
## 3. ตรวจสอบ Auth.js Configuration
|
||||
|
||||
ตรวจสอบไฟล์ Auth configuration ว่ามีรูปแบบถูกต้อง เช่น:
|
||||
|
||||
```ts
|
||||
import NextAuth from "next-auth";
|
||||
|
||||
export const { handlers, auth, signIn, signOut } = NextAuth({
|
||||
providers: [],
|
||||
});
|
||||
```
|
||||
|
||||
ตรวจสอบ:
|
||||
|
||||
- Providers
|
||||
- Credentials Provider
|
||||
- Adapter
|
||||
- Session Strategy
|
||||
- JWT Callback
|
||||
- Session Callback
|
||||
- Authorized Callback
|
||||
- Sign-in Page
|
||||
- Error Page
|
||||
- Environment Variables
|
||||
- Database Connection
|
||||
- Cookie Configuration
|
||||
- Trust Host Configuration
|
||||
- Base Path หากมีการตั้งค่า
|
||||
|
||||
หากมี Custom Adapter หรือ LDAP/Active Directory ให้ตรวจสอบ Error Handling ของส่วนนั้นด้วย
|
||||
|
||||
---
|
||||
|
||||
## 4. ตรวจสอบ Endpoint `/api/auth/session`
|
||||
|
||||
ทดสอบ Endpoint นี้โดยตรง:
|
||||
|
||||
```text
|
||||
GET /api/auth/session
|
||||
```
|
||||
|
||||
ผลลัพธ์ที่ถูกต้องต้องเป็น JSON และมี `Content-Type` เป็น:
|
||||
|
||||
```text
|
||||
application/json
|
||||
```
|
||||
|
||||
ตัวอย่างผลลัพธ์เมื่อยังไม่ได้เข้าสู่ระบบ:
|
||||
|
||||
```json
|
||||
{}
|
||||
```
|
||||
|
||||
หรือ Response ตามมาตรฐานของ Auth.js
|
||||
|
||||
ต้องไม่ตอบกลับเป็น:
|
||||
|
||||
- HTML
|
||||
- หน้า Login
|
||||
- หน้า 404
|
||||
- หน้า Error ของ Next.js
|
||||
- Redirect ไปหน้าอื่น
|
||||
- Reverse Proxy Error Page
|
||||
|
||||
ให้ตรวจสอบ HTTP Status เช่น:
|
||||
|
||||
```text
|
||||
200
|
||||
302
|
||||
307
|
||||
401
|
||||
404
|
||||
500
|
||||
```
|
||||
|
||||
และหาสาเหตุจาก Status ที่พบจริง
|
||||
|
||||
---
|
||||
|
||||
## 5. ตรวจสอบ Middleware หรือ Proxy
|
||||
|
||||
ตรวจสอบว่า Middleware หรือ Proxy ไม่ได้ดัก Route ต่อไปนี้:
|
||||
|
||||
```text
|
||||
/api/auth
|
||||
/api/auth/*
|
||||
/_next/*
|
||||
/favicon.ico
|
||||
```
|
||||
|
||||
Auth.js API Route ต้องไม่ถูก Redirect ไปหน้าล็อกอิน
|
||||
|
||||
ปรับ Matcher ให้ยกเว้น Auth API และ Static Assets อย่างเหมาะสม เช่นแนวทาง:
|
||||
|
||||
```ts
|
||||
export const config = {
|
||||
matcher: ["/((?!api/auth|_next/static|_next/image|favicon.ico).*)"],
|
||||
};
|
||||
```
|
||||
|
||||
แต่ต้องปรับให้เข้ากับโครงสร้างจริงของโครงการ และต้องไม่ทำให้ Route Protection เดิมเสียหาย
|
||||
|
||||
หากโครงการใช้ `proxy.ts` ตามโครงสร้างของ Next.js รุ่นปัจจุบัน ให้ตรวจสอบไฟล์นั้นด้วย ไม่จำกัดเฉพาะ `middleware.ts`
|
||||
|
||||
---
|
||||
|
||||
## 6. ตรวจสอบ Environment Variables
|
||||
|
||||
ตรวจสอบ Environment Variables ที่เกี่ยวข้อง เช่น:
|
||||
|
||||
```text
|
||||
AUTH_SECRET
|
||||
AUTH_URL
|
||||
NEXTAUTH_SECRET
|
||||
NEXTAUTH_URL
|
||||
DATABASE_URL
|
||||
```
|
||||
|
||||
รวมถึง Environment Variables ของ:
|
||||
|
||||
- LDAP
|
||||
- Active Directory
|
||||
- OAuth Provider
|
||||
- Database Adapter
|
||||
- Reverse Proxy
|
||||
- Internal API
|
||||
|
||||
ให้ใช้ชื่อตัวแปรตาม Version และโครงสร้าง Auth.js ที่โครงการใช้งานจริง
|
||||
|
||||
ห้ามเปิดเผย Secret ใน Log หรือ Commit
|
||||
|
||||
หาก Environment Variable ที่จำเป็นขาด ให้เพิ่มตัวอย่างใน:
|
||||
|
||||
```text
|
||||
.env.example
|
||||
```
|
||||
|
||||
โดยใช้ Placeholder เท่านั้น
|
||||
|
||||
---
|
||||
|
||||
## 7. ตรวจสอบ Server Error ที่แท้จริง
|
||||
|
||||
ตรวจสอบ Server Log และ Terminal Log ขณะที่เรียก:
|
||||
|
||||
```text
|
||||
/api/auth/session
|
||||
```
|
||||
|
||||
ค้นหา Root Cause เช่น:
|
||||
|
||||
- Database Connection Error
|
||||
- Prisma Initialization Error
|
||||
- LDAP Connection Error
|
||||
- Provider Configuration Error
|
||||
- Invalid Secret
|
||||
- Callback Error
|
||||
- JWT Decryption Error
|
||||
- Cookie Parsing Error
|
||||
- Runtime Compatibility Error
|
||||
- Edge Runtime ไม่รองรับ Library บางตัว
|
||||
- Node Runtime Configuration Error
|
||||
|
||||
ห้ามแก้ด้วยการซ่อน Error ฝั่ง Client เพียงอย่างเดียว ต้องแก้ Root Cause ที่ทำให้ Endpoint ส่ง HTML
|
||||
|
||||
---
|
||||
|
||||
## 8. ปรับปรุง Error Handling
|
||||
|
||||
ปรับปรุง Error Handling เพื่อให้:
|
||||
|
||||
- Auth API ไม่ส่ง HTML Response โดยไม่จำเป็น
|
||||
- Client ไม่เกิด Unhandled Promise Rejection
|
||||
- ไม่แสดง Stack Trace หรือข้อมูล Sensitive แก่ผู้ใช้งาน
|
||||
- Server Log มีข้อมูลเพียงพอสำหรับ Debug
|
||||
- Production แสดงข้อความที่เหมาะสม
|
||||
- Development ยังสามารถตรวจสอบ Root Cause ได้
|
||||
|
||||
หาก `useSession()`, `getSession()`, `SessionProvider` หรือ Custom API Client มี Error Handling ไม่ครบ ให้ปรับปรุงด้วย
|
||||
|
||||
ห้ามใช้วิธีแก้แบบ:
|
||||
|
||||
```ts
|
||||
try {
|
||||
// ...
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
หากทำให้ Root Cause ถูกซ่อนโดยไม่มี Logging ที่เหมาะสม
|
||||
|
||||
---
|
||||
|
||||
# ส่วนที่ 2: เพิ่มระบบ Session Inactivity Timeout 5 นาที
|
||||
|
||||
## ความต้องการ
|
||||
|
||||
เมื่อผู้ใช้งานเข้าสู่ระบบแล้ว หากไม่มีการเคลื่อนไหวหรือไม่มี Interaction กับระบบต่อเนื่องเป็นเวลา:
|
||||
|
||||
```text
|
||||
5 นาที
|
||||
```
|
||||
|
||||
ให้ระบบดำเนินการดังนี้:
|
||||
|
||||
1. แสดงข้อความแจ้งผู้ใช้งานว่า:
|
||||
|
||||
```text
|
||||
คุณไม่ได้ใช้งานระบบเป็นเวลานาน เซสชันของคุณหมดอายุแล้ว กรุณาเข้าสู่ระบบใหม่
|
||||
```
|
||||
|
||||
2. ทำการ Sign out จาก Auth.js อย่างถูกต้อง
|
||||
3. ล้าง Client State หรือข้อมูล Session ที่เกี่ยวข้อง
|
||||
4. Redirect กลับไปหน้าล็อกอิน
|
||||
5. ผู้ใช้งานต้องเข้าสู่ระบบใหม่ก่อนใช้งานระบบต่อ
|
||||
6. ห้ามเกิด Redirect Loop
|
||||
7. ห้ามกระทบผู้ใช้งานที่ยังมีการเคลื่อนไหวอยู่
|
||||
|
||||
---
|
||||
|
||||
## 1. นิยาม Activity ของผู้ใช้งาน
|
||||
|
||||
ให้ถือว่า Event ต่อไปนี้เป็น Activity:
|
||||
|
||||
```text
|
||||
mousedown
|
||||
mousemove
|
||||
keydown
|
||||
scroll
|
||||
touchstart
|
||||
click
|
||||
focus
|
||||
```
|
||||
|
||||
สามารถเลือกใช้เฉพาะ Event ที่เหมาะสมเพื่อไม่ให้เกิด Performance Issue
|
||||
|
||||
ต้องใช้ Event แบบ Throttle หรือ Debounce เพื่อไม่ให้ Reset Timer ถี่เกินไป โดยเฉพาะ:
|
||||
|
||||
```text
|
||||
mousemove
|
||||
scroll
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. เริ่มตรวจจับเฉพาะผู้ที่เข้าสู่ระบบแล้ว
|
||||
|
||||
ระบบ Inactivity Timeout ต้องทำงานเฉพาะเมื่อ:
|
||||
|
||||
```ts
|
||||
status === "authenticated";
|
||||
```
|
||||
|
||||
ห้ามเริ่ม Timer ในกรณี:
|
||||
|
||||
```ts
|
||||
status === "loading";
|
||||
status === "unauthenticated";
|
||||
```
|
||||
|
||||
เมื่อผู้ใช้งาน Logout แล้ว ต้อง Cleanup:
|
||||
|
||||
- Timer
|
||||
- Event Listener
|
||||
- Storage Listener
|
||||
- Broadcast Channel
|
||||
- Pending Callback
|
||||
|
||||
ทั้งหมดอย่างถูกต้อง
|
||||
|
||||
---
|
||||
|
||||
## 3. ระยะเวลา Timeout
|
||||
|
||||
กำหนดค่ากลาง เช่น:
|
||||
|
||||
```ts
|
||||
const INACTIVITY_TIMEOUT_MS = 5 * 60 * 1000;
|
||||
```
|
||||
|
||||
ควรจัดเก็บไว้ใน Configuration หรือ Constant กลาง เพื่อให้เปลี่ยนภายหลังได้ง่าย
|
||||
|
||||
ห้ามกระจายค่า `300000` ไว้หลายจุดในระบบ
|
||||
|
||||
---
|
||||
|
||||
## 4. รูปแบบการแจ้งเตือน
|
||||
|
||||
เมื่อครบ 5 นาที ให้แสดง Dialog, AlertDialog, Modal หรือ Toast ตาม Component มาตรฐานของโครงการ
|
||||
|
||||
แนะนำให้ใช้:
|
||||
|
||||
```text
|
||||
AlertDialog
|
||||
```
|
||||
|
||||
ข้อความ:
|
||||
|
||||
```text
|
||||
เซสชันหมดอายุ
|
||||
```
|
||||
|
||||
รายละเอียด:
|
||||
|
||||
```text
|
||||
คุณไม่ได้ใช้งานระบบเป็นเวลานาน เซสชันของคุณหมดอายุแล้ว กรุณาเข้าสู่ระบบใหม่
|
||||
```
|
||||
|
||||
ปุ่ม:
|
||||
|
||||
```text
|
||||
เข้าสู่ระบบใหม่
|
||||
```
|
||||
|
||||
เมื่อผู้ใช้งานกดปุ่ม ให้ Redirect ไปหน้าล็อกอิน
|
||||
|
||||
อย่างไรก็ตาม เพื่อความปลอดภัย ระบบต้อง Sign out และถือว่า Session หมดอายุทันทีเมื่อครบเวลา ไม่ควรรอให้ผู้ใช้งานกดปุ่มก่อนจึงค่อย Sign out
|
||||
|
||||
หากใช้ Toast ให้ตั้งระยะเวลาที่ผู้ใช้งานสามารถอ่านข้อความได้อย่างเหมาะสมก่อน Redirect
|
||||
|
||||
ห้ามใช้ Native Browser Alert หากโครงการมี UI Component มาตรฐานอยู่แล้ว
|
||||
|
||||
---
|
||||
|
||||
## 5. ลำดับการทำงานเมื่อหมดเวลา
|
||||
|
||||
ลำดับที่ต้องการ:
|
||||
|
||||
```text
|
||||
ครบเวลาไม่มี Activity
|
||||
↓
|
||||
ป้องกันการทำงานซ้ำด้วย Flag
|
||||
↓
|
||||
หยุด Timer และถอด Event Listener
|
||||
↓
|
||||
แสดงข้อความแจ้ง Session หมดอายุ
|
||||
↓
|
||||
ล้างข้อมูล Client State ที่มีความ Sensitive
|
||||
↓
|
||||
เรียก signOut ของ Auth.js
|
||||
↓
|
||||
Redirect ไปหน้า Login
|
||||
```
|
||||
|
||||
ตัวอย่างแนวทาง:
|
||||
|
||||
```ts
|
||||
await signOut({
|
||||
redirect: false,
|
||||
});
|
||||
|
||||
router.replace("/login?reason=inactivity");
|
||||
router.refresh();
|
||||
```
|
||||
|
||||
ให้ปรับตาม Auth.js API และโครงสร้างจริงของโครงการ
|
||||
|
||||
หากจำเป็นต้องแสดงข้อความหลัง Redirect ให้ส่ง Query Parameter เช่น:
|
||||
|
||||
```text
|
||||
/login?reason=inactivity
|
||||
```
|
||||
|
||||
จากนั้นหน้า Login แสดงข้อความ:
|
||||
|
||||
```text
|
||||
คุณไม่ได้ใช้งานระบบเป็นเวลานาน เซสชันของคุณหมดอายุแล้ว กรุณาเข้าสู่ระบบใหม่
|
||||
```
|
||||
|
||||
ต้องลบหรือ Replace URL อย่างเหมาะสมหลังแสดงข้อความ เพื่อไม่ให้ข้อความแสดงซ้ำเมื่อ Refresh โดยไม่จำเป็น
|
||||
|
||||
---
|
||||
|
||||
## 6. รองรับหลาย Tab
|
||||
|
||||
ระบบควรรองรับกรณีเปิดหลาย Browser Tab
|
||||
|
||||
เมื่อผู้ใช้งานมี Activity ใน Tab หนึ่ง ควร Sync `lastActivity` ให้ Tab อื่นรับรู้ เพื่อไม่ให้ Tab อื่น Logout ผู้ใช้งานผิดพลาด
|
||||
|
||||
สามารถใช้:
|
||||
|
||||
```text
|
||||
localStorage
|
||||
storage event
|
||||
BroadcastChannel
|
||||
```
|
||||
|
||||
แนวทางที่แนะนำ:
|
||||
|
||||
```text
|
||||
auth:last-activity
|
||||
auth:session-expired
|
||||
```
|
||||
|
||||
ข้อกำหนด:
|
||||
|
||||
- Activity จาก Tab หนึ่งต้องอัปเดตเวลาให้ Tab อื่น
|
||||
- เมื่อ Session หมดอายุใน Tab หนึ่ง Tab อื่นต้อง Logout ตาม
|
||||
- ป้องกันหลาย Tab เรียก `signOut()` พร้อมกันจำนวนมาก
|
||||
- Cleanup Channel และ Listener เมื่อ Component Unmount
|
||||
- ต้องตรวจสอบการทำงานใน Browser ที่ไม่รองรับ `BroadcastChannel`
|
||||
|
||||
---
|
||||
|
||||
## 7. ห้ามใช้เฉพาะ Client Timer เป็น Security Control
|
||||
|
||||
Client-side inactivity timer เป็นส่วนของ UX เท่านั้น
|
||||
|
||||
ต้องตรวจสอบ Session Configuration ฝั่ง Server ร่วมด้วย เช่น:
|
||||
|
||||
- JWT expiration
|
||||
- Session maxAge
|
||||
- Cookie expiration
|
||||
- Server-side authorization
|
||||
- Permission validation ใน Protected Route
|
||||
- API Route ต้องตรวจ Session ทุกครั้ง
|
||||
|
||||
ห้ามถือว่าผู้ใช้หมดสิทธิ์เพียงเพราะ Client Redirect แล้วเท่านั้น
|
||||
|
||||
อย่างไรก็ตาม ระยะเวลาหมดอายุฝั่ง Server ไม่จำเป็นต้องเท่ากับ Inactivity Timeout 5 นาที หากระบบต้องรองรับ Sliding Session แต่ต้องอธิบายความแตกต่างไว้ใน Implementation Report
|
||||
|
||||
---
|
||||
|
||||
## 8. ระวังการสูญหายของข้อมูลใน Form
|
||||
|
||||
ตรวจสอบว่าหน้าใดมี Form ที่ผู้ใช้งานอาจกำลังกรอกข้อมูล
|
||||
|
||||
เมื่อ Session หมดอายุ:
|
||||
|
||||
- ต้องไม่พยายามบันทึกข้อมูลหลังหมด Session
|
||||
- ต้องไม่เกิด API Request ด้วย Session ที่หมดอายุ
|
||||
- ต้องไม่เก็บข้อมูล Sensitive ไว้ใน Local Storage โดยไม่จำเป็น
|
||||
- สามารถแจ้งข้อความว่าข้อมูลที่ยังไม่ได้บันทึกอาจสูญหายได้ หากเหมาะสม
|
||||
|
||||
ไม่ต้องเพิ่มระบบ Auto Save เว้นแต่โครงการมีอยู่แล้ว
|
||||
|
||||
---
|
||||
|
||||
# สถาปัตยกรรมที่แนะนำ
|
||||
|
||||
สร้าง Component หรือ Hook กลาง เช่น:
|
||||
|
||||
```text
|
||||
src/components/auth/session-inactivity-guard.tsx
|
||||
```
|
||||
|
||||
หรือ:
|
||||
|
||||
```text
|
||||
src/hooks/use-session-inactivity.ts
|
||||
```
|
||||
|
||||
แล้วนำไปติดตั้งในตำแหน่งที่ครอบคลุมเฉพาะ Protected Area เช่น:
|
||||
|
||||
```text
|
||||
src/app/dashboard/layout.tsx
|
||||
```
|
||||
|
||||
หรือ Protected Layout ที่มีอยู่จริง
|
||||
|
||||
ไม่ควรติดตั้งใน Root Layout หากทำให้หน้า Login เริ่มจับเวลาโดยไม่จำเป็น
|
||||
|
||||
ตัวอย่างโครงสร้าง:
|
||||
|
||||
```text
|
||||
Protected Layout
|
||||
├── SessionProvider
|
||||
├── SessionInactivityGuard
|
||||
└── Protected Page Content
|
||||
```
|
||||
|
||||
ให้ใช้ Provider และ Layout เดิมของโครงการ หากมีอยู่แล้ว
|
||||
|
||||
---
|
||||
|
||||
# ข้อกำหนดด้าน UX
|
||||
|
||||
## ข้อความภาษาไทย
|
||||
|
||||
หัวข้อ:
|
||||
|
||||
```text
|
||||
เซสชันหมดอายุ
|
||||
```
|
||||
|
||||
รายละเอียด:
|
||||
|
||||
```text
|
||||
คุณไม่ได้ใช้งานระบบเป็นเวลานาน เซสชันของคุณหมดอายุแล้ว กรุณาเข้าสู่ระบบใหม่
|
||||
```
|
||||
|
||||
ปุ่ม:
|
||||
|
||||
```text
|
||||
เข้าสู่ระบบใหม่
|
||||
```
|
||||
|
||||
กรณี Redirect ไปหน้า Login โดยตรง ให้หน้า Login แสดงข้อความ:
|
||||
|
||||
```text
|
||||
คุณไม่ได้ใช้งานระบบเป็นเวลานาน กรุณาเข้าสู่ระบบใหม่
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# ข้อกำหนดด้านความปลอดภัย
|
||||
|
||||
- ห้าม Log Token
|
||||
- ห้าม Log Cookie
|
||||
- ห้าม Log Password
|
||||
- ห้าม Log LDAP Credential
|
||||
- ห้ามเปิดเผย Environment Secret
|
||||
- ห้ามเก็บ Access Token ใน Local Storage หากระบบเดิมไม่ได้ออกแบบเช่นนั้น
|
||||
- API และ Server Action ต้องตรวจ Session ฝั่ง Server
|
||||
- Protected Route ต้องไม่พึ่ง Client Guard เพียงอย่างเดียว
|
||||
- ป้องกัน Open Redirect
|
||||
- Redirect URL ต้องเป็น Internal Path ที่กำหนดไว้
|
||||
- ป้องกันการเรียก Logout ซ้ำ
|
||||
- ป้องกัน Redirect Loop ระหว่าง Login และ Protected Route
|
||||
|
||||
---
|
||||
|
||||
# Test Cases ที่ต้องตรวจสอบ
|
||||
|
||||
## Auth.js Error
|
||||
|
||||
### TC-AUTH-001
|
||||
|
||||
เปิด:
|
||||
|
||||
```text
|
||||
/api/auth/session
|
||||
```
|
||||
|
||||
ผลลัพธ์ที่คาดหวัง:
|
||||
|
||||
- HTTP Status ถูกต้อง
|
||||
- Response เป็น JSON
|
||||
- `Content-Type` เป็น JSON
|
||||
- ไม่มี HTML Response
|
||||
|
||||
### TC-AUTH-002
|
||||
|
||||
เปิดหน้า Protected Route ขณะยังไม่เข้าสู่ระบบ
|
||||
|
||||
ผลลัพธ์ที่คาดหวัง:
|
||||
|
||||
- Redirect ไปหน้า Login อย่างถูกต้อง
|
||||
- `/api/auth/session` ไม่ถูก Middleware Redirect
|
||||
- ไม่เกิด `Unexpected token '<'`
|
||||
|
||||
### TC-AUTH-003
|
||||
|
||||
เข้าสู่ระบบสำเร็จ
|
||||
|
||||
ผลลัพธ์ที่คาดหวัง:
|
||||
|
||||
- `useSession()` ได้สถานะ `authenticated`
|
||||
- ไม่มี `ClientFetchError`
|
||||
- Session โหลดได้ตามปกติ
|
||||
|
||||
### TC-AUTH-004
|
||||
|
||||
Database หรือ LDAP ไม่พร้อมใช้งาน
|
||||
|
||||
ผลลัพธ์ที่คาดหวัง:
|
||||
|
||||
- Server มี Error Log ที่เหมาะสม
|
||||
- Client ไม่เกิด Unhandled Error
|
||||
- ผู้ใช้งานได้รับข้อความที่เหมาะสม
|
||||
- ไม่มีข้อมูล Sensitive ถูกเปิดเผย
|
||||
|
||||
---
|
||||
|
||||
## Session Inactivity Timeout
|
||||
|
||||
### TC-IDLE-001
|
||||
|
||||
เข้าสู่ระบบและไม่มี Activity 5 นาที
|
||||
|
||||
ผลลัพธ์ที่คาดหวัง:
|
||||
|
||||
- แสดงข้อความ Session หมดอายุ
|
||||
- Sign out สำเร็จ
|
||||
- Redirect ไปหน้า Login
|
||||
- ต้อง Login ใหม่
|
||||
|
||||
### TC-IDLE-002
|
||||
|
||||
มี Activity ก่อนครบ 5 นาที
|
||||
|
||||
ผลลัพธ์ที่คาดหวัง:
|
||||
|
||||
- Timer ถูก Reset
|
||||
- ผู้ใช้งานไม่ถูก Logout
|
||||
|
||||
### TC-IDLE-003
|
||||
|
||||
มี Activity ต่อเนื่อง
|
||||
|
||||
ผลลัพธ์ที่คาดหวัง:
|
||||
|
||||
- ระบบไม่ Logout
|
||||
- ไม่มี Memory Leak
|
||||
- ไม่มี Timer ถูกสร้างซ้ำ
|
||||
|
||||
### TC-IDLE-004
|
||||
|
||||
เปิดระบบหลาย Tab และใช้งานอยู่ใน Tab หนึ่ง
|
||||
|
||||
ผลลัพธ์ที่คาดหวัง:
|
||||
|
||||
- Activity Sync ไปยัง Tab อื่น
|
||||
- Tab อื่นไม่ Logout ผู้ใช้งานผิดพลาด
|
||||
|
||||
### TC-IDLE-005
|
||||
|
||||
Session หมดอายุใน Tab หนึ่ง
|
||||
|
||||
ผลลัพธ์ที่คาดหวัง:
|
||||
|
||||
- ทุก Tab รับรู้ว่า Session หมดอายุ
|
||||
- ทุก Tab กลับหน้า Login
|
||||
- ไม่เรียก Sign out ซ้ำอย่างผิดปกติ
|
||||
|
||||
### TC-IDLE-006
|
||||
|
||||
Logout ด้วยตนเองก่อนครบเวลา
|
||||
|
||||
ผลลัพธ์ที่คาดหวัง:
|
||||
|
||||
- Timer และ Event Listener ถูก Cleanup
|
||||
- ไม่แสดงข้อความ Inactivity ภายหลัง
|
||||
|
||||
### TC-IDLE-007
|
||||
|
||||
Refresh หน้าใกล้ครบเวลา
|
||||
|
||||
ผลลัพธ์ที่คาดหวัง:
|
||||
|
||||
- ระบบอ่าน `lastActivity` ที่เหมาะสม
|
||||
- ไม่ Reset Timeout อย่างไม่ถูกต้อง หากไม่มี Activity จริง
|
||||
- ไม่ Logout ซ้ำ
|
||||
|
||||
### TC-IDLE-008
|
||||
|
||||
อยู่หน้า Login
|
||||
|
||||
ผลลัพธ์ที่คาดหวัง:
|
||||
|
||||
- ไม่มี Inactivity Timer ทำงาน
|
||||
- ไม่มี Redirect Loop
|
||||
- ไม่มีข้อความหมดอายุแสดงซ้ำโดยไม่จำเป็น
|
||||
|
||||
### TC-IDLE-009
|
||||
|
||||
ผู้ใช้งานกำลังกรอก Form แต่ไม่มี Mouse หรือ Keyboard Activity เกิน 5 นาที
|
||||
|
||||
ผลลัพธ์ที่คาดหวัง:
|
||||
|
||||
- Session หมดอายุตามข้อกำหนด
|
||||
- ระบบแจ้งเตือนอย่างชัดเจน
|
||||
- ไม่มีการส่ง Form หลัง Session หมดอายุ
|
||||
|
||||
---
|
||||
|
||||
# Quality Gate
|
||||
|
||||
ก่อนจบงานต้องดำเนินการ:
|
||||
|
||||
```bash
|
||||
npm run lint
|
||||
npm run typecheck
|
||||
npm run build
|
||||
```
|
||||
|
||||
หรือใช้คำสั่งที่มีอยู่จริงใน `package.json`
|
||||
|
||||
ต้องแก้ไข:
|
||||
|
||||
- TypeScript Error
|
||||
- ESLint Error
|
||||
- Build Error
|
||||
- Import Error
|
||||
- Hydration Error
|
||||
- Unhandled Promise Rejection
|
||||
- Memory Leak จาก Timer หรือ Event Listener
|
||||
|
||||
ห้ามปิด Rule หรือใช้:
|
||||
|
||||
```ts
|
||||
any
|
||||
@ts-ignore
|
||||
eslint-disable
|
||||
```
|
||||
|
||||
โดยไม่มีเหตุผลที่จำเป็นและมีคำอธิบาย
|
||||
|
||||
---
|
||||
|
||||
# ข้อจำกัดในการแก้ไข
|
||||
|
||||
- รักษาโครงสร้างและ Coding Convention เดิม
|
||||
- ใช้ Component มาตรฐานที่โครงการมีอยู่แล้ว
|
||||
- ไม่สร้าง Auth Provider ซ้ำ
|
||||
- ไม่เปลี่ยน Authentication Flow โดยไม่จำเป็น
|
||||
- ไม่ลบ Permission หรือ Route Protection เดิม
|
||||
- ไม่ Hardcode Domain
|
||||
- ไม่ Hardcode Secret
|
||||
- ไม่ Hardcode Login URL หลายตำแหน่ง
|
||||
- ไม่เปลี่ยน Session Strategy หากไม่มีเหตุผลรองรับ
|
||||
- ไม่แก้เฉพาะอาการโดยละเลย Root Cause
|
||||
- ห้ามทำให้ `/api/auth/session` ถูก Redirect ไปหน้า Login
|
||||
|
||||
---
|
||||
|
||||
# ผลลัพธ์ที่ต้องส่งมอบ
|
||||
|
||||
หลังดำเนินการเสร็จ ให้จัดทำรายงานสรุป Markdown โดยประกอบด้วย:
|
||||
|
||||
## 1. Root Cause
|
||||
|
||||
ระบุสาเหตุจริงของ:
|
||||
|
||||
```text
|
||||
Unexpected token '<', "<!DOCTYPE "... is not valid JSON
|
||||
```
|
||||
|
||||
พร้อมระบุว่า HTML Response มาจาก:
|
||||
|
||||
- Route ใด
|
||||
- HTTP Status ใด
|
||||
- Middleware, Server Error หรือ Configuration ส่วนใด
|
||||
|
||||
## 2. Files Changed
|
||||
|
||||
ระบุรายการไฟล์ที่:
|
||||
|
||||
- เพิ่มใหม่
|
||||
- แก้ไข
|
||||
- ลบ
|
||||
|
||||
พร้อมสรุปหน้าที่ของแต่ละไฟล์
|
||||
|
||||
## 3. Auth.js Fix
|
||||
|
||||
อธิบาย:
|
||||
|
||||
- สิ่งที่ผิด
|
||||
- วิธีแก้
|
||||
- เหตุผลที่วิธีใหม่ถูกต้อง
|
||||
- ผลทดสอบ `/api/auth/session`
|
||||
|
||||
## 4. Inactivity Timeout Implementation
|
||||
|
||||
อธิบาย:
|
||||
|
||||
- จุดที่ติดตั้ง Guard
|
||||
- Event ที่ใช้ตรวจ Activity
|
||||
- วิธี Reset Timer
|
||||
- วิธี Logout
|
||||
- วิธี Redirect
|
||||
- วิธีรองรับหลาย Tab
|
||||
- วิธี Cleanup
|
||||
|
||||
## 5. Security Notes
|
||||
|
||||
อธิบาย:
|
||||
|
||||
- Client Timeout
|
||||
- Server Session Expiration
|
||||
- Protected API
|
||||
- Cookie และ Token Handling
|
||||
- ข้อจำกัดที่ยังเหลืออยู่
|
||||
|
||||
## 6. Test Results
|
||||
|
||||
สรุปผล:
|
||||
|
||||
```text
|
||||
Lint
|
||||
Typecheck
|
||||
Build
|
||||
Manual Test
|
||||
Auth Session Endpoint Test
|
||||
Multi-tab Test
|
||||
Inactivity Test
|
||||
```
|
||||
|
||||
## 7. Remaining Risks
|
||||
|
||||
หากยังมีข้อจำกัดหรือสิ่งที่ต้องดำเนินการต่อ ให้ระบุอย่างตรงไปตรงมา ห้ามรายงานว่าสำเร็จหากยังไม่ได้ทดสอบจริง
|
||||
|
||||
---
|
||||
|
||||
# Acceptance Criteria
|
||||
|
||||
งานถือว่าเสร็จสมบูรณ์เมื่อ:
|
||||
|
||||
- ไม่เกิด `ClientFetchError`
|
||||
- ไม่เกิด `Unexpected token '<'`
|
||||
- `/api/auth/session` ตอบกลับเป็น JSON
|
||||
- Middleware ไม่ Redirect Auth API
|
||||
- ผู้ใช้เข้าสู่ระบบได้ตามปกติ
|
||||
- ผู้ใช้ที่ไม่มี Activity ครบ 5 นาทีถูก Sign out
|
||||
- มีข้อความแจ้งว่าไม่ได้ใช้งานระบบเป็นเวลานาน
|
||||
- Redirect กลับหน้า Login สำเร็จ
|
||||
- ต้องเข้าสู่ระบบใหม่
|
||||
- Activity ก่อนครบเวลาสามารถ Reset Timer ได้
|
||||
- รองรับหลาย Tab
|
||||
- ไม่มี Redirect Loop
|
||||
- ไม่มี Timer หรือ Event Listener ค้าง
|
||||
- Protected API ยังตรวจสอบ Session ฝั่ง Server
|
||||
- Lint, Typecheck และ Build ผ่าน
|
||||
- มี Implementation Report สรุป Root Cause และการแก้ไข
|
||||
Reference in New Issue
Block a user