OAuth 2.0 คืออะไร ทำงานยังไง แบบ Step by Step โดย Lotus’s Engineer

Jittagornp
Lotus’s IT
Published in
13 min readFeb 26, 2023

The OAuth 2.0 Authorization Framework

สวัสดีครับ ผมเดียร์จากทีม LSE (Lotus’s Store Engineering) นะครับ ผมเชื่อว่าเพื่อน ๆ Developers หลายคนน่าจะเคยอ่านหรือผ่านตากับบทความนี้กันมาบ้างแล้ว

จริง ๆ บทความนี้ผมเคยทำเป็น Google Slide ไว้ที่ [ต้นฉบับ] เมื่อนานมาแล้ว ตั้งแต่ช่วงที่ประเทศไทยยังมีโควิด 19 ใหม่ ๆ

แต่วันนี้ผมจะขอนุญาตนำมาเรียบเรียงเขียนเป็นบทความลงใน Medium ภายใต้ Lotus’s IT ในฐานะ Software Engineer ของ Lotus’ s เพื่อให้เพื่อน ๆ Developers ทุกท่านได้อ่านกันในอีกรูปแบบนึง (ที่ไม่ใช่ Google Slide)

เนื่องจากทีม LSE ของเรามีการใช้ OAuth ในการทำ Authentication กันเป็นหลัก ทั้ง Application ฝั่ง Web Mobile และ Back-end (Microservices) แทบจะทั้งหมด ก็ใช้ OAuth เลยอยากที่จะชวนเพื่อน ๆ มาทบทวนความรู้กันว่ามันมีรายละเอียดอะไรที่เรายังไม่เข้าใจ หรือยังไม่แม่นเกี่ยวกับ OAuth กันบ้าง

หมายเหตุ: เนื้อหาของบทความค่อนข้างยาว ถ้าเพื่อน ๆ อยากอ่านเฉพาะหัวข้อ หรือข้ามไปที่หัวข้อไหน ก็สามารถคลิกที่สารบัญกันได้เลย

ถ้าพร้อมแล้ว ก็มาเริ่มกันเลยครับ

สารบัญ

รู้จักกับ OAuth

OAuth อ่านว่า “โอ-ออท”

มาจากคำว่า Open Authorization หรือบางคนก็เรียกว่า Open Authentication

ใน Spec (RFC)ไม่ได้เขียนไว้ แต่ใน Spec ใช้ชื่อว่า The OAuth 2.0 Authorization Framework เลยคิดว่ามันน่าจะมาจากคำว่า Open Authorization ***

ทุกคนน่าจะเคยเห็น และเคยใช้งาน กับอะไรประมาณนี้

Mediem login page

จะเข้าใช้งาน App อะไรสัก App แต่ขี้เกียจสมัครใหม่ ก็เลยใช้ Facebook Login แทน เพื่อที่จะเข้าใช้งาน App นั้น แบบนี้

Login to Pantip via Facebook

หรือไม่ก็ใช้ Google หรือ Twitter Login แทน แบบนี้

Login via Google and Twitter

กรรมวิธีที่ใช้ในการ Login แทนนี้ เราเรียกกันว่า “OAuth”

RFC6749

นิยาม OAuth

OAuth เป็น Authorization Framework ตาม Spec RFC6749 ที่ IETF กำหนดขึ้นมา เพื่อใช้สำหรับการ Grant Authorize หรือการอนุญาตสิทธิ์เรา ให้กับ Application ใด ๆ เพื่อให้ Application นั้น สามารถที่จะเข้าถึงข้อมูลของเรา จาก Application ต้นทางได้

IETF ย่อมาจาก Internet Engineering Task Force

ปัจจุบันเป็น Version 2.0

การทำงานของ OAuth

สมมติว่าเบื่อ ๆ ก็เลยอยากจะหาอะไรใน Pantip อ่าน

ไปเจอกระทู้นึงน่าสนใจ อยากจะเข้าไป Comment ด้วย

เลย Login ไปตอบกระทู้

ทำการ Login เข้า Pantip ด้วย Facebook

Login สำเร็จ ป่ะ!!! ไปตอบ กระทู้กัน

