Device flow: Auth กับ Logto
คู่มือนี้สมมติว่าคุณได้สร้างแอปพลิเคชันประเภท "Native" ที่ใช้ device flow เป็น authorization flow ใน Logto Console แล้ว
บทนำ
OAuth 2.0 device authorization grant (device flow) ถูกออกแบบมาสำหรับอุปกรณ์ที่มีข้อจำกัดด้านการป้อนข้อมูล เช่น สมาร์ททีวี, เครื่องเกม, เครื่องมือ CLI, และอุปกรณ์ IoT โดยช่วยให้ผู้ใช้เริ่มกระบวนการลงชื่อเข้าใช้บนอุปกรณ์นั้น แต่ดำเนินการยืนยันตัวตนให้เสร็จสมบูรณ์บนอุปกรณ์อื่นที่มีเบราว์เซอร์ เช่น โทรศัพท์หรือแล็ปท็อป
เนื่องจากตัวอุปกรณ์ไม่สามารถจัดการ flow การลงชื่อเข้าใช้ผ่านเบราว์เซอร์ได้ อุปกรณ์จะแสดงรหัสสั้น ๆ และ URL ให้ผู้ใช้ ผู้ใช้จะไปที่ URL นั้นบนอุปกรณ์อื่น กรอกรหัส และลงชื่อเข้าใช้ ในขณะเดียวกัน อุปกรณ์ต้นทางจะ poll ไปที่ Logto จนกว่าการอนุญาตจะเสร็จสมบูรณ์
รับข้อมูลประจำตัวแอปพลิเคชัน
ใน Logto Console ของคุณ ไปที่หน้ารายละเอียดแอปพลิเคชันเพื่อรับข้อมูลประจำตัวดังต่อไปนี้:
- App ID: ตัวระบุเฉพาะของแอปพลิเคชันของคุณ (หรือที่รู้จักในชื่อ
client_id) - Logto endpoint: จุดเชื่อมต่อเซิร์ฟเวอร์ authorization ของ Logto คุณสามารถดูได้ใน Logto Console ที่ "Application details"
สำหรับ Logto Cloud, endpoint คือ https://{your-tenant-id}.logto.app
แอป device flow เป็น public client จึงไม่ต้องใช้ App Secret
ขอ device code
เริ่ม device flow โดยส่งคำขอ POST ไปยัง device authorization endpoint:
curl --request POST 'https://your.logto.endpoint/oidc/device/auth' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'scope=openid offline_access profile'
ผลลัพธ์ที่ได้รับประกอบด้วย:
| Field | Description |
|---|---|
device_code | โค้ดเฉพาะสำหรับแอปของคุณใช้ในการ poll ไปที่ token endpoint |
user_code | โค้ดสั้น ๆ สำหรับแสดงให้ผู้ใช้กรอกในเบราว์เซอร์ |
verification_uri | URL ที่ผู้ใช้จะกรอก user_code |
verification_uri_complete | URL ที่มี user_code เติมไว้ล่วงหน้าแล้ว ผู้ใช้สามารถไปที่ URL นี้โดยตรงเพื่อข้ามการกรอกรหัสเอง — คุณสามารถนำเสนอเป็น QR code, ลิงก์ หรือวิธีอื่น ๆ ก็ได้ |
expires_in | อายุการใช้งาน (วินาที) ของ device_code และ user_code ให้หยุด polling เมื่อหมดอายุ |
แสดง URL สำหรับยืนยันตัวตนให้ผู้ใช้
แสดง user_code และ verification_uri บนหน้าจออุปกรณ์ของคุณ
หรือจะใช้ verification_uri_complete ที่มีโค้ดเติมไว้ล่วงหน้าแล้วก็ได้ — ผู้ใช้เพียงแค่ยืนยัน วิธีนำเสนอขึ้นอยู่กับคุณ: QR code, ลิงก์ ฯลฯ
Poll สำหรับโทเค็น
ขณะที่ผู้ใช้ดำเนินการยืนยันตัวตนในเบราว์เซอร์ อุปกรณ์ของคุณควร poll ไปที่ token endpoint โดยแอปของคุณควรรออย่างน้อย 5 วินาที ระหว่างแต่ละ polling:
curl --request POST 'https://your.logto.endpoint/oidc/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:device_code' \
--data-urlencode 'device_code=DEVICE_CODE'
แทนที่ DEVICE_CODE ด้วยค่าจาก device authorization response
หยุด polling เมื่อ:
- คุณได้รับ token response ที่สำเร็จ
- เวลาจาก
expires_inใน device code response หมดอายุ - คุณได้รับ error ที่ไม่ควร retry เช่น
expired_tokenหรือaccess_denied
การตอบกลับโทเค็น
หลังจากผู้ใช้อนุมัติแล้ว ผลลัพธ์จะมี:
| Field | Description |
|---|---|
access_token | โทเค็นการเข้าถึง (Access token) โดยปกติเป็นสตริงทึบ; หากมีการร้องขอ resource จะเป็น JWT ที่ aud ถูกตั้งเป็น resource URI |
id_token | โทเค็น ID (ID token) ที่มีการอ้างสิทธิ์ของผู้ใช้ มีเฉพาะเมื่อร้องขอ scope openid |
refresh_token | ใช้เพื่อขอโทเค็นใหม่โดยไม่ต้องยืนยันตัวตนซ้ำ มีเฉพาะเมื่อร้องขอ scope offline_access |
token_type | เป็น Bearer เสมอ |
expires_in | อายุการใช้งานของโทเค็น (วินาที) |
scope | ขอบเขต (scopes) ที่ authorization server อนุมัติ |
Checkpoint: ทดสอบ device flow ของคุณ
ตอนนี้ ทดสอบการเชื่อมต่อ device flow ของคุณ:
- รันแอปของคุณและเรียก device flow เพื่อรับ
device_codeและuser_code - เปิด
verification_uriในเบราว์เซอร์และกรอกuser_codeหรือใช้verification_uri_completeเพื่อข้ามการกรอกรหัส - ดำเนินการลงชื่อเข้าใช้ในเบราว์เซอร์ให้เสร็จสมบูรณ์
- ตรวจสอบว่าแอปของคุณได้รับโทเค็นหลังจาก polling
รับข้อมูลผู้ใช้
ถอดรหัสการอ้างสิทธิ์ใน ID token
id_token ที่ได้จาก token response เป็น JSON Web Token (JWT) มาตรฐาน คุณสามารถถอดรหัส payload ที่ถูกเข้ารหัสแบบ Base64URL (ส่วนที่สองของ JWT คั่นด้วย .) เพื่อเข้าถึงการอ้างสิทธิ์พื้นฐานของผู้ใช้โดยไม่ต้องร้องขอเครือข่ายเพิ่มเติม
payload ที่ถอดรหัสจะมีการอ้างสิทธิ์ เช่น sub (user ID), name, email ฯลฯ ขึ้นอยู่กับ scopes ที่ร้องขอ
สำหรับการใช้งานจริง ควรตรวจสอบลายเซ็นของ JWT ก่อนเชื่อถือการอ้างสิทธิ์ ใช้ JWKS จาก Logto endpoint ของคุณ (https://your.logto.endpoint/oidc/jwks) เพื่อตรวจสอบโทเค็น
ดึงข้อมูลจาก userinfo endpoint
ID token จะมีการอ้างสิทธิ์พื้นฐานตาม scopes ที่ร้องขอ การอ้างสิทธิ์เพิ่มเติมบางอย่าง (เช่น custom_data, identities) จะมีเฉพาะผ่าน OIDC UserInfo endpoint:
curl --request GET 'https://your.logto.endpoint/oidc/me' \
--header 'Authorization: Bearer ACCESS_TOKEN'
แทนที่ ACCESS_TOKEN ด้วย access token ทึบ (ไม่ใช่ JWT resource token) ที่ได้จาก token response ผลลัพธ์จะเป็น JSON object ที่มีการอ้างสิทธิ์ของผู้ใช้ตาม scopes ที่ได้รับอนุมัติ
ขอการอ้างสิทธิ์เพิ่มเติม
คุณอาจพบว่าข้อมูลผู้ใช้บางอย่างไม่มีใน ID token นี่เป็นเพราะ OAuth 2.0 และ OpenID Connect (OIDC) ถูกออกแบบให้ยึดหลัก least privilege (PoLP) และ Logto ก็สร้างบนมาตรฐานเหล่านี้
โดยปกติแล้ว จะมีการส่งคืนการอ้างสิทธิ์ (claim) แบบจำกัด หากคุณต้องการข้อมูลเพิ่มเติม คุณสามารถร้องขอขอบเขต (scope) เพิ่มเติมเพื่อเข้าถึงการอ้างสิทธิ์ (claim) ที่มากขึ้นได้
"การอ้างสิทธิ์ (Claim)" คือการยืนยันข้อมูลบางอย่างเกี่ยวกับผู้ถูกอ้างถึง (subject); "ขอบเขต (Scope)" คือกลุ่มของการอ้างสิทธิ์ (claim) ในกรณีนี้ การอ้างสิทธิ์ (claim) คือข้อมูลบางอย่างเกี่ยวกับผู้ใช้
ตัวอย่างที่ไม่เป็นทางการของความสัมพันธ์ระหว่างขอบเขต (scope) กับการอ้างสิทธิ์ (claim) มีดังนี้:
การอ้างสิทธิ์ (claim) "sub" หมายถึง "ผู้ถูกอ้างถึง (subject)" ซึ่งคือตัวระบุที่ไม่ซ้ำของผู้ใช้ (เช่น user ID)
Logto SDK จะร้องขอขอบเขต (scope) สามรายการเสมอ ได้แก่ openid, profile และ offline_access
หากต้องการขอ scopes เพิ่มเติม ให้ใส่ไว้ในพารามิเตอร์ scope ของ device authorization request เช่น หากต้องการขออีเมลและเบอร์โทรศัพท์ของผู้ใช้:
curl --request POST 'https://your.logto.endpoint/oidc/device/auth' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'scope=openid offline_access profile email phone'
Scopes และ claims
ต่อไปนี้คือรายการขอบเขต (Scopes) ที่รองรับและการอ้างสิทธิ์ (Claims) ที่เกี่ยวข้อง:
ขอบเขต OIDC มาตรฐาน
openid (ค่าเริ่มต้น)
| ชื่อการอ้างสิทธิ์ | ประเภท | คำอธิบาย |
|---|---|---|
| sub | string | ตัวระบุที่ไม่ซ้ำของผู้ใช้ |
profile (ค่าเริ่มต้น)
| ชื่อการอ้างสิทธิ์ | ประเภท | คำอธิบาย |
|---|---|---|
| name | string | ชื่อเต็มของผู้ใช้ |
| username | string | ชื่อผู้ใช้ของผู้ใช้ |
| picture | string | URL ของรูปโปรไฟล์ผู้ใช้ปลายทาง (End-User) URL นี้ต้องอ้างถึงไฟล์รูปภาพ (เช่น PNG, JPEG, หรือ GIF) ไม่ใช่หน้าเว็บที่มีรูปภาพ โปรดทราบว่า URL นี้ควรอ้างอิงถึงรูปโปรไฟล์ของผู้ใช้ปลายทางโดยเฉพาะ เหมาะสำหรับแสดงเมื่ออธิบายผู้ใช้ปลายทาง ไม่ใช่รูปภาพใด ๆ ที่ผู้ใช้ถ่ายมาโดยพลการ |
| created_at | number | เวลาที่สร้างผู้ใช้ปลายทาง เวลานี้แสดงเป็นจำนวนมิลลิวินาทีตั้งแต่ Unix epoch (1970-01-01T00:00:00Z) |
| updated_at | number | เวลาที่ข้อมูลของผู้ใช้ปลายทางถูกอัปเดตล่าสุด เวลานี้แสดงเป็นจำนวนมิลลิวินาทีตั้งแต่ Unix epoch (1970-01-01T00:00:00Z) |
การอ้างสิทธิ์มาตรฐาน อื่น ๆ เช่น family_name, given_name, middle_name, nickname, preferred_username, profile, website, gender, birthdate, zoneinfo, และ locale จะถูกรวมอยู่ในขอบเขต profile ด้วยโดยไม่จำเป็นต้องร้องขอ endpoint userinfo ความแตกต่างเมื่อเทียบกับการอ้างสิทธิ์ข้างต้นคือ การอ้างสิทธิ์เหล่านี้จะถูกส่งกลับเมื่อค่าของมันไม่ว่างเปล่าเท่านั้น ในขณะที่การอ้างสิทธิ์ข้างต้นจะคืนค่า null หากค่าว่างเปล่า
ต่างจากการอ้างสิทธิ์มาตรฐาน การอ้างสิทธิ์ created_at และ updated_at ใช้หน่วยเป็นมิลลิวินาทีแทนที่จะเป็นวินาที
email
| ชื่อการอ้างสิทธิ์ | ประเภท | คำอธิบาย |
|---|---|---|
string | อีเมลของผู้ใช้ | |
| email_verified | boolean | อีเมลได้รับการยืนยันแล้วหรือไม่ |
phone
| ชื่อการอ้างสิทธิ์ | ประเภท | คำอธิบาย |
|---|---|---|
| phone_number | string | เบอร์โทรศัพท์ของผู้ใช้ |
| phone_number_verified | boolean | เบอร์โทรศัพท์ได้รับการยืนยันแล้วหรือไม่ |
address
โปรดดูรายละเอียดของการอ้างสิทธิ์ที่อยู่ได้ที่ OpenID Connect Core 1.0
ขอบเขตที่มีเครื่องหมาย (ค่าเริ่มต้น) จะถูกร้องขอเสมอโดย Logto SDK การอ้างสิทธิ์ภายใต้ขอบเขต OIDC มาตรฐานจะถูกรวมอยู่ในโทเค็น ID เสมอเมื่อมีการร้องขอขอบเขตที่เกี่ยวข้อง — ไม่สามารถปิดได้
ขอบเขตเพิ่มเติม
ขอบเขตต่อไปนี้เป็นขอบเขตที่ Logto ขยายขึ้นและจะคืนค่าการอ้างสิทธิ์ผ่าน userinfo endpoint การอ้างสิทธิ์เหล่านี้ยังสามารถตั้งค่าให้ถูกรวมอยู่ในโทเค็น ID ได้โดยตรงผ่าน Console > Custom JWT ดู Custom ID token สำหรับรายละเอียดเพิ่มเติม
custom_data
| ชื่อการอ้างสิทธิ์ | ประเภท | คำอธิบาย | รวมในโทเค็น ID โดยค่าเริ่มต้น |
|---|---|---|---|
| custom_data | object | ข้อมูลกำหนดเองของผู้ใช้ |
identities
| ชื่อการอ้างสิทธิ์ | ประเภท | คำอธิบาย | รวมในโทเค็น ID โดยค่าเริ่มต้น |
|---|---|---|---|
| identities | object | ข้อมูลตัวตนที่เชื่อมโยงของผู้ใช้ | |
| sso_identities | array | ข้อมูล SSO ที่เชื่อมโยงของผู้ใช้ |
roles
| ชื่อการอ้างสิทธิ์ | ประเภท | คำอธิบาย | รวมในโทเค็น ID โดยค่าเริ่มต้น |
|---|---|---|---|
| roles | string[] | บทบาท (Role) ของผู้ใช้ | ✅ |
urn:logto:scope:organizations
| ชื่อการอ้างสิทธิ์ | ประเภท | คำอธิบาย | รวมในโทเค็น ID โดยค่าเริ่มต้น |
|---|---|---|---|
| organizations | string[] | รหัสองค์กรที่ผู้ใช้สังกัด | ✅ |
| organization_data | object[] | ข้อมูลองค์กรที่ผู้ใช้สังกัด |
การอ้างสิทธิ์ขององค์กรเหล่านี้สามารถดึงได้ผ่าน userinfo endpoint เมื่อใช้ โทเค็นทึบ (Opaque token) อย่างไรก็ตาม โทเค็นทึบไม่สามารถใช้เป็นโทเค็นองค์กรสำหรับเข้าถึงทรัพยากรเฉพาะองค์กร ดู โทเค็นทึบและองค์กร สำหรับรายละเอียดเพิ่มเติม
urn:logto:scope:organization_roles
| ชื่อการอ้างสิทธิ์ | ประเภท | คำอธิบาย | รวมในโทเค็น ID โดยค่าเริ่มต้น |
|---|---|---|---|
| organization_roles | string[] | บทบาทขององค์กรที่ผู้ใช้สังกัดในรูปแบบ <organization_id>:<role_name> | ✅ |
ทรัพยากร API และองค์กร
เราแนะนำให้อ่าน 🔐 การควบคุมการเข้าถึงตามบทบาท (RBAC) ก่อน เพื่อทำความเข้าใจแนวคิดพื้นฐานของ RBAC ใน Logto และวิธีตั้งค่าทรัพยากร API อย่างถูกต้อง
ขอสิทธิ์เข้าถึงทรัพยากร API
หากต้องการเข้าถึง API resource เฉพาะ ให้ใส่พารามิเตอร์ resource ใน device authorization request:
curl --request POST 'https://your.logto.endpoint/oidc/device/auth' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'scope=openid offline_access' \
--data-urlencode 'resource=https://your-api-resource-indicator'
เมื่อผู้ใช้อนุญาตเสร็จและคุณได้รับ refresh token แล้ว คุณสามารถขอโทเค็นการเข้าถึงแบบ JWT สำหรับ API resource ได้:
curl --request POST 'https://your.logto.endpoint/oidc/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=REFRESH_TOKEN' \
--data-urlencode 'resource=https://your-api-resource-indicator'
ผลลัพธ์จะมี JWT access_token ที่ aud ถูกตั้งเป็น API resource indicator ของคุณ
refresh_token จะมีเฉพาะเมื่อมี scope offline_access ใน device authorization request แรกเสมอ เก็บและใช้ refresh_token ล่าสุดเสมอ เพราะ Logto ใช้ token rotation
ดึงโทเค็นองค์กร
หาก องค์กร เป็นเรื่องใหม่สำหรับคุณ โปรดอ่าน 🏢 องค์กร (Multi-tenancy) เพื่อเริ่มต้น
หากต้องการขอข้อมูลที่เกี่ยวข้องกับองค์กร ให้เพิ่ม scope urn:logto:scope:organizations ใน device authorization request:
curl --request POST 'https://your.logto.endpoint/oidc/device/auth' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'scope=openid offline_access urn:logto:scope:organizations' \
--data-urlencode 'resource=urn:logto:resource:organizations'
เมื่อผู้ใช้ลงชื่อเข้าใช้แล้ว คุณสามารถดึงโทเค็นองค์กรโดยใช้ refresh token ได้:
curl --request POST 'https://your.logto.endpoint/oidc/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=REFRESH_TOKEN' \
--data-urlencode 'organization_id=your-organization-id'
ผลลัพธ์จะมี access token ที่ถูกจำกัดขอบเขตสำหรับองค์กรที่ระบุ
ทรัพยากร API ขององค์กร
หากต้องการขอโทเค็นการเข้าถึงสำหรับ API resource ภายในองค์กร ให้ใส่ทั้งพารามิเตอร์ resource และ organization_id:
curl --request POST 'https://your.logto.endpoint/oidc/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=REFRESH_TOKEN' \
--data-urlencode 'organization_id=your-organization-id' \
--data-urlencode 'resource=https://your-api-resource-indicator'