콘텐츠로 이동

Native Application 플로

데스크톱 앱, CLI, CAD 플러그인처럼 자신의 머신에서 로그인한 사용자를 대신하여 작동하는 연동에는 Native app 플로를 사용하세요. 이는 PKCE를 사용한 Authorization Code, localhost 루프백 리디렉션, 클라이언트 시크릿 없음을 사용합니다. 이 가이드는 OAuth 애플리케이션 생성부터 첫 API 호출까지의 설정을 안내합니다.

전제 조건, 네트워크 접근, 보안 모델을 아직 다루지 않았다면 먼저 시작하기 가이드를 검토하세요.


  1. OAuth 애플리케이션 생성: Prevu3D에 네이티브 앱을 등록하고 Client ID를 받습니다
  2. 사용자 접근 구성: 로그인하는 사용자가 필요한 데이터에 도달할 수 있는지 확인합니다
  3. PKCE로 인증: 브라우저 동의 페이지를 통해 사용자를 보냅니다
  4. 코드를 토큰으로 교환: 인증 코드를 액세스 토큰으로 교환합니다
  5. API URL 찾기: 조직의 기본 URL을 검색합니다(지역에 따라 다릅니다)
  6. 첫 호출하기: 간단한 요청으로 연결을 테스트합니다
  1. Prevu3D Platform에 로그인합니다.
  2. SettingsOAuth로 이동합니다.
  3. 클릭하여 새 애플리케이션을 생성합니다.
  4. Authorization Code flow를 활성화합니다.
  5. Native Application을 활성화합니다. 리디렉션 URI는 http://localhost로 고정됩니다.
  6. 클라이언트 시크릿을 상태로 둡니다. 네이티브 앱은 이를 사용하지 않습니다.
  7. Client ID를 복사하고 안전하게 저장합니다. 모든 인증에 필요합니다.

네이티브 플로는 서비스 사용자가 아닌 로그인한 사용자로서 API를 호출합니다. 이는 보안 모델의 계층 2와 3을 다룹니다: 사용자가 볼 수 있는 노드(콘텐츠 접근)와 그것으로 할 수 있는 작업(역할/권한 접근).

  1. 로그인할 사용자가 사용 사례에 필요한 노드(조직, 디비전, 사이트 등)에 대한 콘텐츠 접근 권한이 있는지 확인하세요.
  2. 사용자가 연동에 필요한 권한(읽기, 편집, 관리 등)이 있는 해당 노드에 대한 역할을 가지고 있는지 확인하세요.

동의 화면에서, 사용자는 승인할 조직도 선택합니다. 사용자가 거기서 승인하는 스코프는 계층 1을 구성합니다.

네이티브 앱은 시크릿을 유지할 수 없으므로, 플로는 인증 코드를 보호하기 위해 PKCE를 사용합니다. 동의 페이지를 열기 전에, 세 가지 값을 생성하세요:

방법
code_verifier무작위 문자열, 43~128자
code_challenge패딩 없는 BASE64URL(SHA256(code_verifier))
state무작위 문자열; 리디렉션이 돌아올 때 검증

여유 포트에서 로컬 루프백 서버를 시작한 다음, 사용자의 브라우저를 동의 페이지로 엽니다.

동의 URL: https://cloud.prevu3d.com/oauth

쿼리 매개변수 (모두 필수):

매개변수
response_typecode
code_challenge_methodS256
client_idClient ID
redirect_uri루프백 URI, 예: http://localhost:8765(포트 포함, 후행 슬래시 없음)
scopes스코프당 하나, 예: scopes=read:basic&scopes=read:hierarchy
code_challenge위의 PKCE 챌린지
state무작위 state 값

사용자가 로그인하고 Allow를 클릭하면, 브라우저가 루프백 URI로 리디렉션됩니다:

http://localhost:8765/?code={authorization_code}&state={state}

사용자가 접근을 거부하면, 대신 ?error=access_denied를 받습니다. 인증 코드는 60초 후에 만료되므로, 즉시 교환하세요.

4단계: 코드를 액세스 토큰으로 교환

섹션 제목: “4단계: 코드를 액세스 토큰으로 교환”

엔드포인트: POST https://cloud-api.prevu3d.com/oauth/token

헤더: Content-Type: application/x-www-form-urlencoded

본문:

필드
grant_typeauthorization_code
client_idClient ID
code리디렉션의 코드
redirect_uri3단계에서 사용한 동일한 URI(포트 포함)
code_verifier원래 PKCE verifier

네이티브 앱에는 클라이언트 시크릿을 보내지 마세요.

예제 응답:

{
"hasError": false,
"expires_in": 86400,
"access_token": "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "..."
}

두 토큰을 모두 저장합니다. API 호출에는 access_token을 사용합니다. 만료되면, grant_type=refresh_token과 저장된 refresh_token으로 새 토큰을 요청합니다(클라이언트 시크릿 불필요).

조직의 apiUrl을 얻으려면 검색 엔드포인트를 호출합니다. 지역 URL을 하드코딩하지 마세요.

엔드포인트: GET https://cloud-api.prevu3d.com/oauth/api-info

헤더: Authorization: Bearer <your_access_token>

예제 응답:

{
"user": { "..." : "..." },
"organization": {
"id": "217ebd23-ec54-4af0-a6d6-4a441a6d1966",
"name": "Test Organization"
},
"scopes": ["read:basic", "read:hierarchy"],
"apiUrl": "https://api-ue1.prevu3d.com/realityconnect-api"
}

모든 API 호출의 기본으로 apiUrl 값을 사용합니다. 많은 엔드포인트에 필요한 organization.id도 기록해 두세요.

이제 필요한 모든 것이 있습니다: 액세스 토큰과 기본 URL. 조직의 디비전을 검색하는 간단한 요청을 해봅시다:

GET {apiUrl}/v1/nodes/{organization_id}/browse
Authorization: Bearer <your_access_token>

실제 값을 사용한 예제:

GET https://api-ue1.prevu3d.com/realityconnect-api/v1/nodes/217ebd23-ec54-4af0-a6d6-4a441a6d1966/browse HTTP/1.1
Authorization: Bearer eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9...

디비전 목록을 포함하는 성공적인 응답은 연동이 올바르게 작동하고 있음을 확인합니다.

다음은 위의 모든 작업을 수행하는 완전한 스크립트입니다. client_id를 Client ID로 바꾼 다음, 실행하세요. 임시 루프백 서버를 시작하고, 브라우저에서 동의 페이지를 열고, 승인하면 조직의 디비전을 출력합니다.

import base64
import hashlib
import json
import secrets
import webbrowser
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import parse_qs, quote, urlencode, urlparse
import requests
client_id = "your-client-id"
base_url = "https://cloud-api.prevu3d.com"
scopes = ["read:basic", "read:hierarchy"]
# Step 1: Authorize with PKCE and get an access token
verifier = secrets.token_urlsafe(48)[:64]
challenge = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode()).digest()).decode().rstrip("=")
state = secrets.token_urlsafe(32)
auth_result = {"code": None, "state": None}
class Handler(BaseHTTPRequestHandler):
def log_message(self, *args):
pass
def do_GET(self):
query = parse_qs(urlparse(self.path).query)
auth_result["code"] = query.get("code", [None])[0]
auth_result["state"] = query.get("state", [None])[0]
self.send_response(200)
self.end_headers()
server = HTTPServer(("localhost", 0), Handler)
redirect_uri = f"http://localhost:{server.server_address[1]}"
consent_params = urlencode(
{
"response_type": "code",
"code_challenge_method": "S256",
"client_id": client_id,
"redirect_uri": redirect_uri,
"code_challenge": challenge,
"state": state,
}
)
consent_url = f"{base_url.replace('cloud-api.', 'cloud.', 1)}/oauth?{consent_params}"
consent_url += "&" + "&".join(f"scopes={quote(scope)}" for scope in scopes)
webbrowser.open(consent_url)
server.handle_request()
if auth_result["state"] != state:
raise RuntimeError("Invalid state")
if not auth_result["code"]:
raise RuntimeError("Authorization failed")
token_response = requests.post(
f"{base_url}/oauth/token",
data={
"grant_type": "authorization_code",
"client_id": client_id,
"code": auth_result["code"],
"redirect_uri": redirect_uri,
"code_verifier": verifier,
},
)
token_response.raise_for_status()
access_token = token_response.json()["access_token"]
# Step 2: Get your API URL and org ID
api_info = requests.get(
f"{base_url}/oauth/api-info",
headers={"Authorization": f"Bearer {access_token}"},
).json()
api_url = api_info["apiUrl"]
organization_id = api_info["organization"]["id"]
# Step 3: Browse divisions
divisions = requests.get(
f"{api_url}/v1/nodes/{organization_id}/browse",
headers={"Authorization": f"Bearer {access_token}"},
).json()
print("Divisions:", json.dumps(divisions, indent=2))
다음이 표시되면…이것을 시도하세요…
Allow 클릭 후 Invalid OAuth grant request네이티브 앱의 Client ID(Authorization Code 및 Native app 활성화됨)를 사용하고 있는지 확인하세요. Client Credentials 전용 앱은 리디렉션 URI가 없으며 이 플로를 완료할 수 없습니다.
쿼리 매개변수에 대한 동의 페이지 오류response_type=code, code_challenge_method=S256, statescopes를 포함한 모든 필수 매개변수가 있는지 확인하세요.
토큰 교환 시 400 또는 403코드가 만료되었거나(60초 제한), redirect_uri가 3단계와 일치하지 않거나, PKCE verifier와 challenge가 일치하지 않을 수 있습니다.
API를 호출할 때 403 Forbidden요청은 세 가지 보안 계층을 모두 통과해야 합니다. OAuth 스코프, 로그인한 사용자의 콘텐츠 접근, 대상 노드에 대한 사용자의 역할을 확인하세요.
API 응답에 잘못된 조직사용자가 동의 화면에서 조직을 선택합니다. 다시 인증하고 올바른 것을 선택하세요.

연결, DNS, API URL 문제는 시작하기를 참조하세요.

  • 저장된 refresh 토큰과 함께 grant_type=refresh_token을 사용하여 토큰이 만료되기 전에 새로 고치는 로직을 추가하세요.
  • 사용 가능한 모든 작업을 보려면 API 참조를 살펴보세요.