“กล้วยอบเนือย เรยกลัว เนือยมันคืออะไรวะ”

Flow การทำงาน

จาก Flow เมื่อกี๊ เราเห็นส่วนประกอบ (Factors) อะไรบ้าง

ปรับมุมมอง

เปลี่ยนชื่อใหม่

รู้จักกับ Roles (OAuth Factors)

  1. Resource Owner
  2. Resource Server
  3. Client
  4. Authorization Server

Resource Owner

คือ ตัวเรา ซึ่งเป็นเจ้าของ Resources ต่าง ๆ (เช่น รูปภาพ ข้อความ วิดีโอ ไฟล์) ในระบบ

Resource Server

คือ Server/Service ที่ทำหน้าที่ให้บริการข้อมูล เช่น Server ที่ให้บริการ API หรือ ไฟล์ต่าง ๆ ในระบบ

Client

คือ Application ใด ๆ ที่ต้องการจะเข้าถึงข้อมูลของเราจากผู้ให้บริการข้อมูล (Resource Server) เป็นได้ทั้ง

  • Mobile Application
  • Web Application
  • Desktop Application
  • และอื่น ๆ

Authorization Server

คือ Server ที่ทำหน้าที่ในการยก/อนุญาตสิทธิ์เราให้ Client เพื่อให้ Client สามารถเข้าถึงข้อมูลของเราได้

นอกจาก Roles แล้ว ยังมีอีก 1 สิ่ง ที่เราต้องรู้จัก นั่นก็คือ Grant Types

แต่ก่อนที่จะพูดถึง Grant Types

Client จะต้องทำการลงทะเบียน (Register) กับระบบนั้น ๆ เพื่อให้ได้ client_id และ client_secret มาใช้งานก่อน

เช่น

Pantip จะต้องลงทะเบียนกับ Facebook เพื่อขอ client_id และ client_secret จาก Facebook ก่อน

เมื่อลงทะเบียนเสร็จ Client จะได้

client_id = "117368861736328"
client_secret = "Tty7WYmCq2gshzHWd"

เปรียบเสมือนกับ Username และ Password ของ Client เพื่อขอเข้าใช้งานระบบ (Facebook)

รู้จักกับ Grant Types

Grant Types คือ รูปแบบ หรือวิธีการที่ใช้ในการ ยก/อนุญาตสิทธิ์เราให้กับ Application ที่ต้องการจะเข้าถึงข้อมูลของเราจาก Application ต้นทาง

จากภาพนี้

ลองดูทีละ Steps แบบนี้

ขอข้อมูลหน่อยสิ

OK อนุญาตนะ

เอาสิทธิ์ (Token) ไปแลกข้อมูลเอาเองนะ

อยากได้ข้อมูลอ่ะ เรามีสิทธิ์ (Token) แล้วนะ

เอา ชื่อ รูปภาพ email หรือข้อมูล Public Profile ไป

พักเรื่อง Grant Types แป๊ปนึง… แล้วมาทำความเข้าใจ เรื่อง Token กันก่อน

รู้จักกับ Token

Token คือ String ที่มีหน้าตาประมาณนี้

"ZTY3MDU4OWYtY2E2Zi00M2U0LTgwMmEtZWZlODliNjg1YzQ2OjE1MTI2NDE…"

แต่ละระบบอาจจะ Design/Generate Token หน้าตาไม่เหมือนกัน

ได้มาจากกระบวนการ

  • เข้ารหัส/ถอดรหัส (Encryption) หรือ
  • การแฮช (Hashing)

ใช้เป็นข้อมูลสำหรับระบุตัวตนของผู้ใช้ (User) ณ ช่วงเวลานึง (มีวันหมดอายุ) เช่น

ถ้า Client อยากได้ข้อมูลของผู้ใช้คนไหน ก็ให้เอา Token ที่มีสิทธิ์เข้าถึงข้อมูลของผู้ใช้คนนั้น มาแลก

ตัวอย่าง

ใน OAuth Token มีหลายประเภท แต่ละประเภทใช้งานในวัตถุประสงค์ที่แตกต่างกัน

