OAuth 2.0 และ JWT Authentication คู่มือครบถ้วน

ในโลกดิจิทัลที่ขับเคลื่อนด้วยแอปพลิเคชันและบริการออนไลน์ การรักษาความปลอดภัยของข้อมูลผู้ใช้และระบบเป็นหัวใจสำคัญอย่างยิ่งครับ การยืนยันตัวตน (Authentication) และการอนุญาต (Authorization) ที่มีประสิทธิภาพ ไม่ใช่แค่ฟีเจอร์เสริม แต่เป็นรากฐานที่ขาดไม่ได้สำหรับความน่าเชื่อถือและการเติบโตของธุรกิจในยุคปัจจุบัน หากคุณเป็นนักพัฒนา, สถาปนิกระบบ, หรือเจ้าของผลิตภัณฑ์ที่ต้องการสร้างระบบที่มีความมั่นคง ปลอดภัย และยืดหยุ่น การทำความเข้าใจ OAuth 2.0 และ JWT (JSON Web Tokens) อย่างลึกซึ้ง จะช่วยให้คุณติดอาวุธด้วยเครื่องมือที่ทรงพลังที่สุดสำหรับการจัดการการเข้าถึงและการยืนยันตัวตนครับ บทความนี้จะพาคุณเจาะลึกทุกแง่มุมของสองเทคโนโลยีนี้ ตั้งแต่หลักการพื้นฐานไปจนถึงการนำไปใช้งานจริง พร้อมตัวอย่างและแนวปฏิบัติที่ดีที่สุด เพื่อให้คุณพร้อมนำไปปรับใช้กับโปรเจกต์ของคุณได้อย่างมั่นใจครับ

ทำความเข้าใจโลกของการยืนยันตัวตนและการอนุญาต

ก่อนที่เราจะดำดิ่งสู่รายละเอียดของ OAuth 2.0 และ JWT สิ่งสำคัญคือเราต้องเข้าใจความแตกต่างและบทบาทของ “การยืนยันตัวตน” และ “การอนุญาต” ในบริบทของระบบดิจิทัลเสียก่อนครับ สองคำนี้มักถูกใช้สลับกันไปมา แต่มีความหมายและการทำงานที่แตกต่างกันอย่างสิ้นเชิง

Authentication (การยืนยันตัวตน) คืออะไร?

Authentication คือกระบวนการพิสูจน์ว่าผู้ใช้งานหรือระบบที่คุณกำลังสื่อสารด้วยเป็น “ตัวจริง” หรือไม่ครับ พูดง่ายๆ คือการตอบคำถามว่า “คุณคือใคร?” โดยทั่วไปแล้ว การยืนยันตัวตนจะทำได้หลายวิธี เช่น:

  • Something you know: เช่น ชื่อผู้ใช้และรหัสผ่าน (Username/Password), PIN.
  • Something you have: เช่น โทรศัพท์มือถือสำหรับ OTP (One-Time Password), บัตรสมาร์ทการ์ด, Physical Key.
  • Something you are: เช่น ลายนิ้วมือ (Fingerprint), สแกนใบหน้า (Face ID), ม่านตา (Retina Scan).

เมื่อผู้ใช้ผ่านกระบวนการยืนยันตัวตนสำเร็จ ระบบก็จะ “รู้จัก” ผู้ใช้นั้น และสามารถดำเนินการในขั้นตอนต่อไปได้ครับ

Authorization (การอนุญาต) คืออะไร?

Authorization คือกระบวนการกำหนดว่าผู้ใช้หรือระบบที่ผ่านการยืนยันตัวตนแล้วนั้น “สามารถทำอะไรได้บ้าง” ครับ มันคือการตอบคำถามว่า “คุณมีสิทธิ์ทำอะไรได้บ้าง?” หรือ “คุณได้รับอนุญาตให้เข้าถึงทรัพยากรใดบ้าง?” ตัวอย่างเช่น:

  • ผู้ดูแลระบบ (Admin) มีสิทธิ์ลบผู้ใช้ได้ แต่ผู้ใช้ทั่วไปไม่มี
  • แอปพลิเคชันรูปภาพอาจมีสิทธิ์เข้าถึงรูปภาพของคุณบน Google Photos แต่ไม่มีสิทธิ์เข้าถึงอีเมลของคุณ
  • ผู้ใช้ A สามารถอ่านเอกสารได้ แต่ไม่สามารถแก้ไขหรือลบได้

Authorization มักจะอิงตามบทบาท (Role-Based Access Control – RBAC) หรือตามนโยบาย (Policy-Based Access Control – PBAC) ที่กำหนดไว้ในระบบครับ

ทำไมการยืนยันตัวตนและการอนุญาตจึงสำคัญ?

ในโลกที่ข้อมูลเป็นทรัพย์สินมีค่า การขาดมาตรการยืนยันตัวตนและการอนุญาตที่เข้มแข็ง อาจนำไปสู่หายนะได้ครับ ลองนึกภาพว่าใครก็สามารถเข้าถึงข้อมูลธนาคาร, อีเมล, หรือข้อมูลสุขภาพของคุณได้เพียงเพราะระบบไม่มีการป้องกันที่เพียงพอ นั่นคือเหตุผลที่สองกระบวนการนี้เป็นพื้นฐานของความปลอดภัยทางไซเบอร์:

  • ปกป้องข้อมูลส่วนตัว: ป้องกันไม่ให้ข้อมูลสำคัญของผู้ใช้รั่วไหลหรือถูกเข้าถึงโดยไม่ได้รับอนุญาต
  • รักษาความสมบูรณ์ของระบบ: ป้องกันการเปลี่ยนแปลงข้อมูลหรือการกระทำที่อาจเป็นอันตรายต่อระบบ
  • สร้างความไว้วางใจ: ผู้ใช้จะมั่นใจในการใช้งานบริการเมื่อรู้ว่าข้อมูลของพวกเขาปลอดภัย
  • ปฏิบัติตามกฎระเบียบ: กฎหมายและข้อบังคับหลายฉบับ เช่น GDPR, PDPA กำหนดให้มีการปกป้องข้อมูลอย่างเข้มงวด

เมื่อเราเข้าใจความแตกต่างและบทบาทของ Authentication และ Authorization แล้ว เราก็พร้อมที่จะเจาะลึกไปที่ OAuth 2.0 และ JWT ซึ่งเป็นเครื่องมือที่เข้ามาช่วยจัดการสองส่วนนี้ได้อย่างมีประสิทธิภาพและทันสมัยครับ

เจาะลึก OAuth 2.0: มาตรฐานสำหรับการอนุญาต

OAuth 2.0 เป็นมาตรฐานที่สำคัญอย่างยิ่งในโลกของการพัฒนาเว็บและโมบายล์แอปพลิเคชันครับ มันเข้ามาช่วยแก้ปัญหาใหญ่ในการอนุญาตให้แอปพลิเคชันหนึ่งเข้าถึงทรัพยากรของผู้ใช้อีกแอปพลิเคชันหนึ่งได้อย่างปลอดภัยโดยไม่ต้องให้ข้อมูลรับรองตัวตนโดยตรง

OAuth 2.0 คืออะไร?

OAuth 2.0 ย่อมาจาก “Open Authorization 2.0” เป็นเฟรมเวิร์กมาตรฐานสำหรับการ Authorization (การอนุญาต) ไม่ใช่สำหรับการ Authentication (การยืนยันตัวตน) นะครับ นี่เป็นความเข้าใจผิดที่พบบ่อยมากๆ ครับ

ลองนึกภาพสถานการณ์นี้ครับ: คุณต้องการใช้แอปพลิเคชันแต่งรูป (Client Application) เพื่อดึงรูปภาพจาก Google Photos (Resource Server) ของคุณ ในอดีต คุณอาจต้องป้อนชื่อผู้ใช้และรหัสผ่าน Google ของคุณเข้าไปในแอปแต่งรูปนั้น ซึ่งเป็นอันตรายอย่างยิ่ง เพราะแอปแต่งรูปจะเข้าถึงข้อมูล Google ของคุณได้ทั้งหมด และคุณไม่มีทางรู้เลยว่าแอปนั้นจะนำข้อมูลรับรองตัวตนของคุณไปทำอะไรบ้าง

