การยืนยันตัวตนและการเข้าถึงระบบ
เอกสารอ้างอิงฉบับครบถ้วนของหน้าเข้าสู่ระบบ (LOGIN) และเส้นทางการเข้าถึงระบบทั้งหมด: ช่องทางยืนยันตัวตน 3 แบบ สเปกทุกองค์ประกอบหน้าจอ กฎการตรวจสอบความถูกต้อง (validation) สถานะฟอร์ม เส้นทางหลังล็อกอิน ข้อมูลที่เก็บในเครื่องผู้ใช้ ความปลอดภัย และบัญชีข้อผิดพลาด — ทุกหัวข้อพิสูจน์จากหน้าจอระบบจริงและซอร์สโค้ดโมดูล frontend
frontend ไฟล์หลักได้แก่ src/app/login-screen.tsx, src/app/api/auth/login/route.ts, src/app/api/auth/demo-login/route.ts, src/lib/holding-code.ts และ src/lib/i18n.ts — อ้างเป็นชื่อไฟล์เพื่อให้ตรวจสอบย้อนกลับได้1.1ภาพรวมการยืนยันตัวตน
BC Ai Account เป็นระบบแบบเว็บแอปพลิเคชัน การยืนยันตัวตนทำที่หน้า https://account.bcaicloud.com/ ทั้งหมด โดยรองรับช่องทาง 3 แบบ:
| ช่องทาง | กลไก (จากโค้ด) | ปลายทางหลังสำเร็จ |
|---|---|---|
| รหัสผ่าน (หลัก) | POST /api/auth/login → backend /login พร้อม username, password, holdingcode (ไม่บังคับ) — timeout 15 วินาที | มี holdingcode → /workspace ทันที; ไม่มี → /holding ให้เลือกกลุ่มกิจการก่อน |
ปุ่ม Google Identity Services (popup) → POST /api/auth/google/verify ด้วย credential — ต้องตั้ง NEXT_PUBLIC_GOOGLE_CLIENT_ID ไม่เช่นนั้นปุ่มจะไม่แสดง | เข้าสู่ /holding เพื่อเลือกกลุ่มกิจการเสมอ | |
| Demo | POST /api/auth/demo-login → backend /demo-login; เปิด/ปิดที่ backend ด้วย BCAI_DEMO_LOGIN_ENABLED | เข้าสู่ /holding ของชุดข้อมูลสาธิต |
ทั้งสามช่องทางได้รับ Bearer token + ตั้ง refresh token เป็น HttpOnly cookie (ผ่าน setRefreshTokenCookie) แล้วบันทึก session ฝั่ง client ด้วย setAuthSession ซึ่งจำ method ว่ามาจาก password / google / demo
saved_password) แต่โค้ดปัจจุบัน ลบคีย์เก่าทิ้งทุกครั้งที่โหลดหน้า และไม่บันทึกรหัสผ่านอีกต่อไป — สิ่งที่คงอยู่ในเครื่องผู้ใช้มีเพียง ภาษา ชื่อผู้ใช้ (ถ้าติ๊กจำ) และรหัสกลุ่มกิจการ1.2สเปกองค์ประกอบหน้าจอ (Screen Spec)