ประเภทของ Token ใน OAuth

  • Access Token
  • Refresh Token
  • Authorization Code
  • Device Code + User Code
  • etc.

Access Token

คือ Token ที่ใช้สำหรับขอข้อมูล User จาก Resource Server

ตัวอย่าง

จริง ๆ แล้วรูปนี้ใช้ Access Token

รูปแบบจริง ๆ ของ Access Token ที่ Authorization Server คืนมาให้ (เป็น JSON)

{
"access_token": "U4O0M2U0LTgwMmEtZlODliNjg1YzQ2OjE1M",
"token_type": "bearer",
"expires_in": 1800 //seconds
}

Refresh Token

คือ Token ที่ใช้สำหรับขอ Access Token ใหม่ กรณีที่ Access Token เดิม หมดอายุ

ตัวอย่าง

ส่ง Refresh Token ไปขอ Access Token ใหม่

รูปแบบจริง ๆ ของ Refresh Token ที่ Authorization Server คืนมาให้

{
"access_token": "U4O0M2U0LTgwMmEtZlODliNjg1YzQ2OjE1M",
"token_type": "bearer",
"expires_in": 1800,
"refresh_token": "mEtZlODliNjgU4O0M2M1YzQ2OjE1MU0LTgw" //****
}

Authorization Code

คือ Token ที่เกิดจากการ Grant Authorize (ยก/อนุญาตสิทธิ์) เราให้ Client เพื่อให้ Client นำ Token นี้ มาขอ Access Token อีกที

ตัวอย่าง

ได้ Authorization Code กลับไป

ใช้ Authorization Code มาขอ Access Token

Device Code + User Code

คือ Token ที่ Client ประเภท Smart Device ใช้สำหรับขอ Grant Authorize จาก User เพื่อขอ Access Token อีกที

Smart Device เช่น Smart TV, Smart Radio

ตัวอย่าง

App Youtube ขอข้อมูลไปที่ Authorization Server

ได้ข้อมูลมา

เอา User Code มาแสดงให้ User เห็น

User เอา Code ไปกรอก หรือกด Approve บน Application

ได้ Access Token กลับไป

รูปแบบจริง ๆ ของ Device Code + User Code ที่ Authorization Server คืนมาให้

{
"device_code": "ZlODliNU4O0M2U0LTgwMmEtjg1YzQ2OjE1M",
"user_code": "BDWD-HQPK",
"verification_uri": "https://youtube.com/oauth/authorize/device",
"interval": 5, //seconds
"expires_in": 1800 //seconds
}

Grant Types รูปแบบต่าง ๆ

กลับมาที่ GrantTypes

Grant Types

คือ รูปแบบ หรือ วิธีการที่ใช้ในการขอ Access Token ด้วยวิธีการต่าง ๆ กัน เพื่อนำ Access Token ที่ได้ ไปขอข้อมูล User จาก Resource Server

ประเภทของ Grant Types

  1. Authorization Code
  2. Client Credentials
  3. Device Code
  4. Refresh Token
  5. Implicit Flow (ไม่ใช้กันแล้ว)
  6. Password Grant (ไม่ใช้กันแล้ว)

การ Grant แบบ Authorization Code

Authorization Code คือ รูปแบบการ Grant Authorize โดยการใช้ Code โยน (Redirect) ข้ามระบบ เพื่อนำ Code ไปขอ Access Token

กลับมาที่ภาพนี้

Authorization Code มี 2 Steps การทำงาน คือ

  1. Client ขอ Code
  2. Client นำ Code ไปขอ Access Token

ปกติ การขอ Code และขอ Access Token มักที่จะขอจาก Authorization Server ด้วย Path ประมาณนี้

<Authorization-Server>/oauth/authorize
<Authorization-Server>/oauth/token

แต่บางระบบก็อาจจะ Implement หรือตั้งชื่อ Path ไม่เหมือนกัน แค่อาจจะคล้าย ๆ

1. Client ขอ Code

ขอ Code ไปที่ /oauth/authorize ด้วย Request parameters ดังต่อไปนี้

  • response_type=code
  • client_id
  • redirect_uri
  • scope
  • state (optional)