OAuth 2.0 เข้ามาแก้ปัญหานี้โดยการอนุญาตให้คุณ “มอบสิทธิ์” (Delegate Authorization) ให้แอปพลิเคชันแต่งรูปเข้าถึงทรัพยากรบางส่วนของคุณบน Google Photos ได้ โดยที่คุณไม่ต้องให้ชื่อผู้ใช้และรหัสผ่าน Google แก่แอปแต่งรูปโดยตรงครับ คุณจะอนุญาตผ่าน Google เอง ซึ่ง Google จะออก “Access Token” ให้แอปแต่งรูปนำไปใช้ในการเข้าถึงรูปภาพได้ตามสิทธิ์ที่คุณกำหนด

หัวใจสำคัญของ OAuth 2.0 คือการมอบสิทธิ์แบบจำกัดขอบเขตและระยะเวลาโดยไม่ต้องเปิดเผยข้อมูลรับรองตัวตน

บทบาทของผู้เกี่ยวข้องใน OAuth 2.0

OAuth 2.0 กำหนดบทบาทหลักๆ ไว้ 4 บทบาท เพื่อให้การทำงานเป็นไปอย่างมีระเบียบและปลอดภัยครับ

  1. Resource Owner (เจ้าของทรัพยากร): คือผู้ใช้ปลายทาง (End-user) ที่เป็นเจ้าของข้อมูลหรือทรัพยากรที่ต้องการให้เข้าถึง เช่น คุณที่เป็นเจ้าของรูปภาพใน Google Photos
  2. Client (ไคลเอนต์): คือแอปพลิเคชันที่ต้องการเข้าถึงทรัพยากรของ Resource Owner เช่น แอปพลิเคชันแต่งรูป, เว็บไซต์, หรือแอปมือถือ Client ต้องลงทะเบียนกับ Authorization Server เพื่อรับ Client ID และ Client Secret
  3. Authorization Server (เซิร์ฟเวอร์การอนุญาต): เป็นเซิร์ฟเวอร์ที่ทำหน้าที่ยืนยันตัวตนของ Resource Owner และออก Access Token ให้กับ Client หลังจากที่ Resource Owner ยินยอมที่จะมอบสิทธิ์ เช่น Google, Facebook, SiamLancard.com
  4. Resource Server (เซิร์ฟเวอร์ทรัพยากร): เป็นเซิร์ฟเวอร์ที่เก็บข้อมูลหรือทรัพยากรที่ Client ต้องการเข้าถึง และจะตรวจสอบ Access Token ที่ Client ส่งมาเพื่อดูว่ามีสิทธิ์เข้าถึงทรัพยากรนั้นหรือไม่ เช่น Google Photos API, Facebook Graph API, API ของ SiamLancard.com

Grant Types (Flows) ของ OAuth 2.0

OAuth 2.0 มี “Grant Types” หรือ “Flows” หลายรูปแบบ ซึ่งกำหนดขั้นตอนการรับ Access Token ที่แตกต่างกันไปตามประเภทของ Client และสถานการณ์การใช้งานครับ

Authorization Code Grant (และ PKCE)

นี่คือ Grant Type ที่นิยมและปลอดภัยที่สุด เหมาะสำหรับ Client ที่สามารถเก็บ Client Secret ได้อย่างปลอดภัย เช่น เว็บแอปพลิเคชันฝั่งเซิร์ฟเวอร์ (Server-side Web Application) และยังเป็นพื้นฐานสำหรับแอปพลิเคชันมือถือและ Single Page Applications (SPAs) ด้วยการเพิ่ม PKCE (Proof Key for Code Exchange) เข้าไปครับ

ขั้นตอนการทำงาน (แบบย่อ):

  1. Client ร้องขอ Authorization: ผู้ใช้คลิกปุ่ม “เข้าสู่ระบบด้วย Google” บน Client
  2. Client Redirect ไปยัง Authorization Server: Client จะพาผู้ใช้ไปยังหน้า Login/Consent ของ Authorization Server พร้อมส่ง client_id, redirect_uri, scope, และ state parameter
  3. Resource Owner ยืนยันตัวตนและให้สิทธิ์: ผู้ใช้ Login และกด “อนุญาต”
  4. Authorization Server ส่ง Authorization Code กลับมา: Authorization Server จะ Redirect ผู้ใช้กลับไปยัง redirect_uri ของ Client พร้อมแนบ authorization_code
  5. Client แลกเปลี่ยน Authorization Code เป็น Access Token: Client (จากฝั่งเซิร์ฟเวอร์) จะส่ง authorization_code, client_id, และ client_secret (ที่เก็บไว้อย่างปลอดภัย) ไปยัง Authorization Server เพื่อขอ access_token และ refresh_token (ถ้ามี)
  6. Authorization Server ออก Token: Authorization Server ตรวจสอบข้อมูลแล้วส่ง access_token และ refresh_token กลับมาให้ Client
  7. Client ใช้ Access Token: Client นำ access_token ไปใช้ในการเรียก Resource Server

PKCE (Proof Key for Code Exchange):

“PKCE is a security extension to the Authorization Code Grant that helps to prevent interception of the authorization code by malicious applications. It is strongly recommended for public clients like mobile apps and SPAs.”

สำหรับ Public Clients (เช่น Mobile Apps, SPAs) ที่ไม่สามารถเก็บ client_secret ได้อย่างปลอดภัย PKCE เข้ามาช่วยเพิ่มความปลอดภัยโดยการสร้าง “code verifier” แบบสุ่มและแฮชเป็น “code challenge” ส่งไปพร้อมกับการร้องขอ authorization code จากนั้นเมื่อ Client ได้ authorization code กลับมา จะต้องส่ง code verifier ตัวจริงไปพร้อมกับการแลกเปลี่ยน code เป็น token ด้วย Authorization Server จะตรวจสอบว่า code verifier ตรงกับ code challenge ที่ส่งมาในตอนแรกหรือไม่ ทำให้แม้ authorization code จะถูกดักจับไป ก็ไม่สามารถนำไปแลก token ได้หากไม่มี code verifier ตัวจริงครับ

Client Credentials Grant

เหมาะสำหรับสถานการณ์ที่ Client ต้องการเข้าถึงทรัพยากรของตัวเอง หรือเข้าถึงทรัพยากรที่ไม่ได้ผูกกับผู้ใช้ปลายทางโดยตรง (Machine-to-machine communication) เช่น บริการหลังบ้านที่ต้องการเรียกใช้ API ของบริการอื่น

ขั้นตอนการทำงาน (แบบย่อ):

  1. Client ร้องขอ Access Token: Client ส่ง client_id และ client_secret ไปยัง Authorization Server โดยตรง
  2. Authorization Server ออก Access Token: Authorization Server ตรวจสอบข้อมูลแล้วส่ง access_token กลับมาให้ Client

ในกรณีนี้ ไม่มี Resource Owner เข้ามาเกี่ยวข้องโดยตรงครับ

Resource Owner Password Credentials Grant (ไม่แนะนำ)

ใน Grant Type นี้ Client จะขอชื่อผู้ใช้และรหัสผ่านจาก Resource Owner โดยตรง แล้วนำไปส่งให้ Authorization Server เพื่อขอ Access Token ซึ่งเป็นอันตรายอย่างยิ่งและไม่แนะนำให้ใช้ในแอปพลิเคชันใหม่ๆ ครับ เพราะ Client จะต้องเข้าถึงข้อมูลรับรองตัวตนของผู้ใช้ ซึ่งขัดกับหลักการพื้นฐานของ OAuth 2.0 ที่ต้องการให้ผู้ใช้ไม่ต้องเปิดเผยรหัสผ่านกับ Client

Implicit Grant (ไม่แนะนำและเลิกใช้แล้ว)

เคยใช้สำหรับ Single Page Applications (SPAs) ที่ไม่สามารถเก็บ Client Secret ได้อย่างปลอดภัย โดย Authorization Server จะส่ง Access Token กลับมาให้ Client โดยตรงผ่าน URL (fragment) หลังจากผู้ใช้ให้สิทธิ์โดยไม่มีขั้นตอนการแลกเปลี่ยน code อย่าง Authorization Code Grant แต่มีช่องโหว่ด้านความปลอดภัยหลายประการ จึงถูกพิจารณาว่า “ไม่ปลอดภัย” และ “เลิกใช้แล้ว” (Deprecated) ครับ โดยถูกแทนที่ด้วย Authorization Code Grant + PKCE แทน

