Aller au contenu

Flux pour application native

Utilisez le flux Native app pour les intégrations qui agissent au nom d’un utilisateur connecté depuis sa propre machine, comme une application de bureau, un outil CLI ou un plugin CAO. Il utilise Authorization Code avec PKCE, une redirection loopback localhost et aucun client secret. Ce guide vous accompagne de la création de l’application OAuth jusqu’à votre premier appel API.

Consultez d’abord le guide Premiers pas si vous n’avez pas encore couvert les prérequis, l’accès réseau et le modèle de sécurité.


  1. Créer une application OAuth : enregistrer une application native dans Prevu3D et obtenir un Client ID
  2. Configurer l’accès utilisateur : vous assurer que l’utilisateur qui se connecte peut atteindre les données nécessaires
  3. Autoriser avec PKCE : envoyer l’utilisateur vers la page de consentement dans le navigateur
  4. Échanger le code contre un jeton : échanger le code d’autorisation contre un jeton d’accès
  5. Trouver votre URL d’API : découvrir l’URL de base de votre organisation (elle varie selon la région)
  6. Effectuer votre premier appel : tester la connexion avec une requête simple
  1. Connectez-vous à la Prevu3D Platform (ou à votre environnement de préproduction).
  2. Allez dans SettingsOAuth.
  3. Cliquez pour créer une nouvelle application.
  4. Activez Authorization Code flow.
  5. Activez Native Application. L’URI de redirection est fixée à http://localhost.
  6. Laissez le client secret désactivé. Les applications natives n’en utilisent pas.
  7. Copiez et stockez votre Client ID en lieu sûr. Vous en aurez besoin pour chaque autorisation.

Le flux natif appelle l’API en tant qu’utilisateur connecté, et non en tant qu’utilisateur de service. Cela concerne les couches 2 et 3 du modèle de sécurité : quels nœuds l’utilisateur peut voir (accès au contenu) et ce qu’il peut en faire (accès par rôle / permission).

  1. Assurez-vous que l’utilisateur qui se connectera dispose d’un accès au contenu aux nœuds (organisations, divisions, sites, etc.) requis par votre cas d’usage.
  2. Assurez-vous que l’utilisateur dispose d’un rôle sur ces nœuds avec les permissions dont votre intégration a besoin (lecture, modification, gestion, etc.).

Sur l’écran de consentement, l’utilisateur sélectionne également l’organisation à autoriser. Les scopes approuvés à cet endroit configurent la couche 1.

Les applications natives ne peuvent pas conserver de secret, le flux utilise donc PKCE pour protéger le code d’autorisation. Avant d’ouvrir la page de consentement, générez trois valeurs :

ValeurMéthode
code_verifierChaîne aléatoire de 43 à 128 caractères
code_challengeBASE64URL(SHA256(code_verifier)) sans padding
stateChaîne aléatoire ; validez-la au retour de la redirection

Démarrez un serveur loopback local sur un port libre, puis ouvrez le navigateur de l’utilisateur sur la page de consentement.

URL de consentement production : https://cloud.prevu3d.com/oauth

URL de consentement préproduction : https://cloud.preproduction.prevu3d-int.com/oauth

Paramètres de requête (tous obligatoires) :

ParamètreValeur
response_typecode
code_challenge_methodS256
client_idVotre Client ID
redirect_uriVotre URI loopback, par ex. http://localhost:8765 (inclure le port, sans barre oblique finale)
scopesUn paramètre par scope, par ex. scopes=read:basic&scopes=read:hierarchy
code_challengeLe challenge PKCE généré ci-dessus
stateVotre valeur state aléatoire

Après que l’utilisateur s’est connecté et a cliqué sur Allow, le navigateur redirige vers votre URI loopback :

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

Si l’utilisateur refuse l’accès, vous recevez ?error=access_denied à la place. Les codes d’autorisation expirent après 60 secondes, échangez-les donc immédiatement.

Étape 4 : Échanger le code contre un jeton d’accès

Section intitulée « Étape 4 : Échanger le code contre un jeton d’accès »

Point de terminaison : POST https://cloud-api.prevu3d.com/oauth/token

En-têtes : Content-Type: application/x-www-form-urlencoded

Corps :

ChampValeur
grant_typeauthorization_code
client_idVotre Client ID
codeCode reçu lors de la redirection
redirect_uriLa même URI qu’à l’étape 3 (port inclus)
code_verifierLe verifier PKCE d’origine

N’envoyez pas de client secret pour les applications natives.

Exemple de réponse :

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

Enregistrez les deux jetons. Utilisez l’access_token pour les appels API. Lorsqu’il expire, demandez-en un nouveau avec grant_type=refresh_token et votre refresh_token stocké (aucun client secret requis).

Appelez le point de terminaison de découverte pour obtenir l’apiUrl de votre organisation. Ne codez pas une URL régionale en dur.

Point de terminaison : GET https://cloud-api.prevu3d.com/oauth/api-info

En-têtes : Authorization: Bearer <your_access_token>

Exemple de réponse :

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

Utilisez la valeur apiUrl comme base pour tous vos appels API. Notez également l’organization.id, dont vous aurez besoin pour de nombreux points de terminaison.

Vous avez maintenant tout ce qu’il faut : un jeton d’accès et votre URL de base. Effectuons une requête simple pour récupérer les divisions de votre organisation :

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

Exemple avec des valeurs réelles :

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

Une réponse réussie contenant une liste de divisions confirme que votre intégration fonctionne correctement.

Voici un script complet qui réalise tout ce qui précède. Remplacez client_id par votre Client ID, puis exécutez-le. Il démarre un serveur loopback temporaire, ouvre la page de consentement dans votre navigateur et affiche les divisions de votre organisation une fois que vous avez approuvé.

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))
Si vous voyez…Essayez ceci…
Invalid OAuth grant request après avoir cliqué sur AllowVérifiez que vous utilisez le Client ID d’une application native (Authorization Code et Native app activés). Une application Client Credentials uniquement n’a pas d’URI de redirection et ne peut pas terminer ce flux.
Redirect URI domain must be verified lors de la création de l’applicationN’entrez pas d’URI de redirection personnalisée. Activez plutôt le bouton Native app. Seul http://localhost (sans port, sans secret) peut être enregistré aujourd’hui.
Erreur sur la page de consentement concernant les paramètres de requêteAssurez-vous que response_type=code, code_challenge_method=S256 et tous les paramètres requis sont présents, y compris state et scopes.
400 ou 403 lors de l’échange de jetonLe code a peut-être expiré (limite de 60 secondes), le redirect_uri ne correspond peut-être pas à l’étape 3, ou le verifier et le challenge PKCE ne correspondent pas.
403 Forbidden lors de l’appel APIUne requête doit passer les trois couches de sécurité. Vérifiez les scopes OAuth, l’accès au contenu de l’utilisateur connecté et son rôle sur le nœud cible.
Mauvaise organisation dans les réponses APIL’utilisateur sélectionne l’organisation sur l’écran de consentement. Réautorisez et choisissez la bonne organisation.

Pour les problèmes de connexion, DNS et URL d’API, consultez Premiers pas — URL de l’API et accès réseau.

  • Pour un usage en production, ajoutez une logique pour rafraîchir votre jeton avant expiration avec grant_type=refresh_token et votre refresh token stocké.
  • Explorez la référence API pour voir toutes les opérations disponibles.