Response Type

ใช้สำหรับบอก Authorization Server ว่า ต้องการให้ Authorization Server return อะไรกลับมาให้ ในที่นี้คือ response_type = code

ส่ง code กลับมาให้หน่อย!

Redirect URI

เมื่อ Grant Authorize เสร็จแล้ว จะให้ redirect กลับมาที่ Path ไหนของ Client

URI ไม่ใช่ URL (ต่างกันยังไง ไปอ่านกันเอาเองนะ)

Scope = Limit Access

ใช้สำหรับไว้บอก User ว่า Client ต้องการเข้าถึงข้อมูลของ User ตาม Scope นี้ User จะอนุญาตมั้ย ?

State = CSRF Protection

เป็น Random Number ไว้สำหรับป้องกัน CSRF ระหว่าง Client กับ Authorization Server

state = randomAndStore();

CSRF = Cross Site Request Forgery

หน้าตาของ Request

Http Redirect to: "https://facebook.com/oauth/authorize
?response_type=code
&client_id=117368861736328
&redirect_uri=https://pantip.com/oauth/callback/facebook
&scope=email
&state=Q2OjE1MU0L2M"

Return ของ /oauth/authorize มี Parameters ดังต่อไปนี้ Return กลับมาให้

  • code
  • state (optional)

ตัวอย่าง

Http Redirect to (redirect_uri): "https://pantip.com/oauth/callback/facebook
?code=WZlODliNjg1Yz1MTI2ND2ZiEZTY3MDU4OWYtYTgwMmEtZ
&state=Q2OjE1MU0L2M"

กรณี Error ของ /oauth/authorize

Http Redirect to (redirect_uri): "https://pantip.com/oauth/callback/facebook
?error=unauthorized_client
&state=Q2OjE1MU0L2M"

2. Client นำ Code ไปขอ Access Token

ขอ Access Token ไปที่ /oauth/token ด้วย Request body (Paramerters) ดังต่อไปนี้

  • grant_type=authorization_code
  • code
  • client_id
  • client_secret
  • redirect_uri

หน้าตาของ Request

Content-Type: application/x-www-form-urlencoded
Http POST: "https://facebook.com/oauth/token
?grant_type=authorization_code
&code=WZlODliNjg1Yz1MTI2ND2ZiEZTY3MDU4OWYtYTgwMmEtZ
&client_id=117368861736328
&client_secret=Tty7WYmCq2gshzHWd
&redirect_uri=https://pantip.com/oauth/callback/facebook"

Response ของ /oauth/token

{
"access_token": "U4O0M2U0LTgwMmEtZlODliNjg1YzQ2OjE1M",
"token_type": "bearer",
"expires_in": 1800,
"refresh_token": "mEtZlODliNjgU4O0M2M1YzQ2OjE1MU0LTgw"
}

กรณี Error ของ /oauth/token

 {
"error": "unauthorized_client",
"error_description": "Invalid client_id or client_secret",
"state": "Q2OjE1MU0L2M"
}

การ Grant แบบ Authorization Code จะแบ่งย่อยเป็น 2 แบบ คือ

  • Authorization Code (Default)
  • Authorization Code PKCE

Authorization Code (Default)

สำหรับใช้งานกับระบบที่มี Backend Web Server (ซึ่งก็คือ Flow ที่อธิบายไปเมื่อกี๊)

Authorization Code PKCE (Proof Key for Code Exchange)

สำหรับใช้งานกับ

  • Mobile
  • Single Page Application
  • Desktop Application
  • Other

การ Grant แบบ Authorization Code PKCE

เพราะเราไม่สามารถเอา client_secret ไปเก็บไว้บน Mobile Application, Single Page Application, Desktop Application และอื่น ๆ ได้ เลยเกิด Flow PKCE ขึ้นมา เพื่อเอามาแก้ปัญหานี้

Authorization Code PKCE มี 2 Steps (การทำงาน เหมือนกับแบบ Default)

  1. Client ขอ Code
  2. Client นำ Code ไปขอ Access Token

1. Client ขอ Code