Refresh Token

Access Token มักจะมีอายุสั้น เพื่อจำกัดความเสียหายหากถูกขโมยไป แต่การให้ผู้ใช้ Login ใหม่ทุกครั้งที่ Access Token หมดอายุก็ไม่ใช่ประสบการณ์ที่ดีครับ Refresh Token จึงเข้ามาแก้ปัญหานี้

  • เมื่อ Client ได้รับ Access Token มักจะได้รับ Refresh Token มาพร้อมกันด้วย
  • เมื่อ Access Token หมดอายุ Client สามารถใช้ Refresh Token (ซึ่งมีอายุยาวกว่า) ไปขอ Access Token ใหม่จาก Authorization Server ได้โดยไม่ต้องให้ผู้ใช้ Login ใหม่
  • Refresh Token ควรถูกเก็บไว้อย่างปลอดภัย (เช่น ใน HttpOnly Cookie) และมีการจัดการการ Revoke ที่ดีครับ

องค์ประกอบสำคัญใน OAuth 2.0

  • Client ID: ตัวระบุเฉพาะสำหรับ Client ที่ลงทะเบียนกับ Authorization Server
  • Client Secret: กุญแจลับที่ Client ใช้ในการยืนยันตัวตนกับ Authorization Server (เฉพาะ Confidential Clients)
  • Redirect URI: URL ที่ Authorization Server จะส่ง Authorization Code หรือ Access Token กลับไปหลังจากผู้ใช้ให้สิทธิ์
  • Scope: ขอบเขตของสิทธิ์ที่ Client ต้องการจาก Resource Owner (เช่น read:photos, write:profile, email)
  • Access Token: Token ที่ Client ใช้ในการเข้าถึง Resource Server มีอายุสั้น
  • Refresh Token: Token ที่ Client ใช้ในการขอ Access Token ใหม่เมื่อ Access Token เดิมหมดอายุ มีอายุยาวกว่า
  • State: พารามิเตอร์แบบสุ่มที่ Client ส่งไปในตอนแรกและคาดว่าจะได้รับกลับมา เพื่อป้องกันการโจมตีแบบ CSRF (Cross-Site Request Forgery)

ความปลอดภัยใน OAuth 2.0

ถึงแม้ OAuth 2.0 จะช่วยเพิ่มความปลอดภัย แต่ก็มีข้อควรระวังและแนวทางปฏิบัติที่ดีที่สุดเพื่อให้ระบบของคุณปลอดภัยอย่างแท้จริงครับ

  • ใช้ HTTPS เสมอ: การสื่อสารทั้งหมดระหว่าง Client, Resource Owner, Authorization Server, และ Resource Server ต้องผ่าน HTTPS เพื่อป้องกันการดักจับข้อมูล
  • ใช้ PKCE: สำหรับ Public Clients (Mobile Apps, SPAs) เพื่อป้องกัน Authorization Code Interception Attack
  • ใช้ State Parameter: ป้องกัน CSRF Attack ใน Authorization Code Grant
  • ตรวจสอบ Redirect URI อย่างเข้มงวด: Authorization Server ต้องตรวจสอบว่า redirect_uri ที่ส่งมาตรงกับที่ลงทะเบียนไว้เพื่อป้องกัน Open Redirectors
  • จำกัด Scope: Client ควรร้องขอเฉพาะสิทธิ์ที่จำเป็นเท่านั้น
  • จัดการ Refresh Token อย่างปลอดภัย: เก็บใน HttpOnly Cookie และมีกลไกการ Revoke ที่ดี

ตัวอย่างการทำงานของ OAuth 2.0 (Authorization Code Grant)

ลองดูตัวอย่างการทำงานง่ายๆ ของ OAuth 2.0 ในฝั่ง Client (เว็บแอปพลิเคชัน) ครับ

<!-- ในหน้าเว็บของคุณ -->
<a href="https://your-authorization-server.com/oauth/authorize?
    response_type=code&
    client_id=YOUR_CLIENT_ID&
    redirect_uri=https://your-client-app.com/callback&
    scope=read:profile%20read:email&
    state=YOUR_RANDOM_STATE_STRING">เข้าสู่ระบบด้วยบริการของเรา</a>

จากนั้นเมื่อผู้ใช้คลิกและให้สิทธิ์ Authorization Server จะ Redirect กลับมาที่ https://your-client-app.com/callback พร้อมแนบ code และ state มาใน URL Parameter เช่น:

https://your-client-app.com/callback?code=AUTH_CODE_FROM_SERVER&state=YOUR_RANDOM_STATE_STRING

จากนั้น Backend ของ Client จะนำ code นี้ไปแลกเป็น Access Token ครับ

สำหรับข้อมูลเชิงลึกเกี่ยวกับ PKCE และวิธีนำไปใช้ใน SPA หรือ Mobile App คุณสามารถ อ่านเพิ่มเติมได้ที่นี่ ครับ

JWT (JSON Web Tokens): กุญแจสู่การยืนยันตัวตนแบบไร้สถานะ

หลังจากที่เราเข้าใจ OAuth 2.0 ซึ่งเป็นเฟรมเวิร์กสำหรับการอนุญาตแล้ว คราวนี้เรามาทำความรู้จักกับ JWT ซึ่งเป็นมาตรฐานสำหรับ “การยืนยันตัวตนแบบไร้สถานะ” (Stateless Authentication) ที่มักจะถูกใช้เป็นรูปแบบของ Access Token ใน OAuth 2.0 ครับ

JWT คืออะไร?

JWT ย่อมาจาก “JSON Web Token” เป็นมาตรฐาน (RFC 7519) ที่กำหนดวิธีการสร้างโทเค็นที่ปลอดภัยสำหรับการส่งข้อมูล (Claims) ระหว่างสองฝ่าย โดยข้อมูลนี้สามารถตรวจสอบความถูกต้องได้ (Verify) เพราะมีการลงนามด้วยลายเซ็นดิจิทัล (Digitally Signed) ครับ

ลองนึกภาพว่า JWT คือบัตรประจำตัวอิเล็กทรอนิกส์ที่มีข้อมูลของคุณอยู่ภายใน และบัตรนี้ถูกประทับตราโดยหน่วยงานที่น่าเชื่อถือ คุณสามารถยื่นบัตรนี้ให้ใครก็ได้ที่ต้องการตรวจสอบตัวตนหรือสิทธิ์ของคุณ และพวกเขาสามารถตรวจสอบตราประทับเพื่อยืนยันว่าบัตรนี้เป็นของแท้และข้อมูลข้างในไม่ได้ถูกเปลี่ยนแปลง

จุดเด่นของ JWT คือ “self-contained” หรือ “มีข้อมูลในตัวเอง” ครับ หมายความว่าข้อมูลที่จำเป็นสำหรับการยืนยันตัวตนและการอนุญาต (เช่น ใครคือผู้ใช้, มีสิทธิ์อะไรบ้าง, Token นี้หมดอายุเมื่อไหร่) จะถูกบรรจุอยู่ในตัว Token เอง ทำให้ Resource Server ไม่จำเป็นต้องไปสอบถามฐานข้อมูลทุกครั้งที่ได้รับ Token ซึ่งช่วยลดภาระของเซิร์ฟเวอร์และเพิ่มความเร็วในการตอบสนองได้อย่างมาก

โครงสร้างของ JWT

JWT ประกอบด้วย 3 ส่วนหลักที่คั่นด้วยจุด (.) ครับ คือ:

  1. Header
  2. Payload
  3. Signature

ดังนั้น JWT จะมีรูปแบบหน้าตาประมาณนี้ครับ: aaaaa.bbbbb.ccccc

แต่ละส่วนจะถูกเข้ารหัสด้วย Base64Url-encoded ซึ่งเป็นรูปแบบที่สามารถส่งผ่าน URL ได้อย่างปลอดภัยครับ

Header

