Quickstart (5분)
curl만 사용해서 가입 → OAuth 앱 등록 → Authorization Code + PKCE 플로우 → Access Token 발급 → 사용자 정보 조회 전체 경로를 5분 안에 돌려봅니다.
사전 준비
bash,curl,python3(또는openssl)- logi 서버 URL (이하
$LOGI로 표기). 로컬 개발은http://localhost:3000.
bash
export LOGI="http://localhost:3000"1. 개발자 계정 생성 + 로그인
bash
# 개발자 가입 (role=developer)
curl -s -c /tmp/cookies.txt -X POST "$LOGI/developer/signup" \
-H 'Content-Type: application/json' \
-d '{"user": {"email_address": "dev@example.com", "password": "correctHorseBatteryStaple"}}'
# 세션 쿠키가 /tmp/cookies.txt 에 저장됨 — 이후 요청에 -b 로 첨부2. Personal API Key 발급
/api/v1/applications는 PAK(Personal API Key) Bearer 인증을 사용합니다. 개발자 세션 쿠키로 PAK를 한 번 발급합니다.
bash
curl -s -b /tmp/cookies.txt -X POST "$LOGI/api/v1/me/api_keys" \
-H 'Content-Type: application/json' \
-d '{
"name": "Quickstart CLI",
"scopes": ["apps:manage", "apps:read"]
}' | tee /tmp/pak.json
PAK=$(python3 -c 'import json; print(json.load(open("/tmp/pak.json"))["plaintext"])')3. OAuth 앱 등록
bash
curl -s -X POST "$LOGI/api/v1/applications" \
-H "Authorization: Bearer $PAK" \
-H 'Content-Type: application/json' \
-d '{
"application": {
"name": "My App",
"redirect_uris": ["http://localhost:4000/auth/callback"],
"allowed_scopes": ["profile", "email"]
}
}' | tee /tmp/app.json
CLIENT_ID=$(python3 -c 'import json; print(json.load(open("/tmp/app.json"))["client_id"])')
SECRET=$(python3 -c 'import json; print(json.load(open("/tmp/app.json"))["client_secret"])')💡
웹 포털(/developer/applications/new)에서 폼으로 만들 수도 있습니다. 이 경우 UI가 client_secret을 한 번만 큰 글씨로 노출합니다.
4. 최종 사용자 가입 (테스트용)
bash
curl -s -c /tmp/user-cookies.txt -X POST "$LOGI/signup" \
-H 'Content-Type: application/json' \
-d '{"user": {"email_address": "user@example.com", "password": "correctHorseBatteryStaple", "device_uuid": "demo-device-1"}}'5. PKCE 페어 생성
bash
# 43–128자 URL-safe verifier
VERIFIER=$(openssl rand -hex 32)
# S256 challenge = BASE64URL(SHA256(verifier))
CHALLENGE=$(printf '%s' "$VERIFIER" \
| openssl dgst -sha256 -binary \
| python3 -c 'import sys,base64; print(base64.urlsafe_b64encode(sys.stdin.buffer.read()).rstrip(b"=").decode())')
echo "verifier = $VERIFIER"
echo "challenge = $CHALLENGE"6. Authorization 엔드포인트 호출
bash
REDIRECT="http://localhost:4000/auth/callback"
open "$LOGI/oauth/authorize?\
client_id=$CLIENT_ID&\
redirect_uri=$REDIRECT&\
response_type=code&\
scope=profile+email&\
state=random_xyz&\
code_challenge=$CHALLENGE&\
code_challenge_method=S256"브라우저에서 로그인 후 "허용" 클릭 → http://localhost:4000/auth/callback?code=...&state=random_xyz 로 리다이렉트됩니다. code 를 복사합니다.
7. Access Token 교환
bash
CODE="복사한_code"
curl -s -X POST "$LOGI/oauth/token" \
-d grant_type=authorization_code \
-d code=$CODE \
-d redirect_uri=$REDIRECT \
-d code_verifier=$VERIFIER \
-d client_id=$CLIENT_ID \
-d client_secret=$SECRET응답 예시:
json
{
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ii4uLiJ9...",
"token_type": "Bearer",
"expires_in": 900,
"refresh_token": "DWxB...",
"scope": "profile email"
}8. 사용자 정보 조회
bash
ACCESS_TOKEN="위_응답의_access_token"
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" "$LOGI/oauth/userinfo"
# → {"sub":"1","email":"user@example.com","email_verified":true,"identity_verified_level":0}9. (선택) Refresh Token Rotation
bash
REFRESH_TOKEN="위_응답의_refresh_token"
curl -s -X POST "$LOGI/oauth/token" \
-d grant_type=refresh_token \
-d refresh_token=$REFRESH_TOKEN \
-d client_id=$CLIENT_ID \
-d client_secret=$SECRET기존 RT는 즉시 무효화되고 새 AT/RT가 발급됩니다. 구 RT 재사용 시 체인 전체 revoke — 탈취 방어.
다음 단계
- OAuth Authorization Code + PKCE 상세 — 시퀀스 다이어그램 + RFC 링크
- 프레임워크 예제 — Next.js / Rails / Swift / Express
- Scope 레퍼런스 — 어떤 정보가 어떤 scope에 묶이는지
- 보안 Best Practices — redirect_uri, state, PKCE, rotation