TL;DR
OAuth 2.0 스코프는 액세스 토큰이 수행할 수 있는 작업을 정의하는 권한 문자열입니다. `pets:read` 또는 `orders:write`와 같이 `리소스:액션` 형식을 사용하세요. 인증 중 스코프를 요청하고 API 엔드포인트에서 유효성을 검사합니다. Modern PetstoreAPI는 반려동물, 주문 및 사용자 데이터에 대한 읽기/쓰기 액세스를 위해 스코프를 구현합니다.
서론
타사 앱이 펫샵의 재고를 읽으려 합니다. 이 앱이 주문을 생성하고, 반려동물을 삭제하고, 사용자를 관리하는 모든 권한을 가져야 할까요? 아닙니다. 재고만 읽어야 합니다.
OAuth 2.0 스코프가 이를 해결합니다. 스코프는 액세스 토큰이 어떤 권한을 가지는지 정의합니다. 앱은 `inventory:read` 스코프를 요청합니다. API는 데이터를 반환하기 전에 토큰이 이 스코프를 가지고 있는지 유효성을 검사합니다.
Modern PetstoreAPI는 모든 리소스(반려동물, 주문, 재고, 사용자)에 대한 세분화된 스코프를 구현합니다.
OAuth API를 테스트하는 경우, Apidog는 스코프 유효성 검사 및 인증 흐름을 테스트하는 데 도움을 줍니다.
OAuth 2.0 스코프란 무엇인가요?
스코프는 OAuth 액세스 토큰에 포함된 권한 문자열입니다.
스코프 형식
pets:read - 반려동물 데이터 읽기
pets:write - 반려동물 생성/업데이트
orders:read - 주문 읽기
orders:write - 주문 생성
admin:all - 전체 관리자 접근
OAuth 흐름에서의 스코프
1. 인증 요청:
GET /oauth/authorize?
client_id=app123&
scope=pets:read orders:read&
redirect_uri=https://app.com/callback
2. 사용자 동의:
앱 "PetFinder"가 다음을 원합니다:
- 당신의 반려동물 읽기
- 당신의 주문 읽기
[허용] [거부]
3. 액세스 토큰:
{
"access_token": "eyJhbGc...",
"scope": "pets:read orders:read",
"expires_in": 3600
}
4. API 요청:
GET /v1/pets
Authorization: Bearer eyJhbGc...
200 OK (스코프 유효성 검사됨)
스코프 작동 방식
토큰에 스코프 포함
액세스 토큰에 부여된 스코프가 포함됩니다:
// 디코딩된 JWT
{
"sub": "user-456",
"scope": "pets:read orders:read",
"exp": 1710331200
}
API가 스코프 유효성 검사
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: `Requires scope: ${requiredScope}`
});
}
next();
};
}
스코프 설계
스코프 명명 규칙
리소스:액션 패턴:
pets:read
pets:write
orders:read
orders:write
users:read
users:write
세분화된 스코프:
pets:read
pets:create
pets:update
pets:delete
와일드카드 스코프:
pets:* - 모든 반려동물 작업
*:read - 모든 리소스 읽기
admin:* - 전체 관리자 접근
스코프 계층 구조
admin:all
├── pets:*
│ ├── pets:read
│ ├── pets:write
│ └── pets:delete
├── orders:*
│ ├── orders:read
│ └── orders:write
└── users:*
├── users:read
└── users:write
스코프 유효성 검사 구현
미들웨어 방식
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);
데코레이터 방식 (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가 스코프를 사용하는 방법
사용 가능한 스코프
pets:read - 반려동물 데이터 읽기
pets:write - 반려동물 생성/업데이트
pets:delete - 반려동물 삭제
orders:read - 주문 읽기
orders:write - 주문 생성
inventory:read - 재고 읽기
inventory:write - 재고 업데이트
users:read - 사용자 프로필 읽기
users:write - 사용자 프로필 업데이트
admin:all - 전체 접근
스코프 유효성 검사
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"]
}
Modern PetstoreAPI OAuth 문서를 참조하세요.
Apidog로 스코프 테스트하기
Apidog는 OAuth 스코프 테스트를 지원합니다:
- OAuth 2.0 인증 설정
- 특정 스코프 요청
- 다른 스코프로 엔드포인트 테스트
- 불충분한 스코프에 대한 403 응답 유효성 검사
모범 사례
1. 세분화된 스코프 사용 - `pets:read` ( `read_all` 아님)
2. 명명 규칙 준수 - `리소스:액션` 형식
3. 모든 스코프 문서화 - API 문서에 나열
4. 모든 요청에서 유효성 검사 - 클라이언트를 신뢰하지 마세요
5. 명확한 오류 반환 - 필요한 스코프와 제공된 스코프 표시
6. 최소 권한 사용 - 필요한 최소 스코프 요청
결론
OAuth 2.0 스코프는 세분화된 접근 제어를 제공합니다. `리소스:액션` 형식을 사용하고, 모든 요청에서 유효성을 검사하며, 모든 스코프를 문서화하세요. Modern PetstoreAPI는 실제 운영 환경에 적합한 스코프 구현을 보여줍니다.
FAQ
스코프와 역할의 차이점은 무엇인가요?
스코프는 액세스 토큰에 대한 권한입니다. 역할은 할당된 권한을 가진 사용자 그룹입니다.
여러 스코프를 가질 수 있나요?
네, 공백으로 구분합니다: `pets:read orders:read users:write`
스코프는 어떻게 취소하나요?
액세스 토큰을 취소하거나 다른 스코프를 가진 새 토큰을 발급하세요.
스코프가 JWT에 있어야 하나요?
네, 무상태 유효성 검사를 위해 `scope` 클레임에 포함하세요.
스코프는 얼마나 세분화되어야 하나요?
세분성과 사용성 사이의 균형을 맞추세요. 일반적으로 `pets:read`와 `pets:write`로 충분합니다.