Header เป็นส่วนที่ระบุข้อมูลเกี่ยวกับตัว Token เอง เช่น ประเภทของ Token (ในที่นี้คือ JWT) และอัลกอริทึมที่ใช้ในการเข้ารหัสลายเซ็น (Signature)

ตัวอย่าง Header (JSON):

{
  "alg": "HS256",
  "typ": "JWT"
}
  • alg (Algorithm): ระบุอัลกอริทึมที่ใช้สร้าง Signature เช่น HS256 (HMAC with SHA-256), RS256 (RSA with SHA-256)
  • typ (Type): ระบุประเภทของ Token ซึ่งคือ “JWT”

เมื่อเข้ารหัส Base64Url จะได้เป็นส่วนแรกของ JWT ครับ

Payload (Claims)

Payload คือส่วนที่บรรจุ “Claims” หรือชุดข้อมูลที่คุณต้องการส่งไปพร้อมกับ Token ครับ Claims คือข้อมูลเกี่ยวกับ entity (โดยปกติคือผู้ใช้) และข้อมูลเพิ่มเติม (metadata) อื่นๆ

Claims แบ่งออกเป็น 3 ประเภทหลักๆ ครับ:

  1. Reserved Claims: เป็น Claims ที่กำหนดไว้ในมาตรฐาน JWT เพื่อวัตถุประสงค์เฉพาะเจาะจง แต่ไม่จำเป็นต้องใช้ทั้งหมด และสามารถขยายได้ตามต้องการ
    • iss (Issuer): ผู้ที่ออก Token
    • sub (Subject): หัวข้อของ Token, โดยทั่วไปคือ ID ของผู้ใช้
    • aud (Audience): ผู้รับ Token ที่คาดหวัง
    • exp (Expiration Time): เวลาหมดอายุของ Token (เป็น Unix timestamp)
    • nbf (Not Before): เวลาที่ Token จะเริ่มใช้งานได้
    • iat (Issued At): เวลาที่ Token ถูกออก (เป็น Unix timestamp)
    • jti (JWT ID): ตัวระบุเฉพาะของ Token สำหรับป้องกัน Replay Attack
  2. Public Claims: สามารถกำหนดได้เองแต่ควรระบุใน IANA JSON Web Token Registry หรือใช้ URI ที่มี Collision-Resistant
  3. Private Claims: Claims ที่ตกลงกันเองระหว่างผู้ส่งและผู้รับ Token เพื่อส่งข้อมูลเฉพาะทาง เช่น "role": "admin" หรือ "companyId": "siamlancard"

ตัวอย่าง Payload (JSON):

{
  "sub": "1234567890",
  "name": "John Doe",
  "email": "[email protected]",
  "role": "user",
  "iat": 1516239022,
  "exp": 1516242622
}

เมื่อเข้ารหัส Base64Url จะได้เป็นส่วนที่สองของ JWT ครับ

Signature

Signature คือส่วนที่ใช้ในการตรวจสอบความสมบูรณ์ของ Token ครับ มันถูกสร้างขึ้นโดยการนำ Header และ Payload ที่เข้ารหัส Base64Url แล้ว มาเชื่อมต่อกันด้วยจุด (.) แล้วนำไปแฮช (hash) ด้วยอัลกอริทึมที่ระบุใน Header (เช่น HS256) และใช้ “Secret Key” ที่รู้กันเฉพาะฝั่ง Authorization Server (ผู้ออก Token) และ Resource Server (ผู้ตรวจสอบ Token) เท่านั้น

กระบวนการสร้าง Signature (HS256):

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  YOUR_SECRET_KEY
)

Signature นี้มีความสำคัญอย่างยิ่งครับ เพราะ:

  • ยืนยันความถูกต้องของผู้ออก Token: ผู้รับ Token สามารถตรวจสอบ Signature เพื่อยืนยันว่า Token นี้ถูกสร้างขึ้นโดย Authorization Server ที่เชื่อถือได้
  • ป้องกันการแก้ไขข้อมูล: หาก Header หรือ Payload ถูกแก้ไขแม้แต่น้อย Signature จะไม่ตรงกัน ทำให้ผู้รับรู้ได้ทันทีว่า Token นี้ไม่ถูกต้องหรือถูกปลอมแปลง

เมื่อเข้ารหัส Base64Url จะได้เป็นส่วนที่สามของ JWT ครับ

ทำไมต้องใช้ JWT?

JWT มีข้อดีหลายประการที่ทำให้เป็นที่นิยมในการใช้งาน โดยเฉพาะอย่างยิ่งในสถาปัตยกรรมแบบ Microservices และ Single Page Applications:

  • Statelessness (ไร้สถานะ): Resource Server ไม่จำเป็นต้องเก็บ Session State ของผู้ใช้ไว้ ทำให้ scalability ทำได้ง่ายขึ้น ไม่ต้องพึ่งพาฐานข้อมูลเพื่อตรวจสอบสถานะผู้ใช้ทุกครั้งที่ได้รับคำขอ
  • Compactness (ขนาดเล็ก): JWT มีขนาดเล็ก ทำให้สามารถส่งผ่าน HTTP Header หรือ URL ได้อย่างรวดเร็ว
  • Self-contained (มีข้อมูลในตัวเอง): ข้อมูลที่จำเป็นในการยืนยันตัวตนและการอนุญาตถูกบรรจุอยู่ในตัว Token เอง ทำให้ลดจำนวนครั้งในการติดต่อฐานข้อมูล
  • Performance (ประสิทธิภาพ): การตรวจสอบ JWT สามารถทำได้โดย Resource Server เพียงแค่ตรวจสอบ Signature และ Claims โดยไม่ต้องเรียกฐานข้อมูลหรือบริการอื่น ๆ เพิ่มเติม
  • Security (ความปลอดภัย): ด้วยการใช้ลายเซ็นดิจิทัล ทำให้มั่นใจได้ว่าข้อมูลใน Token ไม่ได้ถูกแก้ไขระหว่างทาง

ความปลอดภัยของ JWT

แม้ JWT จะมีความปลอดภัยในตัวเอง แต่ก็มีข้อควรระวังและแนวทางปฏิบัติที่ดีเพื่อให้การใช้งานปลอดภัยที่สุดครับ

  • อย่าใส่ข้อมูลที่ละเอียดอ่อนใน Payload: เนื่องจาก Payload ถูก Base64Url-encoded ซึ่งสามารถถอดรหัสได้ง่าย ข้อมูลที่ละเอียดอ่อน เช่น รหัสผ่าน หรือข้อมูลส่วนตัวที่ไม่ควรเปิดเผย ไม่ควรถูกใส่ใน Payload
  • กำหนดเวลาหมดอายุ (Expiration Time): Access Token ควรมีอายุสั้น เพื่อจำกัดความเสียหายหากถูกขโมยไป
  • ใช้ Refresh Token: สำหรับการขอ Access Token ใหม่เมื่อ Access Token เดิมหมดอายุ เพื่อเพิ่มความสะดวกให้ผู้ใช้โดยไม่ต้อง Login ใหม่
  • ปกป้อง Secret Key: Secret Key ที่ใช้ในการสร้าง Signature ต้องถูกเก็บไว้อย่างปลอดภัยและไม่เปิดเผยต่อสาธารณะ
  • จัดการการ Revoke Token: JWT โดยธรรมชาติเป็น Stateless ทำให้การ Revoke Token ก่อนหมดอายุเป็นเรื่องที่ท้าทาย อาจต้องใช้กลไก Blacklist หรือการใช้ Access Token ที่มีอายุสั้นมากๆ
  • ใช้ HTTPS เสมอ: เพื่อป้องกันการดักจับ Token ระหว่างการส่งข้อมูล

ตัวอย่างการสร้างและตรวจสอบ JWT ด้วย Python

นี่คือตัวอย่างง่ายๆ ของการสร้างและตรวจสอบ JWT โดยใช้ไลบรารี PyJWT ใน Python ครับ

import jwt
import datetime
import time

# Secret Key ที่ใช้ในการลงนาม JWT
SECRET_KEY = "your-very-secret-key-that-no-one-should-know"

# --- สร้าง JWT ---
def create_jwt(user_id, username, role, expiration_minutes=30):
    payload = {
        "sub": str(user_id),
        "name": username,
        "role": role,
        "iat": datetime.datetime.utcnow(),  # Issued At
        "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=expiration_minutes) # Expiration Time
    }
    # สร้าง JWT โดยใช้ HS256 algorithm
    token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
    return token