ขอ Code ไปที่ /oauth/authorize ด้วย Request parameters ดังต่อไปนี้

  • response_type=code
  • client_id
  • redirect_uri
  • scope
  • state (optional)
  • code_challenge (เพิ่มเข้ามา)
  • code_challenge_method=S256 (SHA256 เพิ่มเข้ามา)

เปรียบเทียบ PKCE vs Default

วิธีการสร้าง code_challenge

//1. สุ่มและจัดเก็บไว้ (ให้ secure)
code_verifier = randomAndStore();

//2. hash ค่าด้วย algorithm แบบ sha256
code_verifier_hashed = sha256(code_verifier);

//3. encode ด้วย base64 url
code_challenge = base64URLEncode(code_verifier_hashed);
function base64URLEncode(str) {
return base64Encode(str)
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}

สมมิว่าได้ code_challenge เป็น

code_verifier="jv5wsaae"
code_challenge="ODAyNzI2OTNhYWRiYzE2ZmQ2NmJjZmU1MzUxODJiMjg5NmVkNTVlZTcwODM3MmIzOGUzN2E4NTMyMmVjYjk3Yw"

หน้าตาของ Request ที่ต้อง Request ไปที่ Authorization Server

Http Redirect to: "https://facebook.com/oauth/authorize
?response_type=code
&client_id=117368861736328
&redirect_uri=https://pantip.com/oauth/callback/facebook
&scope=email
&state=Q2OjE1MU0L2M
&code_challenge=ODAyNzI2OTNhYWRiYzE2ZmQ2NmJjZm...
&code_challenge_method=S256"

Return ของ /oauth/authorize

Http Redirect to (redirect_uri): "https://pantip.com/oauth/callback/facebook
?code=WZlODliNjg1Yz1MTI2ND2ZiEZTY3MDU4OWYtYTgwMmEtZ
&state=Q2OjE1MU0L2M"

กรณี Error ของ /oauth/authorize

Http Redirect to (redirect_uri): "https://pantip.com/oauth/callback/facebook
?error=unauthorized_client
&state=Q2OjE1MU0L2M"

2. Client นำ Code ไปขอ Access Token

ขอ Access Token ไปที่ /oauth/token ด้วย Request body (Paramerters) ดังต่อไปนี้

  • grant_type=authorization_code
  • code
  • client_id
  • code_verifier
  • redirect_uri

เปรียบเทียบ PKCE vs Default

หน้าตาของ Request

Content-Type: application/x-www-form-urlencoded
Http POST: "https://facebook.com/oauth/token
?grant_type=authorization_code
&code=WZlODliNjg1Yz1MTI2ND2ZiEZTY3MDU4OWYtYTgwMmEtZ
&client_id=117368861736328
&code_verifier=jv5wsaae
&redirect_uri=https://pantip.com/oauth/callback/facebook"

Response ของ /oauth/token

{
"access_token": "U4O0M2U0LTgwMmEtZlODliNjg1YzQ2OjE1M",
"token_type": "bearer",
"expires_in": 1800,
"refresh_token": "mEtZlODliNjgU4O0M2M1YzQ2OjE1MU0LTgw"
}

กรณี Error ของ /oauth/token

  {
"error": "unauthorized_client",
"error_description": "Invalid client_id or client_secret",
"state": "Q2OjE1MU0L2M"
}

การ Grant แบบ Client Credentials

คือ รูปแบบการ Grant Authorize โดยการใช้ client_id + client_secret เพื่อนำไปขอ Access Token

Flow นี้จะไม่มีหน้าจอ Grant/Consent เหมือนกับ Flow Authorization Code

เป็น Flow ที่มักจะใช้งานกับ Back-end Service และมักจะใช้งานกับข้อมูลที่อยู่นอกเหนือบริบท (Context) ของ User คือ เป็นข้อมูลของ Client เอง

เหมาะกับการใช้ในบริบท Service <-> Service คุยกัน (Microservices ต่าง ๆ ของทีม LSE ก็ใช้ Flow นี้)

Client Credentials มี 1 Step การทำงาน คือ

  1. Client ขอ Access Token ด้วย client_id + client_secret

