#backend#identity#jwt#session#security
JWT session token และการเพิกถอน
backend ออก JWT 3 ชนิดด้วย HS256: preauth, access, refresh (jwt_token_service.go:18-20). access และ refresh ที่ออกพร้อมกันใช้ jti (Session ID) เดียวกัน จึงเพิกถอนทั้งคู่ได้ด้วยการเพิกถอน jti เดียว. ค่ากุญแจตั้งผ่าน env JWT_SIGNING_KEY เท่านั้น (ตัวแปรสภาพแวดล้อมและการโหลด config ของ backend (Environment config)).
ทำงานอย่างไร
- ออก token
IssueSession(:218): สร้างjtiสุ่ม 16 ไบต์ฐานสิบหก (newSessionID,:71-77) แล้วเซ็น access (อายุACCESS_TOKEN_TTL_MINUTES, default 15) และ refresh (REFRESH_TOKEN_TTL_HOURS, default 168 ชม. = 7 วัน) โดยใช้ claims ชุดเดียวกัน (:230-252).IssuePreAuthToken(:81): อายุPRE_AUTH_TOKEN_TTL_MINUTES(default 5) ใช้ได้กับ/auth/select-companyเท่านั้น.- ถ้า
JWT_SIGNING_KEYว่าง ทุกการออก/ตรวจ token ล้มเหลวด้วยErrSigningKeyNotConfigured(fail-closed,:55-58,82,105,219).
- Claims (
:30-40):sub= username,tenantId= Provider Code,dataGroup,companyCode,databaseName,level,type,jti. - ตรวจ token (
parse,:104-124): ต้องเป็น HMAC เท่านั้น (กันalgอื่นหลอก,:111-113), ตรวจลายเซ็น+หมดอายุด้วย validator มาตรฐาน;VerifyAccessToken/VerifyRefreshToken/VerifyPreAuthTokenตรวจtypeตรงชนิด (:134,158,192) — token คนละชนิดใช้แทนกันไม่ได้. access/refresh เช็กIsRevoked(jti)ต่อ (:162-169). - middleware
Auth(middleware/auth.go:39): อ่านAuthorization: Bearer ...→VerifyAccessToken→ ใส่SessionClaimsลง context ให้ handler/RequireRightใช้ (Router, middleware และแผนผัง route ทั้งหมดของ backend (chi router)). - เพิกถอน (
memory_revocation_store.go):Revoke(jti, expiresAt)เก็บใน mapjti → expiresAt;IsRevokedลบรายการที่เลยเวลาแล้วทิ้งเอง (:99-110).NewFileBackedRevocationStore(path)โหลดไฟล์ตอนเริ่มและเขียนกลับด้วย tmp +os.Renameสิทธิ์0o600ทุกครั้งที่เปลี่ยน (:33-84). - ตำแหน่งไฟล์: env
REVOCATION_STORE_FILEหรือ default<CONFIG_DIR>/revoked_sessions.json(di/container.go:79-82).
ตัวอย่างวงจร
| เหตุการณ์ | ผลต่อ jti |
|---|---|
| login สำเร็จ | ออก jti ใหม่ ใช้ร่วมกัน access+refresh |
/auth/refresh |
เพิกถอน jti เก่า แล้วออก jti ใหม่ (ขั้นตอน login / เลือกบริษัท / refresh / logout (Identity login flow)) |
/auth/logout |
เพิกถอน jti → access token เดิมใช้ไม่ได้ทันที |
| เลย TTL | token หมดอายุเอง; รายการเพิกถอนถูกล้างเมื่อเลยเวลาเดิม |
ข้อควรระวัง
- ชื่อ type คือ
InMemoryRevocationStoreแต่เปิดโหมดไฟล์ได้ — ADR-0004 พูดถึงเป็น in-memory ส่วนโค้ดจริงที่diใช้เป็นแบบไฟล์ ให้ยึดโค้ด (ADR-0004 การเพิกถอน session ตอน logout (RevocationStore)). - ใช้ได้กับ instance เดียว: หลาย pod/หลายโปรเซสไม่แชร์ไฟล์ → เพิกถอนที่ตัวหนึ่งไม่มีผลกับอีกตัว (การ build และ deploy backend: Docker, Kubernetes, Makefile (Deployment)).
- ไม่มีการ rotate กุญแจ/
kid; เปลี่ยนJWT_SIGNING_KEY= token เก่าทั้งหมดใช้ไม่ได้ (อนุมานจากโค้ด). - สิทธิ์เมนูไม่อยู่ใน token — โหลดสดทุกคำขอที่ผ่าน
RequireRight(สิทธิ์เมนูและ middleware RequireRight (Menu rights / permission)). - ข้อมูลใน token (เช่น
level) เป็นค่า ณ ตอนออก ไม่อัปเดตจนกว่าจะ refresh.
ที่มาในโค้ด
backend/internal/adapter/outbound/identity/jwt_token_service.go:18-77— ชนิด token, claims, ตัวสร้าง, session id.backend/internal/adapter/outbound/identity/jwt_token_service.go:81-124— ออก pre-auth และparse.backend/internal/adapter/outbound/identity/jwt_token_service.go:126-262— verify +IssueSession+sign.backend/internal/adapter/outbound/identity/memory_revocation_store.go:16-110— store.backend/internal/di/container.go:79-92— เลือก store ไฟล์และประกอบ token service.backend/internal/application/identity/port.go:121— interfaceRevocationStore.
ดูเพิ่ม
ภาพรวม Backend Go ของ SML Ai Account (Backend overview) · ขั้นตอน login / เลือกบริษัท / refresh / logout (Identity login flow) · สิทธิ์เมนูและ middleware RequireRight (Menu rights / permission) · ตัวแปรสภาพแวดล้อมและการโหลด config ของ backend (Environment config) · ADR-0004 การเพิกถอน session ตอน logout (RevocationStore) · สารบัญ ADR ของ backend (Architecture Decision Records)