# --- ตรวจสอบ JWT ---
def verify_jwt(token):
    try:
        # ตรวจสอบ JWT
        decoded_payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
        return decoded_payload
    except jwt.ExpiredSignatureError:
        print("Token หมดอายุแล้ว")
        return None
    except jwt.InvalidTokenError:
        print("Token ไม่ถูกต้อง หรือลายเซ็นไม่ตรง")
        return None

# ตัวอย่างการใช้งาน
if __name__ == "__main__":
    # สร้าง Token สำหรับผู้ใช้ ID 1, ชื่อ "SiamLancard User", บทบาท "admin"
    jwt_token = create_jwt(1, "SiamLancard User", "admin", expiration_minutes=1)
    print(f"Generated JWT: {jwt_token}")

    # ตรวจสอบ Token ทันที
    decoded_data = verify_jwt(jwt_token)
    if decoded_data:
        print(f"\nDecoded Payload: {decoded_data}")
        print(f"User ID: {decoded_data.get('sub')}")
        print(f"Username: {decoded_data.get('name')}")
        print(f"Role: {decoded_data.get('role')}")

    # รอให้ Token หมดอายุ (ทดสอบ)
    print("\nWaiting for token to expire (60 seconds)...")
    time.sleep(60)

    # ตรวจสอบ Token อีกครั้งหลังหมดอายุ
    decoded_data_expired = verify_jwt(jwt_token)
    if not decoded_data_expired:
        print("Token ถูกปฏิเสธตามที่คาดไว้หลังจากหมดอายุ")

จากโค้ดตัวอย่าง คุณจะเห็นว่าการสร้างและตรวจสอบ JWT นั้นค่อนข้างตรงไปตรงมาเมื่อใช้ไลบรารีที่เหมาะสมครับ แต่ความท้าทายที่แท้จริงคือการนำไปผสานรวมกับระบบทั้งหมดและการจัดการความปลอดภัยอย่างรอบด้านครับ

ผสานพลัง: OAuth 2.0 และ JWT ทำงานร่วมกันอย่างไร

เมื่อคุณเข้าใจหลักการของ OAuth 2.0 (การอนุญาต) และ JWT (การยืนยันตัวตนแบบไร้สถานะ) แล้ว คราวนี้เราจะมาดูกันว่าทั้งสองมาตรฐานนี้ถูกนำมาใช้ร่วมกันอย่างไรในแอปพลิเคชันสมัยใหม่ครับ การรวมกันของ OAuth 2.0 และ JWT ถือเป็นรูปแบบการรักษาความปลอดภัยที่ได้รับความนิยมอย่างกว้างขวางและมีประสิทธิภาพสูง

ลำดับการทำงานร่วมกัน

ในสถานการณ์ทั่วไป OAuth 2.0 จะถูกใช้เป็นเฟรมเวิร์กในการจัดการกระบวนการอนุญาตให้ Client เข้าถึงทรัพยากรของผู้ใช้ และ Access Token ที่ Authorization Server ออกให้นั้น มักจะอยู่ในรูปแบบของ JWT ครับ

