OAuth 2.0 인증
ONDA API 인증 체계와 토큰 관리 방법을 안내합니다.
OAuth 2.0 인증#
ONDA API는 OAuth 2.0 프로토콜을 사용하여 안전한 인증을 제공합니다. API 호출 시 모든 요청에 Access Token을 포함해야 합니다.
OAuth 2.0 개요#
OAuth 2.0은 산업 표준 인증 프로토콜로, 사용자 비밀번호를 직접 공유하지 않고도 안전하게 API 접근 권한을 위임할 수 있습니다.
서버 간 통신에 적합한 플로우입니다. 사용자 상호작용 없이 서비스 자체의 권한으로 API에 접근합니다.
ONDA API는 두 가지 OAuth 플로우를 지원합니다:
| 플로우 | 사용 케이스 | 보안 수준 |
|---|---|---|
| Client Credentials | 서버-투-서버 통신 (백엔드) | 높음 |
| Authorization Code + PKCE | 사용자 인증이 필요한 경우 (프론트엔드) | 매우 높음 |
Client Credentials 플로우#
서버-투-서버 통신에 적합한 플로우입니다. 대부분의 채널 파트너가 사용합니다.
1. Access Token 발급#
Client Credentials 플로우
cURL:
curl -X POST https://api.onda.me/v1/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "scope=read write"
Python:
import requests
response = requests.post(
"https://api.onda.me/v1/oauth/token",
data={
"grant_type": "client_credentials",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"scope": "read write",
},
)
token_data = response.json()
access_token = token_data["access_token"]
refresh_token = token_data["refresh_token"]
expires_in = token_data["expires_in"] # 900 (15분)
Node.js:
const response = await fetch("https://api.onda.me/v1/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: "YOUR_CLIENT_ID",
client_secret: "YOUR_CLIENT_SECRET",
scope: "read write",
}),
});
const tokenData = await response.json();
const { access_token, refresh_token, expires_in } = tokenData;
요청 파라미터:
| 파라미터 | 필수 | 설명 |
|---|---|---|
grant_type | ✅ | client_credentials 고정 |
client_id | ✅ | 채널 생성 시 발급받은 Client ID |
client_secret | ✅ | 채널 생성 시 발급받은 Client Secret |
scope | 요청 권한 범위 (기본값: read write) |
응답:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjaGFubmVsXzEyMzQ1NiIsImlhdCI6MTcwOTM3MDAwMCwiZXhwIjoxNzA5MzcwOTAwLCJzY29wZSI6InJlYWQgd3JpdGUifQ...",
"token_type": "Bearer",
"expires_in": 900,
"refresh_token": "rt_abc123def456",
"scope": "read write"
}
2. API 호출#
발급받은 Access Token을 Authorization 헤더에 포함하여 API를 호출합니다.
API 호출 예시
cURL:
curl -X GET "https://api.onda.me/v1/properties" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Accept: application/json"
Python:
import requests
headers = {
"Authorization": f"Bearer {access_token}",
"Accept": "application/json",
}
response = requests.get(
"https://api.onda.me/v1/properties",
headers=headers,
)
Node.js:
const response = await fetch("https://api.onda.me/v1/properties", {
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
},
});
Authorization Code + PKCE 플로우#
사용자 인증이 필요한 경우 사용하는 플로우입니다. PKCE(Proof Key for Code Exchange)를 사용하여 보안을 강화합니다.
1. Authorization URL 생성#
import secrets
import hashlib
import base64
# Code Verifier 생성 (43-128자의 랜덤 문자열)
code_verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).decode('utf-8').rstrip('=')
# Code Challenge 생성 (SHA256 해시)
code_challenge = base64.urlsafe_b64encode(
hashlib.sha256(code_verifier.encode('utf-8')).digest()
).decode('utf-8').rstrip('=')
# Authorization URL
auth_url = (
"https://api.onda.me/v1/oauth/authorize"
f"?client_id=YOUR_CLIENT_ID"
f"&redirect_uri=https://your-app.com/callback"
f"&response_type=code"
f"&scope=read write"
f"&code_challenge={code_challenge}"
f"&code_challenge_method=S256"
)
# 사용자를 이 URL로 리다이렉트
print(f"Redirect to: {auth_url}")
2. Authorization Code로 토큰 교환#
사용자가 인증을 완료하면 redirect_uri로 Authorization Code가 전달됩니다.
import requests
# redirect_uri로 전달된 code 파라미터
authorization_code = "ac_xyz789"
response = requests.post(
"https://api.onda.me/v1/oauth/token",
data={
"grant_type": "authorization_code",
"client_id": "YOUR_CLIENT_ID",
"code": authorization_code,
"redirect_uri": "https://your-app.com/callback",
"code_verifier": code_verifier, # 1단계에서 생성한 값
},
)
token_data = response.json()
access_token = token_data["access_token"]
refresh_token = token_data["refresh_token"]
토큰 라이프사이클#
Access Token#
- 유효 기간: 15분 (900초)
- 용도: API 호출 인증
- 저장 위치: 메모리 (권장) 또는 안전한 스토리지
Refresh Token#
- 유효 기간: 30일
- 용도: Access Token 갱신
- 저장 위치: 암호화된 데이터베이스 또는 Secure Storage
Refresh Token은 절대 클라이언트 측(브라우저)에 저장하지 마세요. 서버에서만 관리해야 합니다.
토큰 갱신#
Access Token이 만료되기 전에 Refresh Token을 사용하여 갱신합니다.
토큰 갱신
Python:
import requests
response = requests.post(
"https://api.onda.me/v1/oauth/token",
data={
"grant_type": "refresh_token",
"refresh_token": "YOUR_REFRESH_TOKEN",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
},
)
new_token_data = response.json()
new_access_token = new_token_data["access_token"]
new_refresh_token = new_token_data["refresh_token"]
# 기존 토큰을 새 토큰으로 교체
access_token = new_access_token
refresh_token = new_refresh_token
Node.js:
const response = await fetch("https://api.onda.me/v1/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "refresh_token",
refresh_token: refreshToken,
client_id: "YOUR_CLIENT_ID",
client_secret: "YOUR_CLIENT_SECRET",
}),
});
const newTokenData = await response.json();
accessToken = newTokenData.access_token;
refreshToken = newTokenData.refresh_token;
자동 갱신 구현 예시#
import time
import requests
class TokenManager:
def __init__(self, client_id, client_secret):
self.client_id = client_id
self.client_secret = client_secret
self.access_token = None
self.refresh_token = None
self.expires_at = 0
def get_token(self):
"""현재 유효한 토큰 반환 (필요시 자동 갱신)"""
if time.time() >= self.expires_at - 60: # 만료 1분 전에 갱신
self._refresh_token()
return self.access_token
def _refresh_token(self):
"""토큰 갱신"""
response = requests.post(
"https://api.onda.me/v1/oauth/token",
data={
"grant_type": "refresh_token" if self.refresh_token else "client_credentials",
"refresh_token": self.refresh_token,
"client_id": self.client_id,
"client_secret": self.client_secret,
},
)
data = response.json()
self.access_token = data["access_token"]
self.refresh_token = data["refresh_token"]
self.expires_at = time.time() + data["expires_in"]
보안 가이드라인#
Client Secret 보호#
절대 금지: Client Secret을 클라이언트 측 코드(JavaScript, 모바일 앱 등)에 포함하지 마세요.
✅ 올바른 방법:
- 서버 환경 변수에 저장
- AWS Secrets Manager, HashiCorp Vault 등 사용
.env파일 사용 시.gitignore에 추가
❌ 잘못된 방법:
- Git 저장소에 커밋
- 브라우저 JavaScript에 노출
- 모바일 앱 코드에 하드코딩
Token 저장#
| 토큰 유형 | 서버 | 브라우저 | 모바일 앱 |
|---|---|---|---|
| Access Token | 메모리 또는 Redis | 메모리 | Keychain/Keystore |
| Refresh Token | 암호화된 DB | ❌ 저장 금지 | Secure Storage |
| Client Secret | 환경 변수 | ❌ 저장 금지 | ❌ 저장 금지 |
HTTPS 필수#
모든 API 호출은 HTTPS를 통해서만 가능합니다. HTTP 요청은 자동으로 거부됩니다.
IP 화이트리스트#
파트너 센터에서 허용할 IP 주소를 등록하여 추가 보안을 적용할 수 있습니다.
에러 코드#
401 Unauthorized#
{
"error": "invalid_token",
"error_description": "The access token is invalid or has expired"
}
원인: 토큰이 만료되었거나 유효하지 않음 해결: 토큰을 재발급하거나 갱신
403 Forbidden#
{
"error": "insufficient_scope",
"error_description": "The request requires higher privileges than provided by the access token"
}
원인: 토큰의 scope가 부족함 해결: 필요한 scope를 포함하여 토큰을 재발급
400 Bad Request (Invalid Grant)#
{
"error": "invalid_grant",
"error_description": "The provided refresh token is invalid, expired, or revoked"
}
원인: Refresh Token이 만료되었거나 무효화됨 해결: Client Credentials 플로우로 새 토큰 발급