Brand Panel (ฝั่งซ้าย)
| องค์ประกอบ | รายละเอียด |
|---|---|
| แถบป้าย (brand badge) | โลโก้ประกาย + BC Ai Account + คำว่า “พื้นที่ทำงานปลอดภัย” (Secure Workspace) |
| หัวเรื่อง H1 | “เข้าสู่ระบบ” + คำโปรย “ระบบบัญชีและจัดการร้านค้าสำหรับทีมงานหน้าร้าน คลัง และบัญชี” |
| การ์ด 1 — เริ่มใช้งานครั้งแรก | แนะนำผู้ที่ยังไม่มีรหัสกลุ่มกิจการให้ล็อกอินด้วย Google เพื่อสมัครและตั้งค่ากลุ่มกิจการ |
| การ์ด 2 — พื้นที่ทำงานปลอดภัย | อธิบาย Multi Company: ผู้ใช้หนึ่งคนเลือกได้หลายบริษัทหลังเข้าสู่ระบบ |
| แถบสถานะระบบ (ล่างสุด) | จุดสี + ข้อความผลทดสอบการเชื่อมต่อ — เขียว: “เชื่อมต่อ Server สำเร็จ”; hover เห็น Backend URL ที่ใช้จริง (title tooltip) |
Form Panel (ฝั่งขวา)
| องค์ประกอบ | พฤติกรรม (จากโค้ด) |
|---|---|
| ส่วนหัวการ์ด | คำว่า “พื้นที่ทำงานปลอดภัย” + หัวข้อ “ลงชื่อเข้าใช้” + ปุ่มควบคุม: ฟอนต์ / ธีมสี / ธีมมืด / ภาษา / ลิงก์ตั้งค่า (/settings) |
| ส่วนที่ 1: ยืนยันตัวตนด้วยตัวตนดิจิทัล | ปุ่ม Google (GIS renderButton, popup mode) + ปุ่ม “ทดลองใช้ระบบ (Demo)” — ขณะรอรับผลมี spinner “กำลังเข้าสู่ระบบ” |
| เส้นคั่น | “หรือเข้าสู่ระบบด้วยบัญชีภายนอก” (role=separator) |
| ส่วนที่ 2: รหัสกลุ่มกิจการและรหัสผ่าน | ฟอร์มหลัก 3 ช่อง + ตัวเลือกจำชื่อผู้ใช้ + พื้นที่ข้อความผลลัพธ์ + ปุ่ม “เข้าสู่ระบบ” (ดูสเปกฟอร์มหัวข้อ 1.3) |
| หมายเหตุท้ายการ์ด | “เริ่มใช้งานครั้งแรก” พร้อมขั้นตอน 1-2-3: ตั้งกลุ่มกิจการ → ล็อกอินด้วย Google → เข้าพื้นที่ทำงาน |
| ปุ่มลอย (ทั้งหน้า) | Copy DOM (Alt+คลิก) สำหรับทีมสนับสนุน, และ “เมนูด่วน Ctrl+K” |
1.3สเปกฟอร์มรหัสผ่านและกฎ Validation