ลองนึกภาพขั้นตอนการทำงานที่ Client (เช่น เว็บแอปพลิเคชัน) ต้องการเข้าถึงข้อมูลโปรไฟล์ของผู้ใช้จาก Resource Server:

  1. ผู้ใช้ต้องการเข้าสู่ระบบ/ใช้งานบริการ: ผู้ใช้เปิดเว็บแอปพลิเคชันและต้องการเข้าสู่ระบบโดยใช้บัญชีจากผู้ให้บริการภายนอก (เช่น Google, Facebook หรือระบบยืนยันตัวตนขององค์กรเอง)
  2. Client เริ่มกระบวนการ OAuth 2.0: เว็บแอปพลิเคชัน (Client) จะนำผู้ใช้ไปยัง Authorization Server (เช่น Google OAuth endpoint) เพื่อขอสิทธิ์การเข้าถึงข้อมูลบางอย่าง (เช่น email, profile)
  3. ผู้ใช้ยืนยันตัวตนและให้สิทธิ์: ผู้ใช้ Login ที่ Authorization Server และให้ความยินยอมที่จะให้สิทธิ์แก่เว็บแอปพลิเคชันในการเข้าถึงข้อมูลตามขอบเขตที่ร้องขอ
  4. Authorization Server ส่ง Authorization Code กลับมา: หากผู้ใช้ให้สิทธิ์ Authorization Server จะ Redirect ผู้ใช้กลับไปยังเว็บแอปพลิเคชัน (Client) พร้อมกับ authorization_code
  5. Client แลกเปลี่ยน Code เป็น Token: เว็บแอปพลิเคชัน (ฝั่ง Backend) จะนำ authorization_code ที่ได้รับมา ส่งไปยัง Authorization Server อีกครั้ง พร้อมกับ client_id และ client_secret เพื่อแลกเปลี่ยนเป็น Access Token และ Refresh Token
  6. Authorization Server ออก JWT (Access Token) และ Refresh Token: Authorization Server จะทำการตรวจสอบข้อมูล หากถูกต้อง ก็จะสร้างและส่ง JWT (เป็น Access Token) และ Refresh Token กลับมาให้เว็บแอปพลิเคชัน
    • JWT Access Token: มีข้อมูลเกี่ยวกับผู้ใช้ (เช่น User ID, Role, Expire Time) และถูกลงนาม (Signed) โดย Authorization Server
    • Refresh Token: ใช้สำหรับขอ Access Token ใหม่เมื่อ JWT Access Token เดิมหมดอายุ
  7. Client ใช้ JWT ในการเรียก Resource Server: เว็บแอปพลิเคชันจะเก็บ JWT Access Token ไว้ (มักจะเก็บในหน่วยความจำหรือ Local Storage บน Frontend และส่งมาใน Header Authorization: Bearer <JWT> ในทุกๆ คำขอไปยัง Resource Server
  8. Resource Server ตรวจสอบ JWT: เมื่อ Resource Server ได้รับคำขอพร้อม JWT Access Token ก็จะดำเนินการดังนี้:
    • ตรวจสอบลายเซ็น (Signature): เพื่อยืนยันว่า JWT ไม่ได้ถูกแก้ไขและถูกออกโดย Authorization Server ที่เชื่อถือได้
    • ตรวจสอบเวลาหมดอายุ (Expiration Time): เพื่อให้แน่ใจว่า Token ยังไม่หมดอายุ
    • อ่านข้อมูล (Claims) ใน Payload: เพื่อทราบว่าผู้ใช้คนนี้คือใคร และมีสิทธิ์อะไรบ้าง
  9. Resource Server คืนข้อมูล: หาก JWT ถูกต้องและผู้ใช้มีสิทธิ์ Resource Server ก็จะดำเนินการตามคำขอและส่งข้อมูลกลับไปยัง Client
  10. การต่ออายุ Token: เมื่อ JWT Access Token หมดอายุ Client จะใช้ Refresh Token ที่เก็บไว้อย่างปลอดภัย (เช่นใน HttpOnly Cookie) ไปขอ Access Token ใหม่จาก Authorization Server โดยไม่ต้องให้ผู้ใช้ Login ใหม่

ข้อดีของการรวม OAuth 2.0 และ JWT

การรวมกันของสองมาตรฐานนี้สร้างระบบการจัดการการเข้าถึงที่แข็งแกร่งและยืดหยุ่นครับ

  • การอนุญาตแบบ Delegated Authorization ที่ปลอดภัย (จาก OAuth 2.0): ผู้ใช้ไม่ต้องเปิดเผยข้อมูลรับรองตัวตนโดยตรงกับ Client
  • การยืนยันตัวตนแบบไร้สถานะ (Stateless Authentication) ที่มีประสิทธิภาพ (จาก JWT): Resource Server ไม่ต้องเก็บสถานะเซสชัน ช่วยให้ระบบสามารถปรับขนาด (Scalable) ได้ง่ายขึ้นและตอบสนองได้เร็วขึ้น
  • ลดภาระการตรวจสอบ: Resource Server สามารถตรวจสอบ JWT ได้ด้วยตัวเองโดยไม่ต้องไปสอบถาม Authorization Server หรือฐานข้อมูลทุกครั้ง ทำให้ลด Latency และลดภาระงานของ Authorization Server
  • ความยืดหยุ่น: JWT สามารถบรรจุ Claims ที่หลากหลาย ทำให้ง่ายต่อการจัดการสิทธิ์และข้อมูลผู้ใช้
  • การทำงานร่วมกับ Microservices: เหมาะสำหรับสถาปัตยกรรม Microservices ที่มี Resource Server หลายตัว เพราะแต่ละ Microservice สามารถตรวจสอบ JWT ด้วย Secret Key หรือ Public Key เดียวกันได้โดยไม่ต้องสื่อสารกับกันเองหรือกับ Authorization Server ตลอดเวลา

ด้วยความสามารถเหล่านี้ การใช้ OAuth 2.0 โดยมี JWT เป็น Access Token จึงกลายเป็นสถาปัตยกรรมมาตรฐานสำหรับการสร้างระบบยืนยันตัวตนและการอนุญาตที่มีความปลอดภัยและประสิทธิภาพสูงในปัจจุบันครับ การทำความเข้าใจในรายละเอียดของกระบวนการนี้เป็นสิ่งสำคัญสำหรับนักพัฒนาทุกคนที่ต้องการสร้างแอปพลิเคชันที่แข็งแกร่งและเชื่อถือได้

ตัวอย่างการใช้งานจริง: การติดตั้งและโครงสร้าง

เพื่อช่วยให้คุณเห็นภาพการนำ OAuth 2.0 และ JWT ไปใช้งานจริง เราจะมาดูโครงสร้างพื้นฐานและขั้นตอนที่เกี่ยวข้องในการสร้างระบบที่มีการยืนยันตัวตนและการอนุญาตที่แข็งแกร่งครับ

สถาปัตยกรรมทั่วไป

สถาปัตยกรรมที่ใช้ OAuth 2.0 และ JWT มักจะประกอบด้วยส่วนประกอบหลักๆ ดังนี้ครับ:

  1. Frontend Application (Client):
    • อาจเป็น Single Page Application (SPA) ที่สร้างด้วย React, Angular, Vue.js หรือ Mobile Application (iOS/Android)
    • ทำหน้าที่แสดง UI, รวบรวมข้อมูล, และส่งคำขอไปยัง Backend API
    • เริ่มต้นกระบวนการ OAuth 2.0 เพื่อขอ Access Token
    • จัดเก็บ Access Token (JWT) และ Refresh Token (หากมี)
    • แนบ Access Token ไปกับทุกคำขอที่ส่งไปยัง Backend API
  2. Backend API (Resource Server):
    • เป็น API ที่ให้บริการข้อมูลและฟังก์ชันการทำงานต่างๆ (เช่น RESTful API, GraphQL API)
    • อาจสร้างด้วย Node.js (Express), Python (Django/Flask), Java (Spring Boot), PHP (Laravel), Go เป็นต้น
    • ทำหน้าที่ตรวจสอบ JWT ในทุกคำขอที่เข้ามา (Validate Signature, Check Expiration, Read Claims)
    • ตัดสินใจว่าจะอนุญาตให้ผู้ใช้เข้าถึงทรัพยากรที่ร้องขอหรือไม่ โดยอิงจาก Claims ใน JWT
    • จัดการธุรกิจเชิงตรรกะ (Business Logic) และสื่อสารกับฐานข้อมูล
  3. Authorization Server (Identity Provider):
    • ทำหน้าที่เป็นศูนย์กลางในการจัดการการยืนยันตัวตนและการอนุญาต
    • อาจเป็นบริการสำเร็จรูป (เช่น Auth0, Okta, Keycloak, Firebase Auth) หรือระบบที่พัฒนาขึ้นเอง
    • ยืนยันตัวตนของผู้ใช้ (ผ่าน Username/Password, Social Login ฯลฯ)
    • ออก Authorization Code
    • ออก JWT (Access Token) และ Refresh Token
    • จัดการ Client ID, Client Secret, Redirect URI ของ Client Applications
  4. Database:
    • เก็บข้อมูลผู้ใช้, ข้อมูล Application, และข้อมูลธุรกิจอื่นๆ
    • อาจมีฐานข้อมูลแยกสำหรับ Authorization Server เพื่อเก็บข้อมูลผู้ใช้และ Client Registrations

ในสถาปัตยกรรมนี้ Frontend ไม่ได้ติดต่อกับ Authorization Server โดยตรงในขั้นตอนการแลกเปลี่ยน Code เป็น Token เพื่อรักษาความปลอดภัยของ Client Secret (ถ้ามี) แต่จะให้ Backend API เป็นผู้จัดการในส่วนนี้ครับ

ขั้นตอนการใช้งานจริง (Authorization Code + PKCE)

เราจะเน้นไปที่ Authorization Code Grant with PKCE ซึ่งเป็นแนวทางที่แนะนำสำหรับ Public Clients (SPA, Mobile Apps) ครับ

  1. การลงทะเบียน Client:
    • คุณต้องลงทะเบียน Frontend Application ของคุณ (เช่น SPA ของ SiamLancard.com) กับ Authorization Server
    • คุณจะได้รับ Client ID และกำหนด Redirect URI ที่อนุญาต
    • เนื่องจากเป็น Public Client จึงไม่จำเป็นต้องมี Client Secret (แต่ใช้ PKCE แทน)
  2. เริ่มต้นการเข้าสู่ระบบ (Frontend):
    • ผู้ใช้คลิกปุ่ม “เข้าสู่ระบบ” บน Frontend ของคุณ
    • Frontend จะสร้าง code_verifier (สตริงสุ่ม) และ code_challenge (แฮชของ code_verifier)
    • Frontend จะ Redirect ผู้ใช้ไปยัง Authorization Server พร้อมพารามิเตอร์เช่น client_id, redirect_uri, scope, state, code_challenge, และ code_challenge_method (S256)
  3. การยืนยันตัวตนและการให้สิทธิ์ (Authorization Server):
    • Authorization Server แสดงหน้า Login ให้ผู้ใช้
    • ผู้ใช้ Login และกดยินยอมให้สิทธิ์ (Consent)
    • Authorization Server ตรวจสอบ client_id และ redirect_uri
    • หากถูกต้อง Authorization Server จะ Redirect ผู้ใช้กลับไปยัง redirect_uri ของ Frontend พร้อม authorization_code และ state
  4. การแลกเปลี่ยน Code เป็น Token (Frontend -> Backend -> Authorization Server):
    • Frontend รับ authorization_code และ state มา
    • Frontend ส่ง authorization_code และ code_verifier (ที่เก็บไว้ก่อนหน้านี้) ไปยัง Backend API ของคุณ
    • Backend API ของคุณจะส่งคำขอ POST ไปยัง Token Endpoint ของ Authorization Server พร้อม authorization_code, client_id, redirect_uri, code_verifier และ grant_type=authorization_code
    • Authorization Server ตรวจสอบความถูกต้องของ authorization_code และ PKCE โดยใช้ code_verifier
    • หากถูกต้อง Authorization Server จะออก JWT (Access Token) และ Refresh Token กลับมาให้ Backend API ของคุณ
    • Backend API ส่ง JWT (Access Token) และ Refresh Token กลับไปยัง Frontend ของคุณ (มักจะอยู่ใน Body ของ Response)
  5. การจัดเก็บ Token และการเรียกใช้ API (Frontend):
    • Frontend จัดเก็บ JWT Access Token (ในหน่วยความจำ หรือ Local Storage) และ Refresh Token (ใน HttpOnly Cookie หรือหน่วยความจำ)
    • ทุกครั้งที่ Frontend ต้องการเรียก Backend API จะแนบ JWT Access Token ไปใน HTTP Header: Authorization: Bearer <YOUR_JWT_TOKEN>
  6. การตรวจสอบ Token และการอนุญาต (Backend API – Resource Server):
    • เมื่อ Backend API ได้รับคำขอพร้อม JWT Access Token จะมี Middleware หรือ Filter ที่ทำหน้าที่:
      • ดึง JWT ออกมาจาก Header
      • ตรวจสอบลายเซ็นของ JWT โดยใช้ Public Key ของ Authorization Server (หากใช้ RS256) หรือ Secret Key (หากใช้ HS256)
      • ตรวจสอบเวลาหมดอายุ (exp claim)
      • ตรวจสอบ Claims อื่นๆ เช่น issuer (iss), audience (aud)
      • หาก JWT ถูกต้อง จะอ่าน Claims (เช่น User ID, Role, Scope) และนำข้อมูลเหล่านี้ไปใช้ในการตัดสินใจว่าผู้ใช้มีสิทธิ์เข้าถึงทรัพยากรที่ร้องขอหรือไม่
    • หากการตรวจสอบล้มเหลว จะส่ง HTTP 401 Unauthorized หรือ 403 Forbidden กลับไป
    • หากการตรวจสอบสำเร็จ คำขอจะถูกส่งต่อไปยัง Business Logic
  7. การต่ออายุ Access Token (Frontend -> Backend -> Authorization Server):
    • เมื่อ JWT Access Token หมดอายุ Frontend จะใช้ Refresh Token (ส่งผ่าน Backend หรือเรียก Authorization Server โดยตรงขึ้นอยู่กับการออกแบบ) เพื่อขอ Access Token ใหม่จาก Authorization Server
    • Authorization Server ตรวจสอบ Refresh Token และออก JWT Access Token ใหม่ให้

ตัวอย่าง Code Snippet (แนวคิด)

นี่คือตัวอย่างแนวคิดของโค้ดในส่วนต่างๆ ที่เกี่ยวข้องครับ

Client-side (Frontend) การเรียกใช้ API ด้วย JWT

สมมติว่าคุณมี JWT เก็บไว้ในตัวแปร accessToken

// ตัวอย่าง JavaScript (Fetch API) ใน Frontend
async function fetchProtectedData() {
    const accessToken = localStorage.getItem('accessToken'); // หรือจาก state management
    if (!accessToken) {
        console.error('No access token found. Please log in.');
        // Redirect to login page or initiate OAuth flow
        return;
    }

    try {
        const response = await fetch('https://api.siamlancard.com/protected-resource', {
            method: 'GET',
            headers: {
                'Authorization': `Bearer ${accessToken}`,
                'Content-Type': 'application/json'
            }
        });

        if (response.ok) {
            const data = await response.json();
            console.log('Protected data:', data);
        } else if (response.status === 401) {
            console.error('Unauthorized. Token might be expired or invalid.');
            // Implement token refresh logic here
            await refreshAccessToken(); // สมมติว่ามีฟังก์ชันนี้
            // Retry the request
        } else {
            console.error('Failed to fetch data:', response.statusText);
        }
    } catch (error) {
        console.error('Network error:', error);
    }
}

// ฟังก์ชันสมมติสำหรับการรีเฟรชโทเค็น
async function refreshAccessToken() {
    console.log('Attempting to refresh access token...');
    // ในความเป็นจริง การรีเฟรชมักจะทำผ่าน Backend เพื่อความปลอดภัยของ Refresh Token
    // หรือถ้าทำบน Frontend ต้องใช้ HttpOnly Cookie สำหรับ Refresh Token
    const refreshToken = getRefreshTokenFromCookie(); // สมมติว่ามีฟังก์ชันนี้
    if (!refreshToken) {
        console.error('No refresh token found. Full re-login required.');
        // Redirect to login
        return;
    }

    try {
        const response = await fetch('https://your-authorization-server.com/oauth/token', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            body: new URLSearchParams({
                'grant_type': 'refresh_token',
                'refresh_token': refreshToken,
                'client_id': 'YOUR_CLIENT_ID'
            })
        });

        if (response.ok) {
            const data = await response.json();
            localStorage.setItem('accessToken', data.access_token);
            // Optionally update refresh token if a new one is returned
            console.log('Access token refreshed successfully!');
            return true;
        } else {
            console.error('Failed to refresh token:', response.statusText);
            // If refresh token fails, force re-login
            // clearTokensAndRedirectToLogin();
            return false;
        }
    } catch (error) {
        console.error('Error during token refresh:', error);
        return false;
    }
}

