12.7 · Authentication & Registration
บทนี้เจาะลึก business logic ของการล็อกอิน การสมัครสมาชิก การลืมรหัสผ่าน และการจัดการ session — flow ที่เชื่อม vtrc-api ↔ cu-central-api ↔ LDAP/AD
ภาพรวม
| ด้าน | ค่า |
|---|---|
| ไฟล์ controller | lib/controllers/auth/auth.js (~340 บรรทัด) |
| Verification | lib/controllers/verification/verification.js |
| User mgmt | lib/controllers/user/user.js |
| Repository | lib/repositories/{auth,session,verification}/*.js |
| Forward controller | centralize/controllers/{auth,verification}.js |
| Captcha expire | 5 นาที |
| JWT expire (vtrc-api) | 15 นาที |
| JWT expire (cu-central-api) | 20 นาที |
| Public device token expire | 30 วัน |
1. Login flow — login
ที่อยู่: controllers/auth/auth.js:43
1. Client ส่ง:
- sessionId (lsid), password, clientInfo, apiKey, organization
↓
2. vtrc-api ตรวจ apiKey → map เป็น APP_TYPE (WEB, WEB_ADMIN, MOBILE, ...)
↓
3. checkLoginSession(sessionId) — session ต้อง active (stat=1)
↓
4. signInCD(sessionLogin, password, org) → ส่งต่อไป cu-central-api:
POST /auth/signin { username, password, org, apiKey }
↓
5. cu-central-api ทำ:
a. checkApiKey
b. getEmployeeByEmpCode (MSSQL)
c. bindAdWithUserAndPassword (LDAP)
d. generateSignInToken (JWT cu-central-api)
e. เก็บ session ใน Redis
f. ส่งกลับ { accessToken, refreshToken, userData: { uid, profileKey, sourceDb } }
↓
6. vtrc-api:
a. create() — สร้าง User row ใน MariaDB (ถ้ายังไม่มี)
b. generateToken(userId, accessRole, appType, ...) → JWT vtrc-api
c. setUserDivision — ผูก division
d. เก็บ session ใน MariaDB (Session table)
↓
7. ส่งกลับ client: { accessToken, refreshToken } const newToken = await generateToken(
user.userId,
accessRole,
appType,
null,
result.userData.profileKey,
result.accessToken, // JWT cu-central-api
result.refreshToken, // refreshToken cu-central-api
sessionLogin.loginSession.empCode
);JWT 2 ตัว
เมื่อ login สำเร็จ — vtrc-api เก็บ JWT ของ cu-central-api ไว้ใน Session table เพื่อใช้ตอน forward ภายหลัง
2. Login bypass — mobile PIN
ที่อยู่: auth.js:51 → signInAppBypass (auth.js:136)
สำหรับ mobile app ที่ใช้ PIN แทน password:
1. Client ส่ง:
- authSessionId (UUID)
- signature (RSA-SHA256)
↓
2. ดึง public key จาก AuthSessionMoblie table
↓
3. verify signature
↓
4. ถ้าผ่าน → signInAppBypass → cu-central-api /auth/signinbypass
↓
5. cu-central-api login ไม่ต้องใช้ password// concept — RSA verify
const verify = crypto.verify('sha256', data, publicKey, signature)
if (!verify) throw responError('SIGNATURE_INVALID')3. Registration flow — สมัครสมาชิก
ที่อยู่: controllers/verification/verification.js:10-39
[เริ่มต้น]
│
▼
1. requestVerify(citizenId, birthDate, org, empCode)
│ → ส่งต่อ cu-central-api เพื่อตรวจข้อมูลพนักงาน
│ → ถ้าข้อมูลถูกต้อง → ส่ง verificationCode ไป email/SMS
▼
2. sendVerifyCode (CD)
│ → verificationCode เก็บใน CD Redis
▼
3. requestSendingOTP(phoneNumber)
│ → ส่ง OTP ไปเบอร์มือถือ
│ → OTP เก็บใน CD Redis
▼
4. requestVerifyOTPandSetupPassword(args)
│ → ตรวจ verificationCode + OTP
│ → ถ้าผ่าน:
│ a. create User row ใน vtrc MariaDB
│ b. create Consent row (policy accept)
│ c. setupPasswordUserCD — ส่ง password ไปตั้งใน AD/LDAP
▼
[สมัครเสร็จ — พร้อม login]ที่มา: verification.js:82
4. Forget password flow
ที่อยู่: controllers/user/user.js:126
1. forgetPassword(args)
│ → profileVerificationCheckerCD({ requestFor: "FORGET_PASSWORD", ...})
│ → CD ส่ง verificationCode ไป email/SMS
▼
2. checkSMSforgetPassword(args)
│ → fetchCDProfileVerification — verify code
│ → ถ้าผ่าน → return verificationCode สำหรับใช้ตอน reset
▼
3. resetPassword(args)
│ → changePasswordByVerifyCode (CD)
│ → destroyLoginSession — logout ทุก session
▼
[เปลี่ยนรหัสผ่านเสร็จ]export const resetPassword = async (args, context) => {
const sessionLogin = await checkLoginSession(args.lsid)
if (sessionLogin.loginSession.stat !== 1) {
responError('SESSION_INVALID')
}
const currentUserRole = await getCurrentRoleUserByEmpCode(sessionLogin.loginSession.empCode, ACCESS_BACK_OFFICE)
const setData = {
verificationCode, citizenId, employeeCode, birthDate,
password, confirmPassword, org, remainingCount,
isBackofficeUser: currentUserRole.length <= 0 ? false : true
}
const result = await changePasswordByVerifyCode(setData)
await destroyLoginSession(sessionLogin.loginSessionId)
return result
}5. Session management
signOutSystem
ที่อยู่: auth.js:243
1. signOutCD(accessToken) → cu-central-api /auth/signout
↓
2. destroy vtrc session (MariaDB)
↓
3. destroy user division
↓
4. delete NotificationUserSettings (raw SQL)Multi-device session
vtrc-api แยก session ตาม device:
| Device | ใช้โดย |
|---|---|
WEB | vtrc-web (พนักงาน) |
WEB_ADMIN | vtrc-rc-backoffice (HR) |
MOBILE | mobile app |
user สามารถ login พร้อมกันหลาย device — แต่ละ device มี session แยกกัน
Force logout
ไม่พบ explicit API สำหรับ force logout — มีเพียง forceLoging ซึ่งเป็น logger helper ไม่ใช่ logout
6. Captcha
ที่อยู่: auth.js:304
// auth.js:304-310 (concept)
export const captcha = async (context) => {
const captcha = svgCaptcha.create({ size: 6, noise: 3 })
await captchaSession.create({
captchaSessionId: uuid,
text: captcha.text,
expireDate: moment().add(5, 'minutes').toDate() // expire 5 นาที
})
return captcha.data // SVG
}- Library:
svg-captcha - Expire: 5 นาที
- ใช้ก่อน login (verify ใน checkLoginSession)
7. Domain lists — รายชื่อหน่วยงาน
// concept
export const domainLists = async (context) => {
return await signInDomains() // cu-central-api query
}cu-central-api ส่งรายการ org ที่ login ได้ — เพื่อ frontend แสดงเป็น dropdown ในหน้า login
8. Login session — loginSession table
ก่อน login จริง — client ต้อง verify ตัวเองผ่าน verifyEmpCodeLogin ก่อน เพื่อสร้าง loginSession:
1. verifyEmpCodeLogin(empCode) → สร้าง loginSession (stat=1)
↓
2. login(sessionId, password, ...)
↓
3. ใช้ loginSession.empCode ในการยืนยัน9. JWT generation
ที่อยู่: lib/repositories/auth/auth.js:96
jwt.sign(
{ id: sessionId, uid: userId },
JWT_SECRET,
{ expiresIn: JWT_EXP, issuer: JWT_ISSUER, jwtid }
)
// default: JWT_EXP='15m', JWT_ISSUER='redcross.or.th:vtrc'10. Magic numbers / hardcoded values
| Value | ความหมาย | ที่มา |
|---|---|---|
| 5 นาที | captcha expire | auth.js:307 |
| 15 นาที | JWT vtrc-api expire | env JWT_EXP |
| 20 นาที | JWT cu-central-api expire | env CD |
| 30 วัน | public device token expire | notification.js:113 |
'WEB', 'WEB_ADMIN', 'MOBILE' | APP_TYPE | config.js |
11. Cross-service calls
| vtrc-api function | cu-central-api endpoint | วัตถุประสงค์ |
|---|---|---|
signInCD | POST /auth/signin | LDAP auth |
signInByPass | POST /auth/signinbypass | bypass auth (mobile) |
refreshToken | POST /auth/token | refresh JWT |
signOutCD | POST /auth/signout | revoke session |
profileVerificationCheckerCD | GraphQL | forget password check |
setupPasswordUserCD | GraphQL | ตั้ง password ใน AD |
requestOtpCD | GraphQL | ส่ง OTP |
sendVerifyCodeByCD | GraphQL | ส่ง verificationCode |
fetchCDProfileVerification | GraphQL | verify code |
changePasswordByVerifyCode | GraphQL | เปลี่ยน password |
12. ข้อระวังทั้งหมด
1. signInbypass endpoint ใน CD
เปิดอยู่ใน cu-central-api — ถ้าแก้โค้ดผิด อาจเป็น backdoor
2. Multi-device login
ไม่จำกัดจำนวน device — user สามารถ login พร้อมกันหลายเครื่อง
3. ไม่มี rate limit
ไม่จำกัดครั้งที่ login ผิด — ควรมี (cu-central-api อาจจำกัด แต่ vtrc-api ไม่ได้ทำเอง)
4. LoginSession stat=1 hard-coded
stat=1 หมายถึง active — ถ้าเปลี่ยน enum ต้องแก้ทุกที่
5. Consent (policy accept)
ไม่ได้ track version — ถ้า policy เปลี่ยน user เก่าไม่ต้องยินยอมใหม่
13. Summary — auth domain
| ด้าน | ทำใน vtrc-api | ส่งต่อ cu-central-api |
|---|---|---|
| Login orchestration | ใช่ | — |
| LDAP auth | — | ใช่ |
| JWT issue (vtrc-api) | ใช่ | — |
| JWT issue (cu-central-api) | — | ใช่ |
| Session store | ใช่ (MariaDB) | ใช่ (Redis) |
| Captcha | ใช่ | — |
| Registration | ใช่ (orchestration) | ใช่ (verify, setup password) |
| Forget/reset password | ใช่ (orchestration) | ใช่ (verify, change) |
| Multi-device session | ใช่ | — |