1. Client ขอ Access Token ด้วย client_id + client_secret

ขอ Access Token ไปที่ /oauth/token ด้วย Request body (Paramerters) ดังต่อไปนี้

  • grant_type=client_credentials
  • client_id
  • client_secret
Content-Type: application/x-www-form-urlencoded
Http POST: "https://facebook.com/oauth/token
?grant_type=client_credentials
&client_id=117368861736328
&client_secret=Tty7WYmCq2gshzHWd"

Response ของ /oauth/token

{
"access_token": "U4O0M2U0LTgwMmEtZlODliNjg1YzQ2OjE1M",
"token_type": "bearer",
"expires_in": 1800,
"refresh_token": "mEtZlODliNjgU4O0M2M1YzQ2OjE1MU0LTgw"
}

กรณี Error ของ /oauth/token

 {
"error": "unauthorized_client",
"error_description": "Invalid client_id or client_secret",
"state": "Q2OjE1MU0L2M"
}

การ Grant แบบ Device Code

คือ รูปแบบการ Grant Authorize สำหรับอุปกรณ์บางประเภท ที่ไม่มี Keyboard สำหรับกรอกข้อมูล หรือ มี Input รับข้อมูลที่ไม่ค่อยสะดวกสำหรับ User เช่น Smart TV, Smart Radio, etc.

Device Code มี 4 Steps การทำงาน คือ

  1. Device ขอ device_code + user_code + verification_uri จาก Authorization Server
  2. Device แสดง user_code ให้ User เห็น และนำไปกรอกที่ verification_uri
  3. User Grant Authorize (โดยการกรอก user_code)
  4. Device Get access_token จาก device_code + user_code

1. Device ขอ device_code + user_code + verification_uri จาก Authorization Server

Device ขอ Device Code ไปที่ /oauth/authorize ด้วย Request parameters ดังต่อไปนี้

  • response_type=device_code
  • client_id

หน้าตาของ Request ที่ต้อง Request ไปที่ Authorization Server

Http POST: "https://youtube.com/oauth/authorize
?response_type=device_code
&client_id=117368861736328"

Response ของ /oauth/authorize เป็น JSON ที่มี Field ประมาณนี้

  • device_code
  • user_code
  • verification_uri
  • interval
  • expires_in
{
"device_code": "ZlODliNU4O0M2U0LTgwMmEtjg1YzQ2OjE1M",
"user_code": "BDWD-HQPK",
"verification_uri": "https://youtube.com/oauth/authorize/device",
"interval": 5, //seconds
"expires_in": 1800 //seconds
}

2. Device แสดง user_code ให้ User เห็น และนำไปกรอกที่ verification_uri

3. User Grant Authorize (โดยการกรอก user_code)

ตรงนี้ User อาจจะไม่กรอก อาจจะแค่กดปุ่ม Approve เอาก็ได้

4. Device Get access_token จาก device_code + user_code

Device ขอ Access Token ไปที่ /oauth/token โดยการ Polling Check ว่า User Grant รึยัง ด้วย Request body (Paramerters) ดังต่อไปนี้

  • grant_type=urn:ietf:params:oauth:grant-type:device_code
  • client_id
  • device_code
Content-Type: application/x-www-form-urlencoded
Http Post: "https://youtube.com/oauth/token
?grant_type=urn:ietf:params:oauth:grant-type:device_code
&client_id=117368861736328
&device_code=ZlODliNU4O0M2U0LTgwMmEtjg1YzQ2OjE1M"

Response ของ /oauth/token กรณีที่ Polling ถี่เกินไป

{
"error": "slow_down"
}

Response ของ /oauth/token กรณีที่ User ยังไม่ Grant

{
"error": "authorization_pending"
}

Response ของ /oauth/token กรณีที่ User ไม่ Grant แล้ว devicce_code Expired

{
"error": "expired_token"
}

Response ของ /oauth/token กรณีที่ User Grant แล้ว

{
"access_token": "U4O0M2U0LTgwMmEtZlODliNjg1YzQ2OjE1M",
"token_type": "bearer",
"expires_in": 1800,
"refresh_token": "mEtZlODliNjgU4O0M2M1YzQ2OjE1MU0LTgw"
}