Server-side (Backend) การตรวจสอบ JWT

ตัวอย่างการใช้ Middleware ใน Node.js (Express) พร้อมไลบรารี jsonwebtoken

// ตัวอย่าง Node.js (Express) Backend API
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
const PORT = 3000;

// Secret Key ที่ Authorization Server ใช้ลงนาม JWT
// ใน Production ควรโหลดมาจาก Environment Variables หรือ Key Management System
const JWT_SECRET = 'your-very-secret-key-that-no-one-should-know'; // ควรเป็น Public Key หาก Authorization Server ใช้ RS256

// Middleware สำหรับตรวจสอบ JWT
function authenticateToken(req, res, next) {
    const authHeader = req.headers['authorization'];
    const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN

    if (token == null) return res.sendStatus(401); // ถ้าไม่มี token

    jwt.verify(token, JWT_SECRET, (err, user) => {
        if (err) {
            console.error("JWT Verification Error:", err.message);
            return res.sendStatus(403); // ถ้า token ไม่ถูกต้อง หรือหมดอายุ
        }
        req.user = user; // เก็บข้อมูล user จาก JWT payload ไว้ใน request object
        next(); // ไปยัง route handler ถัดไป
    });
}

// Protected Route ที่ต้องใช้ JWT
app.get('/protected-resource', authenticateToken, (req, res) => {
    // req.user จะมีข้อมูลจาก JWT payload เช่น { sub: '1234567890', name: 'John Doe', role: 'admin', ... }
    res.json({
        message: 'คุณเข้าถึงข้อมูลที่ได้รับการป้องกันแล้ว!',
        user: req.user,
        data: {
            id: 1,
            title: 'บทความ OAuth 2.0 & JWT',
            content: 'เนื้อหาสำหรับผู้ใช้ที่ได้รับการยืนยันตัวตนแล้ว'
        }
    });
});

app.listen(PORT, () => {
    console.log(`Backend API listening at http://localhost:${PORT}`);
});

จากตัวอย่างโค้ดเหล่านี้ คุณจะเห็นว่า JWT Access Token ถูกส่งจาก Frontend ไปยัง Backend ผ่าน Authorization Header และ Backend ใช้ Middleware ในการตรวจสอบ Token ก่อนที่จะดำเนินการตามคำขอครับ การแยกส่วน Frontend, Backend, และ Authorization Server ออกจากกันอย่างชัดเจน ช่วยให้ระบบมีความยืดหยุ่นและปรับขนาดได้ง่ายขึ้นครับ

สำหรับโปรเจกต์ขนาดใหญ่ การจัดการ Secret Key หรือ Public Key สำหรับ JWT ควรใช้บริการ Key Management System (KMS) หรือ Environment Variables ที่ปลอดภัย เพื่อหลีกเลี่ยงการ hardcode key ในโค้ดครับ

ข้อควรพิจารณาและแนวทางปฏิบัติที่ดีที่สุด

การนำ OAuth 2.0 และ JWT ไปใช้งานอย่างมีประสิทธิภาพและปลอดภัยนั้น ไม่ใช่แค่การเขียนโค้ดให้ทำงานได้ครับ แต่ยังต้องคำนึงถึงแนวทางปฏิบัติที่ดีที่สุดและข้อควรพิจารณาต่างๆ เพื่อป้องกันช่องโหว่และเพิ่มความแข็งแกร่งให้กับระบบของคุณครับ

การจัดการ Token Expiration และ Refresh Token

  • Access Token ควรมีอายุสั้น: เพื่อลดความเสี่ยงหากถูกขโมยไป (เช่น 5-30 นาที)
  • ใช้ Refresh Token: เมื่อ Access Token หมดอายุ Client ควรใช้ Refresh Token เพื่อขอ Access Token ใหม่โดยไม่ต้องให้ผู้ใช้ Login ใหม่
  • Refresh Token ควรมีอายุยาวกว่า: (เช่น หลายวันหรือหลายสัปดาห์) แต่ควรมีกลไกการ Revoke ที่ดี
  • หมุนเวียน Refresh Token (Rotation): ทุกครั้งที่ Refresh Token ถูกใช้เพื่อขอ Access Token ใหม่ ควรกำหนดให้ Authorization Server ออก Refresh Token ตัวใหม่มาด้วย และ Revoke ตัวเก่าทิ้ง เพื่อลดความเสี่ยงจากการถูกดักจับ Refresh Token

