OAuth 2.0 คืออะไร ทำงานยังไง แบบ Step by Step โดย Lotus’s Engineer
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
- การทำงานของ OAuth
- รู้จักกับ Roles (OAuth Factors)
- รู้จักกับ Grant Types
- รู้จักกับ Token
- Access Token
- Refresh Token
- Authorization Code
- Device Code + User Code
- Grant Types รูปแบบต่าง ๆ
- การ Grant แบบ Authorization Code
- การ Grant แบบ Authorization Code PKCE
- การ Grant แบบ Client Credentials
- การ Grant แบบ Device Code
- การ Grant แบบ Refresh Token
- การ Grant แบบ Refresh Token สำหรับ PKCE
- การ Grant แบบ Implicit Flow (ไม่ใช้แล้ว)
- การ Grant แบบ Password Grant (ไม่ใช้แล้ว)
- การ Get Data
- วิธีการ Implement OAuth
- แหล่งเรียนรู้ OAuth เพิ่มเติม
รู้จักกับ OAuth
OAuth อ่านว่า “โอ-ออท”
มาจากคำว่า Open Authorization หรือบางคนก็เรียกว่า Open Authentication
ใน Spec (RFC)ไม่ได้เขียนไว้ แต่ใน Spec ใช้ชื่อว่า The OAuth 2.0 Authorization Framework เลยคิดว่ามันน่าจะมาจากคำว่า Open Authorization ***
ทุกคนน่าจะเคยเห็น และเคยใช้งาน กับอะไรประมาณนี้
จะเข้าใช้งาน App อะไรสัก App แต่ขี้เกียจสมัครใหม่ ก็เลยใช้ Facebook Login แทน เพื่อที่จะเข้าใช้งาน App นั้น แบบนี้
หรือไม่ก็ใช้ Google หรือ Twitter Login แทน แบบนี้
กรรมวิธีที่ใช้ในการ Login แทนนี้ เราเรียกกันว่า “OAuth”
นิยาม 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)
- Resource Owner
- Resource Server
- Client
- 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
- Authorization Code
- Client Credentials
- Device Code
- Refresh Token
- Implicit Flow (ไม่ใช้กันแล้ว)
- Password Grant (ไม่ใช้กันแล้ว)
การ Grant แบบ Authorization Code
Authorization Code คือ รูปแบบการ Grant Authorize โดยการใช้ Code โยน (Redirect) ข้ามระบบ เพื่อนำ Code ไปขอ Access Token
กลับมาที่ภาพนี้
Authorization Code มี 2 Steps การทำงาน คือ
- Client ขอ Code
- 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)
- Client ขอ Code
- 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 การทำงาน คือ
- 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 การทำงาน คือ
- Device ขอ device_code + user_code + verification_uri จาก Authorization Server
- Device แสดง user_code ให้ User เห็น และนำไปกรอกที่ verification_uri
- User Grant Authorize (โดยการกรอก user_code)
- 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 การทำงาน คือ
- 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 การทำงาน คือ
- ขอ 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 การทำงาน คือ
- ขอ 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
สามารถเข้าไปดูได้ที่