Skip to content

12.1 · Auth mechanisms inventory

แพลตฟอร์มนี้ไม่มี auth mechanism เดียว — มีอย่างน้อย 9 รูปแบบที่แตกต่างกัน กระจายอยู่ตามยุคที่เขียนและทีมที่เขียน ไม่มี central identity provider ที่ทุก service ไปตรวจสอบร่วมกัน (ไม่มี JWKS, ไม่มี OAuth authorization server ภายใน)


A. GraphQL @auth directive + apiKey header (legacy core)

ระบบvtrc-api
กลไกGraphQL directive @auth(accessRole: [...], accessMenu: [...]) ที่ resolver level + header apiKey (ไม่ใช่ apiKey field แต่มาจาก API_DEVICE_KEY[].key — ดู Canonical Truths) ที่ map ไปหา APP_TYPE (WEB/MOBILE/WEB_ADMIN)
JWTpayload มีแค่ id, uid — ไม่มี role/scope ใน token เอง role ถูก query ใหม่ทุกครั้งจาก DB ผ่าน getCurrentRoleUser()
จุดอ่อนJWT_SECRET default 'secret' ถ้า env ไม่ตั้ง (SEC-VTRC-API-02), jwt.verify() ไม่ระบุ algorithm explicit (เปิดช่อง algorithm confusion ในทางทฤษฎี), getCurrentRoleUser/checkMenuAuth มี RBAC bug หลายจุด (ดู Volume 3 vtrc-api/03-auth-session)
apiKey เป็น device-level ไม่ใช่ user-levelทุก user ที่ใช้ app ประเภทเดียวกัน (เช่นทุกคนที่เข้า vtrc-web) ส่ง apiKey ตัวเดียวกัน — ไม่ใช่ต่อ user ไม่ใช่ต่อ session ดู 12.2

B. LDAP/Active Directory bind (legacy core, HR identity)

ระบบcu-central-api
กลไกbind ตรงกับ AD ของ HRMI (Chula) ด้วย adUsername/password ผ่าน activeDirectory.connector.js
จุดอ่อนrejectUnauthorized: false (SEC-CU-06) — ไม่ verify TLS certificate ของ LDAP server เปิดช่อง MITM, มี REST route /auth/signinbypass ที่ passwordless (SEC-CU-04), ignoreAuth directive ข้าม auth ได้ถ้ามี apiKey เพียงอย่างเดียว (SEC-CU-05)
ดูรายละเอียดVolume 3 cu-central-api/02-auth-ldap-directive

C. NestJS JWT guard + @Public() override (domain microservices, ส่วนใหญ่ของ Volume 6)

รูปแบบที่ใช้มากที่สุดในทั้งแพลตฟอร์ม — พบใน benefit-api, recruitment-api, pms-api, vtrc-common, sso-api, 2way-api, ส่วนใหญ่ของ chat-center-api

ส่วนประกอบรายละเอียด
Global guardJwtAuthGuard ผูกที่ APP_GUARD ระดับ module — ทุก request ต้องมี valid JWT เป็น default
Overridedecorator @Public() + Reflector เช็คว่า route ไหนยกเว้น
จุดอ่อนร่วมการตัดสินใจว่า endpoint ไหนควร @Public() ทำแบบ ad-hoc ต่อ repo — พบ endpoint ที่ควร private กลับเป็น @Public() ใน benefit-api (wdHospitalsSEC-BENEFIT-03), 2way-api (เกือบทุก write endpoint — SEC-2WAYAPI-04), 2way-line-service (ทุก endpoint — SEC-2WAYLINE-01), vtrc-common (ทุก endpoint จริง — SEC-COMMON-01), pms-api (/budget/getEmployeeSalarySEC-PMS-01), 2way-vtrc-api (POST /2way/getProfileSEC-2WAYVTRC-02), meeting-vtrc-api (/meeting/mockuser/oneSEC-MTVTRC-02)
Copy-paste ไม่ใช่ shared librarylibs/jwt/jwt-auth.guard.ts เหมือนกันไบต์ต่อไบต์ระหว่าง vtrc-common/chat-center-api/job-scheduler-api (QUAL-COMMON-02) — แปลว่า pattern เดียวกันถูก copy ไปทุกที่ รวมถึง bug/design choice เดิมด้วย