| ช่อง | ประเภท / autoComplete | กฎ (จากโค้ด) |
|---|---|---|
| รหัสกลุ่มกิจการ | text / organization | Pattern /^[a-z][a-z0-9]{2,29}$/: ตัวพิมพ์เล็ก a-z + ตัวเลข 0-9 ยาว 3–30 ตัว ขึ้นต้นด้วยตัวอักษร ห้าม _ และสัญลักษณ์อื่น — ระบบ normalize อัตโนมัติ(trim + เปลี่ยนเป็นตัวพิมพ์เล็กขณะพิมพ์) placeholder ตัวอย่าง bcdemo01 |
| ชื่อผู้ใช้ | text / username | รับได้ทั้งรหัสพนักงานหรืออีเมล — ต้องไม่ว่าง (trim ก่อนตรวจ) |
| รหัสผ่าน | password / current-password | ต้องไม่ว่าง; ปุ่มตาสลับแสดง/ซ่อน (showPassword) |
| จำชื่อผู้ใช้ | checkbox | เมื่อติ๊ก: เก็บชื่อผู้ใช้ใน localStorage คีย์ saved_username; ไม่ติ๊ก: ลบคีย์ทิ้ง — ส่วนรหัสกลุ่มกิจการ (saved_holdingcode) ระบบจำให้เสมอเมื่อล็อกอินสำเร็จ |
เงื่อนไขเปิดปุ่ม “เข้าสู่ระบบ” (canSubmit): Backend URL พร้อม + ชื่อผู้ใช้ไม่ว่าง + รหัสผ่านไม่ว่าง + ไม่อยู่ระหว่างโหลด — ทั้งนี้ รหัสกลุ่มกิจการไม่อยู่ในเงื่อนไข ผู้ใช้ล็อกอินโดยไม่กรอกก็ได้ ระบบจะพาไปหน้าเลือกกลุ่มกิจการ (/holding) ให้เลือกทีหลัง
ลำดับการตรวจสอบฝั่ง client (performLogin)
- 1Backend URL ว่าง → แจ้ง “กรอก Backend URL”กรณีหายาก เพราะ URL ถูกกำหนดอัตโนมัติจาก origin ของหน้า
- 2รหัสกลุ่มกิจการ normalize แล้วว่าง → แจ้งให้กรอกเกิดเมื่อกรอกมาแต่เหลือแต่ช่องว่าง/สัญลักษณ์
- 3รูปแบบรหัสกลุ่มกิจการไม่ผ่าน pattern → แจ้กข้อความกฎ a-z 0-9 ยาว 3-30ข้อความจริงจาก holding-code.ts
- 4ชื่อผู้ใช้ว่าง / รหัสผ่านว่าง → แจ้งตามช่องก่อนเรียก API
- 5เรียก POST /api/auth/login (timeout 15 วินาที)สำเร็จ: บันทึก session → ไป /workspace หรือ /holding; ผิดพลาด: แสดงข้อความจาก server ในพื้นที่ message
1.4เส้นทางหลังล็อกอิน (Routing)
ระบบพาผู้ใช้ไปตามสายทาง 4 ขั้นตามวิธีล็อกอินและการมี holdingcode:
| ขั้น | เส้นทาง (URL) | หน้าจอ | เงื่อนไขที่จะเจอ |
|---|---|---|---|
| 1 | / | หน้าเข้าสู่ระบบ | ทุกกรณี — จุดเริ่มต้น |
| 2 | /holding | เลือกกลุ่มกิจการ | ล็อกอินด้วย Google/Demo เสมอ; หรือล็อกอินรหัสผ่านแบบไม่กรอกรหัสกลุ่มกิจการ |
| 3 | /workspace | เลือกบริษัท (และเลือกสาขาต่อ) | ล็อกอินรหัสผ่านพร้อมรหัสกลุ่มกิจการจะมาถึงที่นี่ทันที; หรือหลังเลือกกลุ่มกิจการจาก /holding |
| 4 | /menu | เมนูหลัก | หลังเลือกบริษัทและสาขาครบ |




1.5การแสดงผลหลายภาษา (i18n)
ระบบใช้หลัก Thai-First: ไฟล์ src/locales/th.json เป็นแหล่งความจริง ภาษาอื่นแปลจากไทย รวมรองรับ 12 ภาษา:
- ภาษาไทย (th) — ค่าเริ่มต้น
- English (en), 中文 (cn), 日本語 (ja), 한국어 (ko)
- ພາສາລາວ (lo), မြန်မာဘာသာ (my), ភាសាខ្មែរ (km)
- Tiếng Việt (vi), Bahasa Melayu (ms), Bahasa Indonesia (id), Filipino (fil)


การเลือกภาษาเก็บใน localStorage (user_language) และซิงก์เป็น cookie เพื่อให้ backend ตอบข้อความ/แคตตาล็อกภาษาเดียวกัน ค่าที่ไม่รู้จักจะถูกทำให้เป็นภาษาไทย
1.6การทดสอบการเชื่อมต่อ Backend
หน้าล็อกอินทดสอบการเชื่อมต่อ backend อัตโนมัติ หลังโหลดหน้าประมาณ 0.35 วินาที ผ่าน POST /api/backend/check ผลลัพธ์แสดงที่แถบสถานะมุมล่างซ้าย:
- สำเร็จ — จุดเขียว + “เชื่อมต่อ Server สำเร็จ”
- ล้มเหลว — แบนเนอร์แดงด้านบนการ์ด: “เชื่อมต่อ Backend ไม่ได้” พร้อม URL ที่ใช้ และลิงก์ “ไปตั้งค่า →” พาไป
/settings
Backend URL ปัจจุบันถูกอนุมานจาก origin ของหน้า (runtimeGoApiUrlForOrigin) ผู้ใช้ปกติไม่ต้องตั้งค่าเอง — การแก้ URL ทำที่ “ศูนย์ตั้งค่าระบบ” เท่านั้น