การ Grant แบบ Refresh Token

คือ รูปแบบการ Grant Authorize เพื่อขอ Access Token ใหม่ กรณีที่ Access Token เดิมหมดอายุ

Flow นี้เป็น Optional บางระบบก็ไม่มีให้

มี 1 Step การทำงาน คือ

  1. Client ขอ Access Token ใหม่ ด้วย Refresh Token

1. Client ขอ Access Token ใหม่ ด้วย Refresh Token

Refresh Token ไปที่ /oauth/token ด้วย Request body (Paramerters) ดังต่อไปนี้

  • grant_type=refresh_token
  • refresh_token
  • client_id
  • client_secret
Content-Type : application/x-www-form-urlencoded
Http POST: "https://facebook.com/oauth/token
?grant_type=refresh_token
&refresh_token=mEtZlODliNjgU4O0M2M1YzQ2OjE1MU0LTgw
&client_id=117368861736328
&client_secret=Tty7WYmCq2gshzHWd"

Response ของ /oauth/token

{
"access_token": "U4O0M2U0LTgwMmEtZlODliNjg1YzQ2OjE1M",
"token_type": "bearer",
"expires_in": 1800,
"refresh_token": "mEtZlODliNjgU4O0M2M1YzQ2OjE1MU0LTgw"
}

การ Grant แบบ Refresh Token สำหรับ PKCE

Refresh Token ไปที่ /oauth/token

  • grant_type=refresh_token
  • refresh_token
  • client_id
  • code_verifier

เปรียบเทียบ PKCE vs Default

หน้าตาของ Request

Content-Type: application/x-www-form-urlencoded
Http POST: "https://facebook.com/oauth/token
?grant_type=refresh_token
&refresh_token=mEtZlODliNjgU4O0M2M1YzQ2OjE1MU0LTgw
&client_id=117368861736328
&client_verifier=jv5wsaae"

Response ของ /oauth/token

{
"access_token": "U4O0M2U0LTgwMmEtZlODliNjg1YzQ2OjE1M",
"token_type": "bearer",
"expires_in": 1800,
"refresh_token": "mEtZlODliNjgU4O0M2M1YzQ2OjE1MU0LTgw"
}

การ Grant แบบ Implicit Flow (ไม่ใช้แล้ว)