pms-api เพิ่ม ThrottlerGuard เป็น global guard ชั้นเดียวที่มีใน Volume 6 (100 req/60s ต่อ IP) — เป็น defense-in-depth ที่ repo อื่นในกลุ่มไม่มี

D. Self-managed JWT ชั้นที่สอง (parallel auth layer)

พบใน recruitment-api, pms-api — มี AuthDataService ที่เรียก jwt.sign()/jwt.verify() ตรงด้วย jsonwebtoken แยกจาก Passport strategy ของ Layer C ข้างบน

  • access token (15 นาที) + refresh token (1 วัน) เก็บลง authData table เพื่อ revoke ได้ (ต่างจาก Layer C ที่ revoke ไม่ได้จนกว่า token expire)
  • recruitment-api เช็ค decodedToken.iss === 'redcross.or.th:vtrc' เพื่อแยกว่า token มาจาก vtrc-api หรือตัวเอง — ใช้ได้เพราะ shared secret (ดู Volume 11.4)

E. Global middleware JWT verify (Express, ไม่มี guard/decorator pattern)

ระบบai-recruitment-api
กลไกmiddleware verifyJWT ผูกกับทุก route แบบ global (Express ไม่ใช่ NestJS) เรียก jwt.verify(token, SECRET_KEY) ตรง
จุดอ่อนไม่มี role/scope check เลยหลังจากยืนยัน signature — token ที่ valid (จาก secret ที่แชร์กับ 4 service) ผ่านได้ทุก endpoint (SEC-AIRECRUIT-02)

F. ไม่มี auth guard เลย (exception เดียวใน Volume 6)

ระบบworkflow-api
กลไกไม่มี JwtAuthGuard ระดับ global หรือ module — อ่าน role ตรงจาก JWT payload โดยไม่ verify signature ก่อน (SEC-WORKFLOW-01)
ผลกระทบใครก็ตามส่ง JWT ที่ decode ได้ (ไม่ต้อง valid signature) พร้อม payload ที่อ้างว่าเป็น role สูง สามารถ impersonate ได้ — ยิ่งอันตรายเพราะ workflow-api มี DB connection ตรงไปยัง 4 database ของ owner อื่น (Volume 11.2)

G. Frontend-to-frontend SSO handoff ผ่าน URL

ระบบรูปแบบ
vtrc-backoffice-newlogin flow ส่ง access token + refresh token เป็น path segment ของ URL ข้าม origin (SEC-BONEW-01) — token หลุดเข้า browser history/server log
vtrc-rc-backofficeSSO bypass ผ่าน /pathLogin/:token/:refreshToken (C3) — รูปแบบเดียวกัน
จุดร่วมทั้งสอง repo ใช้ URL path เป็นช่องทางส่ง credential ข้าม frontend แทน POST body/header — เป็น anti-pattern ที่ปรากฏซ้ำ 2 ครั้งข้าม repo คนละยุค

H. Static/hardcoded backdoor credential (ไม่ใช่ auth mechanism ที่ตั้งใจแต่ทำงานเป็น mechanism จริงในโค้ด)

ระบบรูปแบบ
2way-apiusername/password hardcode สำหรับ login backdoor (SEC-2WAYAPI-01)
2way-vtrc-apistatic master bypass token "testnaja" (SEC-2WAYVTRC-01)
cu-central-api/auth/signinbypass (SEC-CU-04)
centralize-apiทุก route mark @Public() เท่ากับ auth bypass 100% แม้จะมี @Public() decorator แบบ NestJS ทางเทคนิค (CRIT-03)