1.7บัญชี Demo (ชุดข้อมูลสาธิต)
ปุ่ม “ทดลองใช้ระบบ (Demo)” เรียก /api/auth/demo-login ซึ่งขอ token จาก backend /demo-login — การเปิดใช้ถูกควบคุมด้วยตัวแปร BCAI_DEMO_LOGIN_ENABLED ฝั่ง backend ทำให้องค์กรที่ไม่ต้องการสามารถปิดได้
| สถานการณ์ | ข้อความที่ผู้ใช้เห็น (จากโค้ด) |
|---|---|
| ปิด Demo / backend ไม่พร้อม | “Demo ยังไม่พร้อมใช้งาน” (503) |
| เซิร์ฟเวอร์นั้นไม่มีบัญชี Demo | “ระบบนี้ไม่ได้เปิดบัญชี Demo” (404) |
| เกินเวลา 15 วินาที | “Server ไม่ตอบกลับทันเวลา” (504) |
| เชื่อมต่อไม่ได้ | “ไม่สามารถเชื่อมต่อ Server ได้” |
| ตอบกลับไม่ครบ token | “Server ตอบกลับไม่ครบ” (502) |
ชุดข้อมูลสาธิตที่พบในเซิร์ฟเวอร์สาธิต: กลุ่มกิจการ “รุ่งเรือง (สาธิต)” รหัส demo — ผู้ใช้ชื่อ demo บทบาท “เจ้าของ” เข้าถึง 3 บริษัท 4 สาขา: บริษัท รุ่งเรืองวัสดุก่อสร้าง จำกัด (C01), บริษัท หอมกรุ่น คอฟฟี่ แอนด์ เบเกอรี่ จำกัด (C02), ห้างหุ้นส่วนจำกัด รุ่งเรืองการค้า (C03)
1.8ข้อมูลที่เก็บในเครื่องผู้ใช้ (localStorage)
| คีย์ | ความหมาย | พฤติกรรม |
|---|---|---|
| user_language | ภาษาที่เลือก | เขียนทุกครั้งที่เปลี่ยนภาษา |
| saved_username | ชื่อผู้ใช้ | เก็บเฉพาะเมื่อติ๊ก “จำชื่อผู้ใช้” ตอนล็อกอินสำเร็จ; ไม่ติ๊ก → ลบ |
| saved_holdingcode | รหัสกลุ่มกิจการ | เก็บเสมอเมื่อล็อกอินพร้อมรหัสกลุ่มกิจการ (ไม่ผูกกับ checkbox) |
| remember_username | สถานะ checkbox | “true”/“false” |
| saved_password, remember_password, backend_url, backend_url_history (คีย์เก่า) | คีย์สมัยก่อน | ถูกลบทิ้งอัตโนมัติทุกครั้งที่โหลดหน้าล็อกอิน (ยุคล้าสมัย — ไม่ใช้แล้ว) |
session หลังล็อกอิน (token, ชื่อผู้ใช้, method, โปรไฟล์, holdingcode) บริหารโดยโมดูล client-auth-session.ts ส่วน refresh token อยู่ใน HttpOnly cookie ซึ่ง JavaScript ของหน้าเว็บอ่านไม่ได้
1.9บัญชีข้อความผิดพลาด (Error Catalog)
| ข้อความที่แสดง | ความหมาย / สาเหตุ | แนวทางจัดการ |
|---|---|---|
| กรุณากรอกชื่อผู้ใช้ | ช่องชื่อผู้ใช้ว่าง | กรอกให้ครบก่อนกดเข้าสู่ระบบ |
| กรุณากรอกรหัสผ่าน | ช่องรหัสผ่านว่าง | กรอกให้ครบก่อนกดเข้าสู่ระบบ |
| holdingcode ต้องใช้ a-z และ 0-9 เท่านั้น ยาว 3-30 ตัว และขึ้นต้นด้วย a-z ห้ามใช้ _ หรือสัญลักษณ์ | รูปแบบรหัสกลุ่มกิจการไม่ผ่าน pattern | ตรวจรหัส: ตัวพิมพ์เล็ก+ตัวเลข ขึ้นต้นตัวอักษร ยาว 3-30 |
| รูปแบบรหัสกลุ่มกิจการไม่ถูกต้อง | กรอกรหัสมาแต่ normalize แล้วว่าง (เช่น มีแต่ช่องว่าง/สัญลักษณ์) | ล้างช่องแล้วพิมพ์ใหม่ |
| เข้าสู่ระบบไม่สำเร็จ (loginFailed) | backend ตอบ “login failed / username or password is invalid” — ชื่อผู้ใช้หรือรหัสผ่านผิด, หรือผู้ใช้ไม่ได้เปิดใช้ | ตรวจรหัสกลุ่มกิจการ-ชื่อผู้ใช้-รหัสผ่าน; ถ้าถูกต้องแล้วให้ผู้ดูแลตรวจสถานะผู้ใช้/รีเซ็ตรหัสผ่าน |
| รูปแบบข้อมูลไม่ถูกต้อง (400) | คำขอไป server ไม่ใช่ JSON ที่ถูกต้อง | รีเฟรชหน้า (F5) แล้วลองใหม่ — พบได้ยาก |
| Backend URL ไม่ถูกต้อง | Backend URL ที่อนุมาน/ตั้งไว้ใช้ไม่ได้ | เปิด /settings ตรวจ URL แล้วกด “ทดสอบ” |
| เชื่อมต่อ Backend ไม่ได้ (แบนเนอร์) | ทดสอบการเชื่อมต่ออัตโนมัติล้มเหลว | ตรวจอินเทอร์เน็ต → F5 → ยังไม่หายให้ผู้ดูแลตรวจสถานะเซิร์ฟเวอร์ |
1.10ความปลอดภัยและแนวปฏิบัติผู้ดูแล
- ไม่เก็บรหัสผ่านบนเครื่องผู้ใช้ — โค้ดปัจจุบันลบคีย์
saved_passwordเดิมทิ้งทุกครั้งที่เปิดหน้าล็อกอิน - Refresh token เป็น HttpOnly cookie — ป้องกัน JavaScript อ่าน (ลดความเสี่ยง XSS)
- Google Sign-In ใช้โหมด popup + ตรวจ credential ฝั่ง server (
/api/auth/google/verify) ไม่เชื่อถือข้อมูลจาก client ตรง ๆ - ตรวจสอบซ้ำสองชั้น — client และ Next.js API route ตรวจ format ก่อนส่งต่อ backend เสมอ
- มีชุดทดสอบความปลอดภัยเฉพาะหน้าจอ —
login-screen.security.test.tsและholding-screen.security.test.ts - สิ่งที่ผู้ดูแลควรสื่อสารกับทีม: ห้ามแชร์บัญชี, ใช้คอมร่วมให้ “ออกจากระบบ” ทุกครั้ง, และรหัสกลุ่มกิจการถือเป็นข้อมูลกึ่งเปิดเผย (ยังต้องมีชื่อผู้ใช้+รหัสผ่านจึงเข้าได้)
1.11มุมมองการใช้งานตามบทบาท
- ควบคุมการเปิด/ปิด Demo ผ่านตัวแปร BCAI_DEMO_LOGIN_ENABLED ที่ backend — ปิดได้ถ้าไม่ต้องการให้พนักงานเข้าชุดข้อมูลสาธิต
- จัดการ Google Login ผ่าน NEXT_PUBLIC_GOOGLE_CLIENT_ID — ถ้าไม่ได้ตั้งค่า ปุ่ม Google จะไม่ถูกวาดออกมา
- ยืนยันตัวตนใช้ Bearer token + refresh token ใน HttpOnly cookie; session ผูกกับ Backend URL ปัจจุบันของหน้า
- ศูนย์ตั้งค่าระบบ (/settings) เก็บ Backend URL และรหัสผ่าน Setup — เข้าถึงได้โดยไม่ต้องล็อกอิน ควรจำกัดให้เฉพาะผู้ดูแล
- การเลือก “กลุ่มกิจการ/บริษัท/สาขา” หลังล็อกอินคือการเลือกขอบเขตข้อมูลบัญชี — บันทึกผิดบริษัทแล้วต้องยกเลิกและลงใหม่ให้ถูกนิติบุคคล
- โครงสร้างหลายบริษัทของระบบช่วยแยกบัญชีตามนิติบุคคล สอดคล้องการจัดทำงบการเงินรายนิติบุคคลตาม พ.ร.บ.การบัญชี พ.ศ. 2543
- รหัสสาขา (เช่น 00000 สำนักงานใหญ่, 00001 สาขาลาดหลุมแก้ว) สัมพันธ์การแยกพื้นที่เสียภาษีสาขาตามประมวลรัษฎากร (ภ.พ.09) — ตั้งให้ตรงสาขาจดแจ้งจริง
- ปุ่ม Demo เหมาะฝึกทีมบัญชีใหม่โดยไม่กระทบข้อมูลจริง
- ออกแบบกลุ่มกิจการ = กลุ่มบริษัทเครือตาม TAS 1 (งบการเงินรวม) เมื่อมีความสัมพันธ์แม่-ลูก; ระบบรองรับหลายบริษัทในหนึ่งกลุ่ม
- รหัสกลุ่มกิจการเปลี่ยนไม่ได้หลังสร้าง — ควรกำหนด naming convention ที่สอดคล้องโครงสร้างนิติบุคคลตั้งแต่ต้น
- การแยกสาขาในระบบรองรับการจัดสรรรายได้/ค่าใช้จ่ายระหว่างสาขาเพื่องบภาษี ภ.ง.ด.50/51 ที่มีสาขา
- สังเกตชุดข้อมูลสาธิต: กลุ่มเดียวมีทั้ง บจ. (C01, C02) และ หจ. (C03) — แนวทางตั้งกลุ่มเครือธุรกิจผสมได้จริง
- ตัวสร้างกลุ่มกิจการคือบัญชี Google แรกของกิจการ — ควรใช้อีเมลบริษัท ไม่ใช่อีเมลส่วนตัวที่คนเดียวถือไว้
- รหัสกลุ่มกิจการคือ “ประตูแรก” ของการล็อกอินพนักงาน — หลุดไปถือว่าคนนอกรู้ชื่อ แต่ยังต้องมีชื่อผู้ใช้+รหัสผ่านจึงเข้าได้
- ผู้เชียวชาญสามารถเพิ่มกลุ่มกิจการใหม่ได้เองจากหน้าเลือกกลุ่มกิจการ (ปุ่ม “เพิ่มกลุ่มกิจการ”) เช่น แยกกิจการใหม่ลองผิดลองถูก
- ล็อกอินด้วยรหัสกลุ่มกิจการ+ชื่อผู้ใช้+รหัสผ่านคือวิธีประจำวัน — ระบบจำรหัสกลุ่มกิจการและ (ถ้าติ๊ก) ชื่อผู้ใช้ไว้ในเครื่อง
- ระบบไม่เก็บรหัสผ่านในเครื่อง (localStorage) — ปลอดภัยแม้ใช้คอมร้านร่วมกัน แต่ควรกดออกจากระบบทุกครั้งที่เลิกงาน
- ลืมรหัสผ่านติดต่อผู้ดูแลระบบของกิจการ — หน้าล็อกอินไม่มีปุ่มรีเซ็ตด้วยตนเอง