การจัดเก็บ Token อย่างปลอดภัย

  • Access Token (JWT):
    • Single Page Applications (SPAs): ควรเก็บใน หน่วยความจำ (In-memory) และส่งผ่าน HTTP Header ในแต่ละคำขอ การเก็บใน Local Storage หรือ Session Storage มีความเสี่ยงต่อ XSS (Cross-Site Scripting) Attacks
    • Mobile Applications: ควรเก็บใน Keychain (iOS) หรือ Keystore (Android)
  • Refresh Token:
    • ควรเก็บใน HttpOnly, Secure Cookie ครับ การตั้งค่า HttpOnly จะป้องกัน JavaScript ไม่ให้เข้าถึง Token ได้ ทำให้ปลอดภัยจาก XSS
    • การตั้งค่า Secure จะทำให้ Cookie ถูกส่งผ่าน HTTPS เท่านั้น
    • ในกรณีของ Mobile App อาจเก็บใน Keychain/Keystore เช่นเดียวกับ Access Token

“ไม่ควรเก็บ Access Token หรือ Refresh Token ใน Local Storage ของเบราว์เซอร์สำหรับ SPAs เนื่องจากมีความเสี่ยงสูงต่อการโจมตีแบบ XSS”

การตรวจสอบ Scope และสิทธิ์

  • จำกัด Scope ที่ร้องขอ: Client ควรสร้องขอ Scope (สิทธิ์) เท่าที่จำเป็นสำหรับการทำงานของแอปพลิเคชันเท่านั้น
  • Resource Server ต้องตรวจสอบ Scope: นอกจากตรวจสอบความถูกต้องของ JWT แล้ว Resource Server ยังต้องตรวจสอบด้วยว่า JWT ที่ได้รับมานั้นมี Scope หรือสิทธิ์เพียงพอที่จะเข้าถึงทรัพยากรที่ร้องขอหรือไม่
  • ใช้ Claims ใน JWT สำหรับ RBAC/PBAC: สามารถใส่ Claims เกี่ยวกับบทบาท (role) หรือสิทธิ์เฉพาะ (permissions) ลงใน JWT เพื่อให้ Resource Server ใช้ในการตัดสินใจด้าน Authorization ได้อย่างรวดเร็ว

การใช้งาน HTTPS/SSL เสมอ

การสื่อสารทั้งหมดระหว่าง Client, Authorization Server, และ Resource Server จะต้องผ่าน HTTPS (TLS/SSL) เสมอ เพื่อป้องกันการดักจับข้อมูล (Man-in-the-Middle Attack) ซึ่งรวมถึง Token ต่างๆ ด้วยครับ

การจัดการการ Revoke Token

การ Revoke Token ก่อนหมดอายุเป็นความท้าทายของระบบที่ใช้ JWT แบบ Stateless ครับ

  • Revoke Refresh Token: เมื่อผู้ใช้ออกจากระบบ (Logout) หรือเปลี่ยนรหัสผ่าน ควร Revoke Refresh Token ทันที เพื่อให้ Access Token ที่หมดอายุไปแล้วไม่สามารถถูกต่ออายุได้อีก
  • Blacklist Access Token (สำหรับกรณีฉุกเฉิน): หากมี Access Token ถูกขโมยและจำเป็นต้อง Revoke ทันที (ก่อนหมดอายุ) คุณอาจต้องใช้กลไก Blacklist ที่เก็บรายการ JWT ที่ถูก Revoke ไว้ใน Database หรือ Cache (เช่น Redis) ซึ่งจะเพิ่มภาระให้กับ Resource Server แต่จำเป็นในบางสถานการณ์
  • ลดอายุ Access Token: หากคุณต้องการความสามารถในการ Revoke ทันที แต่ไม่ต้องการใช้ Blacklist คุณสามารถลดอายุ Access Token ให้สั้นลงมากๆ (เช่น 1-5 นาที) และใช้ Refresh Token บ่อยขึ้น

PKCE สำหรับ Public Clients

สำหรับ Mobile Apps และ Single Page Applications (SPAs) ที่ไม่สามารถเก็บ Client Secret ได้อย่างปลอดภัย PKCE (Proof Key for Code Exchange) เป็นสิ่งจำเป็นอย่างยิ่งในการใช้งาน Authorization Code Grant เพื่อป้องกันการโจมตีที่ Authorization Code ถูกดักจับไป

การลงนามด้วย Public/Private Key (RS256)

หากคุณมี Authorization Server ที่แยกออกจาก Resource Server หลายตัว การใช้อัลกอริทึมแบบ Asymmetric (เช่น RS256) จะปลอดภัยและจัดการง่ายกว่าครับ

  • Authorization Server: ใช้ Private Key ในการลงนาม JWT
  • Resource Server: ใช้ Public Key ที่สอดคล้องกันในการตรวจสอบลายเซ็น JWT

วิธีนี้ทำให้ Resource Server ไม่จำเป็นต้องเข้าถึง Secret Key ของ Authorization Server และสามารถตรวจสอบ JWT ได้โดยไม่ต้องสื่อสารกับ Authorization Server ครับ Public Key สามารถเผยแพร่ผ่าน JWKS (JSON Web Key Set) Endpoint ได้

การปฏิบัติตามแนวทางเหล่านี้ จะช่วยให้คุณสร้างระบบที่ใช้ OAuth 2.0 และ JWT ได้อย่างมั่นคง ปลอดภัย และมีความน่าเชื่อถือสูงครับ

ตารางเปรียบเทียบ: OAuth 2.0 vs. JWT

เพื่อให้เห็นภาพความแตกต่างและบทบาทของ OAuth 2.0 และ JWT ชัดเจนยิ่งขึ้น ลองดูตารางเปรียบเทียบนี้ครับ

คุณสมบัติ OAuth 2.0 JWT (JSON Web Token)
วัตถุประสงค์หลัก เฟรมเวิร์กสำหรับ Authorization (การอนุญาต) แบบมอบสิทธิ์ (Delegated Authorization) มาตรฐานสำหรับการ ยืนยันตัวตน (Authentication) และการส่งข้อมูล (Claims) แบบไร้สถานะ
ประเภท Authorization Framework Token Format (Standard)
ตอบคำถาม “Client มีสิทธิ์เข้าถึงทรัพยากรของผู้ใช้ได้หรือไม่?” “คุณคือใคร และข้อมูลนี้ถูกต้องตามที่ผู้ส่งอ้างหรือไม่?”
บทบาท กำหนดกระบวนการสื่อสารระหว่าง Resource Owner, Client, Authorization Server, Resource Server เป็นรูปแบบของ Access Token ที่ถูกส่งระหว่าง Client และ Resource Server
การมีสถานะ (Statefulness) กระบวนการ (Flow) มีสถานะที่ต้องติดตาม (เช่น Authorization Code) เป็น Stateless (ไร้สถานะ) ในตัวของมันเอง ข้อมูลอยู่ใน Token
สิ่งที่ออกให้ Authorization Code, Access Token, Refresh Token (Access Token มักจะเป็น JWT) Token ที่มี Header, Payload, Signature
ความสัมพันธ์ OAuth 2.0 ใช้ JWT เป็นรูปแบบของ Access Token JWT ถูกใช้ ในบริบทของ OAuth 2.0
ตัวอย่างการใช้งาน “Login with Google”, “เชื่อมต่อแอปพลิเคชัน A กับข้อมูลของคุณบนแอปพลิเคชัน B” Session token ใน API,

จัดส่งรวดเร็วส่งด่วนทั่วประเทศ
รับประกันสินค้าเคลมง่าย มีใบรับประกัน
ผ่อนชำระได้บัตรเครดิต 0% สูงสุด 10 เดือน
สะสมแต้ม รับส่วนลดส่วนลดและคะแนนสะสม

© 2026 SiamLancard — จำหน่ายการ์ดแลน อุปกรณ์ Server และเครื่องพิมพ์ใบเสร็จ

SiamLancard
Logo
Shopping cart