สรุปสั้นๆ
สิทธิ์ OAuth 2.0 (scopes) คือสตริงที่กำหนดขอบเขตการอนุญาตว่า access token สามารถทำอะไรได้บ้าง ใช้รูปแบบ resource:action เช่น pets:read หรือ orders:write ร้องขอ scopes ในระหว่างการอนุญาต และตรวจสอบความถูกต้องที่ปลายทาง API Modern PetstoreAPI ใช้ scopes เพื่อกำหนดสิทธิ์การอ่าน/เขียนข้อมูลสัตว์เลี้ยง, คำสั่งซื้อ และข้อมูลผู้ใช้
บทนำ
แอปพลิเคชันภายนอกต้องการอ่านข้อมูลสินค้าคงคลังของร้านขายสัตว์เลี้ยงของคุณ แอปฯ นั้นควรมีสิทธิ์เข้าถึงทั้งหมดเพื่อสร้างคำสั่งซื้อ, ลบสัตว์เลี้ยง และจัดการผู้ใช้หรือไม่? ไม่ใช่ ควรได้รับอนุญาตให้อ่านข้อมูลสินค้าคงคลังเท่านั้น
สิทธิ์ OAuth 2.0 (scopes) ช่วยแก้ปัญหานี้ Scopes กำหนดว่า access token มีสิทธิ์อะไรบ้าง แอปฯ จะร้องขอ scope inventory:read API ของคุณจะตรวจสอบว่า token มี scope นี้ก่อนส่งข้อมูลกลับไป
Modern PetstoreAPI ใช้ scopes แบบละเอียดสำหรับทุกทรัพยากร: สัตว์เลี้ยง, คำสั่งซื้อ, สินค้าคงคลัง และผู้ใช้
หากคุณกำลังทดสอบ OAuth API, Apidog จะช่วยคุณทดสอบการตรวจสอบ scope และขั้นตอนการอนุญาต
OAuth 2.0 Scopes คืออะไร?
Scopes คือสตริงการอนุญาตที่รวมอยู่ใน OAuth access token
รูปแบบของ Scope
pets:read - อ่านข้อมูลสัตว์เลี้ยง
pets:write - สร้าง/อัปเดตสัตว์เลี้ยง
orders:read - อ่านคำสั่งซื้อ
orders:write - สร้างคำสั่งซื้อ
admin:all - สิทธิ์ผู้ดูแลระบบเต็มรูปแบบ
Scope ในขั้นตอน OAuth
1. คำร้องขอการอนุญาต:
GET /oauth/authorize?
client_id=app123&
scope=pets:read orders:read&
redirect_uri=https://app.com/callback
2. การยินยอมของผู้ใช้:
แอปฯ "PetFinder" ต้องการ:
- อ่านข้อมูลสัตว์เลี้ยงของคุณ
- อ่านคำสั่งซื้อของคุณ
[อนุญาต] [ปฏิเสธ]
3. Access Token:
{
"access_token": "eyJhbGc...",
"scope": "pets:read orders:read",
"expires_in": 3600
}
4. คำร้องขอ API:
GET /v1/pets
Authorization: Bearer eyJhbGc...
200 OK (ตรวจสอบ scope แล้ว)
Scopes ทำงานอย่างไร
Token มี Scopes
Access token รวมถึง scopes ที่ได้รับอนุญาต:
// JWT ที่ถอดรหัสแล้ว
{
"sub": "user-456",
"scope": "pets:read orders:read",
"exp": 1710331200
}
API ตรวจสอบ Scopes
app.get('/v1/pets', requireScope('pets:read'), async (req, res) => {
const pets = await getPets();
res.json(pets);
});
app.post('/v1/pets', requireScope('pets:write'), async (req, res) => {
const pet = await createPet(req.body);
res.status(201).json(pet);
});
function requireScope(requiredScope) {
return (req, res, next) => {
const token = extractToken(req);
const decoded = verifyToken(token);
if (!decoded.scope.includes(requiredScope)) {
return res.status(403).json({
error: 'insufficient_scope',
message: `ต้องใช้ scope: ${requiredScope}`
});
}
next();
};
}
การออกแบบ Scopes
หลักเกณฑ์การตั้งชื่อ Scope
รูปแบบ Resource:Action:
pets:read
pets:write
orders:read
orders:write
users:read
users:write
Scopes แบบละเอียด:
pets:read
pets:create
pets:update
pets:delete
Scopes แบบ Wildcard:
pets:* - การดำเนินการทั้งหมดเกี่ยวกับสัตว์เลี้ยง
*:read - อ่านทรัพยากรทั้งหมด
admin:* - สิทธิ์ผู้ดูแลระบบเต็มรูปแบบ
ลำดับชั้นของ Scope
admin:all
├── pets:*
│ ├── pets:read
│ ├── pets:write
│ └── pets:delete
├── orders:*
│ ├── orders:read
│ └── orders:write
└── users:*
├── users:read
└── users:write
การนำการตรวจสอบ Scope ไปใช้งาน
แนวทางการใช้ Middleware
function requireScopes(...requiredScopes) {
return (req, res, next) => {
const token = extractToken(req);
const decoded = verifyToken(token);
const tokenScopes = decoded.scope.split(' ');
const hasAllScopes = requiredScopes.every(scope =>
tokenScopes.includes(scope) || tokenScopes.includes('admin:all')
);
if (!hasAllScopes) {
return res.status(403).json({
error: 'insufficient_scope',
required: requiredScopes,
provided: tokenScopes
});
}
req.user = decoded;
next();
};
}
// การใช้งาน
app.get('/v1/pets', requireScopes('pets:read'), getPets);
app.post('/v1/pets', requireScopes('pets:write'), createPet);
app.delete('/v1/pets/:id', requireScopes('pets:delete'), deletePet);
แนวทางการใช้ Decorator (TypeScript)
function RequireScopes(...scopes: string[]) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = async function (...args: any[]) {
const req = args[0];
const res = args[1];
const token = extractToken(req);
const decoded = verifyToken(token);
if (!hasScopes(decoded.scope, scopes)) {
return res.status(403).json({ error: 'insufficient_scope' });
}
return originalMethod.apply(this, args);
};
};
}
// การใช้งาน
class PetsController {
@RequireScopes('pets:read')
async getPets(req, res) {
const pets = await this.petService.findAll();
res.json(pets);
}
@RequireScopes('pets:write')
async createPet(req, res) {
const pet = await this.petService.create(req.body);
res.status(201).json(pet);
}
}
Modern PetstoreAPI ใช้ Scopes อย่างไร
Scopes ที่ใช้งานได้
pets:read - อ่านข้อมูลสัตว์เลี้ยง
pets:write - สร้าง/อัปเดตสัตว์เลี้ยง
pets:delete - ลบสัตว์เลี้ยง
orders:read - อ่านคำสั่งซื้อ
orders:write - สร้างคำสั่งซื้อ
inventory:read - อ่านสินค้าคงคลัง
inventory:write - อัปเดตสินค้าคงคลัง
users:read - อ่านโปรไฟล์ผู้ใช้
users:write - อัปเดตโปรไฟล์ผู้ใช้
admin:all - สิทธิ์เข้าถึงทั้งหมด
การตรวจสอบ Scope
GET /v1/pets
Authorization: Bearer token_with_pets:read
200 OK
POST /v1/pets
Authorization: Bearer token_with_pets:read
403 Forbidden
{
"error": "insufficient_scope",
"required": ["pets:write"],
"provided": ["pets:read"]
}
ดู เอกสาร OAuth ของ Modern PetstoreAPI
การทดสอบ Scopes ด้วย Apidog
Apidog รองรับการทดสอบ OAuth scope:
- กำหนดค่าการรับรองความถูกต้อง OAuth 2.0
- ร้องขอ scopes เฉพาะ
- ทดสอบปลายทางด้วย scopes ที่แตกต่างกัน
- ตรวจสอบการตอบกลับ 403 สำหรับ scopes ที่ไม่เพียงพอ
แนวทางปฏิบัติที่ดีที่สุด
1. ใช้ scopes แบบละเอียด - pets:read ไม่ใช่ read_all
2. ปฏิบัติตามหลักเกณฑ์การตั้งชื่อ - รูปแบบ resource:action
3. จัดทำเอกสาร scopes ทั้งหมด - ระบุในเอกสาร API
4. ตรวจสอบความถูกต้องในทุกคำร้องขอ - อย่าเชื่อถือฝั่งไคลเอนต์
5. ส่งคืนข้อผิดพลาดที่ชัดเจน - แสดง scopes ที่จำเป็นเทียบกับ scopes ที่มี
6. ใช้หลักการให้สิทธิ์ขั้นต่ำสุด - ร้องขอ scopes ขั้นต่ำที่จำเป็น
บทสรุป
OAuth 2.0 scopes ช่วยให้สามารถควบคุมการเข้าถึงได้อย่างละเอียด ใช้รูปแบบ resource:action, ตรวจสอบความถูกต้องในทุกคำร้องขอ และจัดทำเอกสาร scopes ทั้งหมด Modern PetstoreAPI แสดงให้เห็นถึงการนำ scope ไปใช้งานที่พร้อมใช้งานจริง
คำถามที่พบบ่อย
Scopes กับ Roles ต่างกันอย่างไร?
Scopes คือสิทธิ์สำหรับ access token Roles คือกลุ่มผู้ใช้ที่มีสิทธิ์ที่ถูกกำหนดไว้
สามารถมีหลาย scopes ได้หรือไม่?
ได้ โดยคั่นด้วยช่องว่าง: pets:read orders:read users:write
จะเพิกถอน scopes ได้อย่างไร?
เพิกถอน access token หรือออก token ใหม่ด้วย scopes ที่แตกต่างกัน
Scopes ควรอยู่ใน JWT หรือไม่?
ได้ ควรใส่ไว้ใน scope claim สำหรับการตรวจสอบความถูกต้องแบบ stateless
Scopes ควรมีความละเอียดแค่ไหน?
สร้างความสมดุลระหว่างความละเอียดกับการใช้งาน pets:read และ pets:write มักจะเพียงพอแล้ว