รายละเอียด trust-boundary ของกลุ่มนี้ ดู 12.2

I. External SSO delegation (ไม่ verify JWT เอง แต่พึ่ง service อื่น)

ระบบdelegate ไปที่
meeting-vtrc-apiเรียก vtrc-api เพื่อ verify token (ไม่มี JWT secret ของตัวเองสำหรับ user-facing auth) — ดู Volume 7 meeting-vtrc-api/03-persistence-integrations
meeting-backofficeเรียก meeting-vtrc-api REST (auth bypass endpoint ตามที่ persistence chapter บันทึกไว้)
sso-apiมี JwtModule.register() ประกาศไว้แต่ JwtService ไม่ถูก inject ไปใช้จริง (CORR-SSO-01) — signIn() ไม่ออก JWT ของตัวเอง endpoint อื่นที่ guard ด้วย JWT จึงใช้งานไม่ได้จริงในทางปฏิบัติ เป็น auth mechanism ที่ "ประกาศไว้แต่ broken"

J. Mobile token storage + payload encryption

AppToken storagePayload encryption
vtrc-mobileAsyncStorageRSA private key hardcode ในซอร์ส/bundle (SEC-MOBILE-01)
new-vtrc-mobileAsyncStorage plaintext (regression จาก vtrc-mobile, SEC-NEW-02)RSA key เดียวกับ vtrc-mobile (SEC-NEW-01)
vtrc-application-frontไม่พบ Critical finding ด้าน auth — ปัญหาหลักคือ IPA binary commit เข้า repo (LEG-FRONT-01)

K. Recruitment/OCR pipeline — OTP + AES encryption ที่มี backdoor

ระบบรูปแบบ
recruitment-webhardcoded OTP bypass (SEC-RCWEB-03) + AES key (REACT_APP_SECRET_KEY) เข้ารหัส PII แต่ key ฝัง bundle (SEC-RCWEB-01)
vtrc-ocr-apiไม่มี auth-related Critical — เป็น mock engine ที่ไม่ได้เชื่อม OCR จริง (QUAL-OCRAPI-01)

ตารางเปรียบเทียบสรุป

Mechanismจำนวน repo ที่ใช้Central verification?Revocable?
GraphQL @auth + apiKey (legacy)1 (vtrc-api)ไม่ — ตรวจ role จาก DB ตรงSession-based, revoke ได้
LDAP/AD bind1 (cu-central-api)ไม่ — bind ตรงกับ ADN/A
NestJS JWT guard + @Public()≥ 7 repoไม่ — secret แชร์กันเองเป็นกลุ่ม ไม่มี JWKSไม่ (จนกว่า token expire) เว้นแต่มี Layer D
Self-managed JWT (AuthDataService)2 (recruitment-api, pms-api)ไม่ได้ (ผ่าน authData table)
Global middleware verify (Express)1 (ai-recruitment-api)ไม่ไม่
ไม่มี guard เลย1 (workflow-api)ไม่มี auth จริงN/A
Frontend SSO ผ่าน URL2 (vtrc-backoffice-new, vtrc-rc-backoffice)ไม่ขึ้นกับ token เอง
Backdoor/static bypass≥ 4 repoN/AN/A
External delegation3 (meeting-vtrc-api, meeting-backoffice, sso-api แบบ broken)ขึ้นกับ upstreamขึ้นกับ upstream

ไม่มี repo ใดใน 25 repo ใช้ OAuth2/OIDC มาตรฐาน, JWKS, หรือ central identity provider ที่ verify ได้จากภายนอกโดยไม่ต้อง copy secret — ทุก "SSO" ที่พบในชื่อไฟล์/ตัวแปร (เช่น sso-api) หมายถึง gateway ไปยัง SSO ภายนอกของสภากาชาด ไม่ใช่ internal SSO ระหว่าง 25 repo นี้เอง


รายละเอียดว่า trust boundary เหล่านี้ blur กันตรงไหนบ้าง ดู 12.2 Shared trust boundaries