ネイティブアプリケーションフロー
Native appフローは、デスクトップアプリ、CLI、CADプラグインなど、サインインしたユーザーのマシン上でユーザーの代理として動作する統合に使用します。PKCE付きAuthorization Code、localhostループバックリダイレクト、クライアントシークレットなしを使用します。このガイドでは、OAuthアプリケーションの作成から最初のAPI呼び出しまでを説明します。
前提条件、ネットワークアクセス、セキュリティモデルをまだ確認していない場合は、先にセットアップガイドを読んでください。
実施する内容
Section titled “実施する内容”- OAuthアプリケーションを作成する:Prevu3Dにネイティブアプリを登録し、Client IDを取得する
- ユーザーアクセスを設定する:サインインするユーザーが必要なデータにアクセスできることを確認する
- PKCEで承認する:ブラウザの同意ページにユーザーを送る
- コードをトークンと交換する:認可コードをアクセストークンと交換する
- API URLを見つける:組織のベースURLを確認する(リージョンによって異なります)
- 最初の呼び出しを行う:簡単なリクエストで接続をテストする
ステップ1:OAuthアプリケーションを作成する
Section titled “ステップ1:OAuthアプリケーションを作成する”- Prevu3D Platform(またはプレプロダクション環境)にログインします。
- Settings → OAuthに移動します。
- クリックして新しいアプリケーションを作成します。
- Authorization Code flowを有効にします。
- Native Applicationを有効にします。リダイレクトURIは
http://localhostに固定されています。 - クライアントシークレットはオフのままにします。ネイティブアプリでは使用しません。
- Client IDをコピーして安全に保管します。すべての認可で必要になります。
ステップ2:ユーザーアクセスを設定する
Section titled “ステップ2:ユーザーアクセスを設定する”ネイティブフローはサービスユーザーではなく、サインインしたユーザーとしてAPIを呼び出します。これはセキュリティモデルのレイヤー2と3に該当します。ユーザーがどのノードを参照できるか(コンテンツアクセス)、およびそれに対して何ができるか(ロール / 権限アクセス)です。
- サインインするユーザーが、ユースケースに必要なノード(組織、ディビジョン、サイトなど)へのコンテンツアクセスを持っていることを確認します。
- ユーザーがそれらのノードで、統合に必要な権限を持つロールを持っていることを確認します(読み取り、編集、管理など)。
同意画面では、ユーザーは承認する組織も選択します。そこで承認されたスコープがレイヤー1を設定します。
ステップ3:PKCEで承認する
Section titled “ステップ3:PKCEで承認する”ネイティブアプリはシークレットを保持できないため、このフローでは認可コードを保護するためにPKCEを使用します。同意ページを開く前に、次の3つの値を生成します。
| 値 | 方法 |
|---|---|
code_verifier | 43〜128文字のランダム文字列 |
code_challenge | パディングなしのBASE64URL(SHA256(code_verifier)) |
state | ランダム文字列。リダイレクト時に検証する |
空いているポートでローカルループバックサーバーを起動し、ユーザーのブラウザで同意ページを開きます。
本番の同意URL: https://cloud.prevu3d.com/oauth
プレプロダクションの同意URL: https://cloud.preproduction.prevu3d-int.com/oauth
クエリパラメータ(すべて必須):
| パラメータ | 値 |
|---|---|
response_type | code |
code_challenge_method | S256 |
client_id | あなたのClient ID |
redirect_uri | ループバックURI。例:http://localhost:8765(ポートを含め、末尾のスラッシュは付けない) |
scopes | スコープごとに1パラメータ。例: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:コードをアクセストークンと交換する
Section titled “ステップ4:コードをアクセストークンと交換する”エンドポイント: POST https://cloud-api.prevu3d.com/oauth/token
ヘッダー: Content-Type: application/x-www-form-urlencoded
ボディ:
| フィールド | 値 |
|---|---|
grant_type | authorization_code |
client_id | あなたのClient ID |
code | リダイレクトで受け取ったコード |
redirect_uri | ステップ3と同じURI(ポートを含む) |
code_verifier | 元のPKCE verifier |
ネイティブアプリではクライアントシークレットを送信しないでください。
レスポンス例:
{ "hasError": false, "expires_in": 86400, "access_token": "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9...", "refresh_token": "..."}両方のトークンを保存します。API呼び出しにはaccess_tokenを使用します。期限切れになったら、保存したrefresh_tokenでgrant_type=refresh_tokenを使って新しいトークンをリクエストします(クライアントシークレットは不要です)。
ステップ5:APIベースURLを取得する
Section titled “ステップ5:APIベースURLを取得する”検出エンドポイントを呼び出して、組織の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"}apiUrlの値をすべてのAPI呼び出しのベースとして使用します。多くのエンドポイントで必要になるorganization.idもメモしておいてください。
ステップ6:最初のAPI呼び出しを行う
Section titled “ステップ6:最初のAPI呼び出しを行う”必要なものはすべて揃いました。アクセストークンとベースURLです。組織のディビジョンを取得する簡単なリクエストを送ってみましょう。
GET {apiUrl}/v1/nodes/{organization_id}/browseAuthorization: Bearer <your_access_token>実際の値を使った例:
GET https://api-ue1.prevu3d.com/realityconnect-api/v1/nodes/217ebd23-ec54-4af0-a6d6-4a441a6d1966/browse HTTP/1.1Authorization: Bearer eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9...ディビジョンのリストを含む成功レスポンスは、統合が正しく動作していることを示します。
Pythonで試す
Section titled “Pythonで試す”以下は、上記のすべてを実行する完全なスクリプトです。client_idをあなたのClient IDに置き換えて実行してください。一時的なループバックサーバーを起動し、ブラウザで同意ページを開き、承認後に組織のディビジョンを出力します。
import base64import hashlibimport jsonimport secretsimport webbrowserfrom http.server import BaseHTTPRequestHandler, HTTPServerfrom 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 tokenverifier = 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 IDapi_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 divisionsdivisions = requests.get( f"{api_url}/v1/nodes/{organization_id}/browse", headers={"Authorization": f"Bearer {access_token}"},).json()
print("Divisions:", json.dumps(divisions, indent=2))トラブルシューティング
Section titled “トラブルシューティング”| 表示される内容 | 試すこと |
|---|---|
| Allowクリック後のInvalid OAuth grant request | ネイティブアプリのClient IDを使用していることを確認してください(Authorization CodeとNative appが有効)。Client CredentialsのみのアプリにはリダイレクトURIがなく、このフローを完了できません。 |
| アプリ作成時のRedirect URI domain must be verified | カスタムリダイレクトURIを入力しないでください。代わりにNative appトグルをオンにしてください。現在登録できるのはhttp://localhost(ポートなし、シークレットなし)のみです。 |
| クエリパラメータに関する同意ページのエラー | response_type=code、code_challenge_method=S256、およびstateとscopesを含むすべての必須パラメータが揃っていることを確認してください。 |
| トークン交換時の400または403 | コードの期限切れ(60秒制限)、redirect_uriがステップ3と一致しない、またはPKCE verifierとchallengeが一致しない可能性があります。 |
| API呼び出し時の403 Forbidden | リクエストは3つのセキュリティレイヤーすべてを通過する必要があります。OAuthスコープ、サインインしたユーザーのコンテンツアクセス、対象ノードでのユーザーのロールを確認してください。 |
| APIレスポンスの組織が間違っている | ユーザーは同意画面で組織を選択します。再承認して正しい組織を選んでください。 |
接続、DNS、API URLの問題については、セットアップ — API URLとネットワークアクセスを参照してください。
次のステップ
Section titled “次のステップ”- 本番利用では、保存したリフレッシュトークンで
grant_type=refresh_tokenを使い、トークンが期限切れになる前に更新するロジックを追加してください。 - APIリファレンスですべての利用可能な操作を確認してください。