คือ รูปแบบการ Grant Authorize ที่คล้าย ๆ กับ Authorization Code Flow แต่จะ Return (Redirect) access_token กลับมาทาง URI ด้วย fragment (#) แทนการ Return (Redirect) code

ก่อนที่จะมี Authorization Code PKCE

Implicit Flow ถูกนำมาใช้กับ Mobile Application, Single Page Application, Desktop Application แต่เพราะมีช่องโหว่ เลยถูกยกเลิกไป

Implicit Flow มี 1 Step การทำงาน คือ

  1. ขอ Access Token

1. ขอ Access Token

ขอ Access Token ไปที่ /oauth/authorize ด้วย Request parameters ดังต่อไปนี้

Http Redirect to: "https://facebook.com/oauth/authorize
?response_type=token
&client_id=117368861736328
&redirect_uri=https://pantip.com/oauth/callback/facebook
&scope=email
&state=Q2OjE1MU0L2M"

Return ของ /oauth/authorize

Http Redirect to (redirect_uri): "https://pantip.com/oauth/callback/facebook
#access_token=U4O0M2U0LTgwMmEtZlODliNjg1YzQ2OjE1M
&type=bearer
&expires_in=1800
&state=Q2OjE1MU0L2M"

กรณี Error ของ /oauth/authorize

Http Redirect to (redirect_uri): "https://pantip.com/oauth/callback/facebook
#error=access_denied
&state=Q2OjE1MU0L2M"

การ Grant แบบ Password Grant (ไม่ใช้แล้ว)

(Resource Owner Password Credentials Grant)

คือ รูปแบบการ Grant Authorize โดยการใช้ Username และ Password ของ User เพื่อขอ Access Token

Password Grant ที่ยกเลิกการใช้งานไป เพราะจะทำให้ Username + Password ของ User หลุดไปยัง Client ที่เชื่อมต่อไปที่ Authorization Server

Password Grant มี 1 Step การทำงาน คือ

  1. ขอ Access Token

1. ขอ Access Token

ขอ Access Token ไปที่ /oauth/token ด้วย Request body (Paramerters) ดังต่อไปนี้

Content-Type: application/x-www-form-urlencoded
Http POST: "https://facebook.com/oauth/token
?grant_type=password
&username=myemail@mail.com
&password=A3ddj3w
&client_id=117368861736328
&client_secret=Tty7WYmCq2gshzHWd"

Response ของ /oauth/token

{
"access_token": "U4O0M2U0LTgwMmEtZlODliNjg1YzQ2OjE1M",
"token_type": "bearer",
"expires_in": 1800,
"refresh_token": "mEtZlODliNjgU4O0M2M1YzQ2OjE1MU0LTgw"
}

การ Get Data

หลังจากที่เรารู้วิธีการได้มาซึ่ง Access Token ด้วยวิธีการต่าง ๆ แล้ว ต่อมา คือการนำ Access Token ไปใช้งาน

ดูที่ JSON Response นี้

{
"access_token": "U4O0M2U0LTgwMmEtZlODliNjg1YzQ2OjE1M",
"token_type": "bearer", //***
"expires_in": 1800,
"refresh_token": "mEtZlODliNjgU4O0M2M1YzQ2OjE1MU0LTgw"
}

token_type = bearer

เป็นการบอกว่า ให้เรานำ Token นี้ไปใช้งานแบบ Bearer คือ การเรียกใช้งาน API ใด ๆ ในระบบ ให้แนบ Token ไปกับ Http Request Header ดังนี้

"Authorization": "Bearer $TOKEN"

วิธีการ Implement OAuth

ให้ดูก่อนว่า เราต้องการ Implement OAuthใน Role ไหน

  • Client
  • Resource Server
  • Authorization Server

Client

เราสามารถใช้ Library ต่าง ๆ ที่มีอยู่เขียน Code เพื่อเชื่อมต่อไปที่ Authorization Server ปลายทาง ที่ Code นั้นรองรับได้เลย

หรือ จะเขียน Code ทั้งหมดเองก็ได้ ถ้าเราเข้าใจ Flow การทำงานของ OAuth

โดยส่วนมาก เรามักจะเห็นคนเขียน OAuth กันใน Role นี้ เพราะมันทำได้ไม่ยาก เช่น การเขียน Code เพื่อ Login ผ่าน Facebook Google หรือ Twitter เป็นต้น

Resource Server

เช่นเดียวกับ Client คือ สามารถใช้ Library ต่าง ๆ ที่มีอยู่ช่วยในการ Config เพื่อให้ Library เชื่อมต่อไปที่ Authorization Server ปลายทาง ที่ Code นั้นรองรับได้เลย

หรือ จะเขียน Code ทั้งหมดเองก็ได้เช่นเดียวกัน ถ้าเราเข้าใจ Flow การทำงานของ OAuth

ทีม LSE เราเขียน Java Spring-boot กันเป็นหลัก เวลาที่ต้องการ Implement OAuth ใน Role นี้ เราก็จะใช้ Spring-security เข้ามาช่วย https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/index.html

Authorization Server

Role นี้คนไม่ค่อยทำ เพราะว่ามันทำได้ยากกว่า แต่ก็มี Services + Libraries ที่สามารถเอามาทำตรงนี้ได้

ทีม LSE เราใช้ Keycloak ในการทำ Authorization Server ถ้าหากเพื่อน ๆ สนใจ ก็สามารถเข้าไปเรียนรู้กันได้ที่ https://www.keycloak.org/

Services และ Libraries ต่าง ๆ สำหรับทำ OAuth

สามารถเข้าไปดูได้ที่

แหล่งเรียนรู้ OAuth เพิ่มเติม

--

--

Jittagornp

วิศกรซอฟต์แวร์ ที่หลงรักการเขียนซอฟต์แวร์ และมีความสุขกับการได้อยู่กับครอบครัว