TÓM TẮT
Phạm vi (scopes) của OAuth 2.0 là các chuỗi quyền xác định những gì mã thông báo truy cập (access token) có thể thực hiện. Sử dụng định dạng tài_nguyên:hành_động như pets:read hoặc orders:write. Yêu cầu phạm vi trong quá trình ủy quyền, xác thực chúng tại các điểm cuối API. Modern PetstoreAPI triển khai các phạm vi cho quyền đọc/ghi dữ liệu thú cưng, đơn hàng và người dùng.
Giới thiệu
Một ứng dụng bên thứ ba muốn đọc kho hàng của cửa hàng thú cưng của bạn. Liệu nó có nên có toàn quyền tạo đơn hàng, xóa thú cưng và quản lý người dùng không? Không. Nó chỉ nên đọc kho hàng.
Phạm vi OAuth 2.0 giải quyết vấn đề này. Phạm vi xác định các quyền mà mã thông báo truy cập có. Ứng dụng yêu cầu phạm vi inventory:read. API của bạn xác thực mã thông báo có phạm vi này trước khi trả về dữ liệu.
Modern PetstoreAPI triển khai các phạm vi chi tiết cho tất cả tài nguyên: thú cưng, đơn hàng, kho hàng và người dùng.
Nếu bạn đang kiểm thử các API OAuth, Apidog sẽ giúp bạn kiểm thử xác thực phạm vi và luồng ủy quyền.
Phạm vi OAuth 2.0 là gì?
Phạm vi là các chuỗi quyền được bao gồm trong mã thông báo truy cập OAuth.
Định dạng Phạm vi
pets:read - Đọc dữ liệu thú cưng
pets:write - Tạo/cập nhật thú cưng
orders:read - Đọc đơn hàng
orders:write - Tạo đơn hàng
admin:all - Toàn quyền quản trị
Phạm vi trong Luồng OAuth
1. Yêu cầu ủy quyền:
GET /oauth/authorize?
client_id=app123&
scope=pets:read orders:read&
redirect_uri=https://app.com/callback
2. Sự đồng ý của người dùng:
Ứng dụng "PetFinder" muốn:
- Đọc danh sách thú cưng của bạn
- Đọc đơn hàng của bạn
[Cho phép] [Từ chối]
3. Mã thông báo truy cập:
{
"access_token": "eyJhbGc...",
"scope": "pets:read orders:read",
"expires_in": 3600
}
4. Yêu cầu API:
GET /v1/pets
Authorization: Bearer eyJhbGc...
200 OK (phạm vi đã được xác thực)
Cách Phạm vi hoạt động
Mã thông báo chứa các Phạm vi
Mã thông báo truy cập bao gồm các phạm vi được cấp:
// JWT đã giải mã
{
"sub": "user-456",
"scope": "pets:read orders:read",
"exp": 1710331200
}
API xác thực các Phạm vi
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: `Yêu cầu phạm vi: ${requiredScope}`
});
}
next();
};
}
Thiết kế Phạm vi
Quy ước đặt tên Phạm vi
Mô hình Tài nguyên:Hành động:
pets:read
pets:write
orders:read
orders:write
users:read
users:write
Phạm vi chi tiết:
pets:read
pets:create
pets:update
pets:delete
Phạm vi ký tự đại diện:
pets:* - Tất cả các thao tác với thú cưng
*:read - Đọc tất cả tài nguyên
admin:* - Toàn quyền quản trị
Phân cấp Phạm vi
admin:all
├── pets:*
│ ├── pets:read
│ ├── pets:write
│ └── pets:delete
├── orders:*
│ ├── orders:read
│ └── orders:write
└── users:*
├── users:read
└── users:write
Triển khai Xác thực Phạm vi
Cách tiếp cận 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();
};
}
// Cách sử dụng
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);
Cách tiếp cận 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);
};
};
}
// Cách sử dụng
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);
}
}
Cách Modern PetstoreAPI sử dụng Phạm vi
Các Phạm vi khả dụng
pets:read - Đọc dữ liệu thú cưng
pets:write - Tạo/cập nhật thú cưng
pets:delete - Xóa thú cưng
orders:read - Đọc đơn hàng
orders:write - Tạo đơn hàng
inventory:read - Đọc kho hàng
inventory:write - Cập nhật kho hàng
users:read - Đọc hồ sơ người dùng
users:write - Cập nhật hồ sơ người dùng
admin:all - Toàn quyền truy cập
Xác thực Phạm vi
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"]
}
Xem tài liệu OAuth của Modern PetstoreAPI.
Kiểm thử Phạm vi với Apidog
Apidog hỗ trợ kiểm thử phạm vi OAuth:
- Cấu hình xác thực OAuth 2.0
- Yêu cầu các phạm vi cụ thể
- Kiểm thử các điểm cuối với các phạm vi khác nhau
- Xác thực phản hồi 403 cho các phạm vi không đủ
Các Thực hành tốt nhất
1. Sử dụng phạm vi chi tiết - pets:read chứ không phải read_all
2. Tuân thủ quy ước đặt tên - định dạng tài_nguyên:hành_động
3. Tài liệu hóa tất cả các phạm vi - Liệt kê trong tài liệu API
4. Xác thực trên mọi yêu cầu - Không tin tưởng client
5. Trả về lỗi rõ ràng - Hiển thị phạm vi yêu cầu so với phạm vi được cung cấp
6. Sử dụng quyền hạn tối thiểu - Yêu cầu phạm vi tối thiểu cần thiết
Kết luận
Phạm vi OAuth 2.0 cung cấp khả năng kiểm soát truy cập chi tiết. Sử dụng định dạng tài_nguyên:hành_động, xác thực trên mọi yêu cầu và tài liệu hóa tất cả các phạm vi. Modern PetstoreAPI trình bày một triển khai phạm vi sẵn sàng cho sản xuất.
Câu hỏi thường gặp
Sự khác biệt giữa phạm vi và vai trò là gì?
Phạm vi là các quyền dành cho mã thông báo truy cập. Vai trò là các nhóm người dùng với các quyền được gán.
Bạn có thể có nhiều phạm vi không?
Có, hãy phân tách bằng dấu cách: pets:read orders:read users:write
Làm thế nào để thu hồi phạm vi?
Thu hồi mã thông báo truy cập hoặc cấp một mã thông báo mới với các phạm vi khác.
Phạm vi có nên có trong JWT không?
Có, hãy bao gồm trong trường scope để xác thực không trạng thái.
Phạm vi nên chi tiết đến mức nào?
Cân bằng giữa sự chi tiết và tính khả dụng. pets:read và pets:write thường là đủ.
