developer portal
Get API key →

Authentication

Basic Auth for server-to-server. OAuth 2.1 + PKCE for user-driven flows.

The API supports two authentication modes. Pick based on who owns the credentials: your system (Basic Auth) or your end user (OAuth).

Which mode to pick

Basic Auth

Standard HTTP Basic Authentication. The Authorization header carries Basic <credentials>, where <credentials> is the base64-encoding of id and password, separated by a colon.

The id itself is the pair user|company (with a pipe), where user is the IntelliTracer login and company is the tenant's company code.

plain credentials.txt
user:     alice
company:  acme
password: hunter2

unencoded: alice|acme:hunter2
base64:    YWxpY2V8YWNtZTpodW50ZXIy

header:    Authorization: Basic YWxpY2V8YWNtZTpodW50ZXIy

Prefer not to encode it yourself? The credential formatter does it client-side. Nothing leaves the browser.

A complete request:

curlnodepython basic-auth.sh
curl https://api.geodynamics.dev/api/v1/resourcegroups \
  -H "Authorization: Basic YWxpY2V8YWNtZTpodW50ZXIy"
node basic-auth.ts
const username = `${process.env.GD_USER}|${process.env.GD_COMPANY}`;
const password = process.env.GD_PASSWORD!;
const token = Buffer.from(`${username}:${password}`).toString("base64");

const res = await fetch(
  "https://api.geodynamics.dev/api/v1/resourcegroups",
  { headers: { Authorization: `Basic ${token}` } },
);
python basic-auth.py
import os, requests
from requests.auth import HTTPBasicAuth

auth = HTTPBasicAuth(
    f"{os.environ['GD_USER']}|{os.environ['GD_COMPANY']}",
    os.environ['GD_PASSWORD'],
)

res = requests.get(
    "https://api.geodynamics.dev/api/v1/resourcegroups",
    auth=auth,
    timeout=10,
)

In practice

OAuth 2.1 + PKCE

For user-driven flows you authenticate against the GeoDynamics Accounts authority at accounts.intellitracer.be. You register a client once (we give you a client_id and client_secret) and then exchange an authorization code for an access token per user. PKCE with S256 is required.

Setup

Request a client through support. Send us:

Flow

  1. Generate a code_verifier (32 random bytes, base64url) and derive a code_challenge from it (SHA-256, base64url).
  2. Redirect the user to /oauth/authorize with response_type=code, your client_id, redirect_uri, state, code_challenge, and code_challenge_method=S256.
  3. The user signs in. The authority redirects back to your redirect_uri with code and state.
  4. Exchange the code at /oauth/token for an access_token, sending your original code_verifier along.
  5. Use Authorization: Bearer <access_token> against the authority's /userinfo and (optionally) against portal.geodynamics.eu/api/public/v1/application-info.

Authorize redirect:

plain authorize.txt
GET https://accounts.intellitracer.be/oauth/authorize
  ?client_id=YOUR_CLIENT_ID
  &redirect_uri=https://yourapp.com/callback
  &response_type=code
  &scope=openid email profile
  &state=RANDOM_STATE_TOKEN
  &code_challenge=BASE64URL_SHA256_OF_VERIFIER
  &code_challenge_method=S256

Token exchange in your /callback handler:

node token-exchange.ts
const res = await fetch(
  "https://accounts.intellitracer.be/oauth/token",
  {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      grant_type: "authorization_code",
      client_id: process.env.GD_CLIENT_ID!,
      client_secret: process.env.GD_CLIENT_SECRET!,
      code: callbackCode,
      redirect_uri: "https://yourapp.com/callback",
      code_verifier: storedCodeVerifier,
    }),
  },
);

const { access_token } = await res.json();

Fetch user info:

curlnode userinfo.sh
curl https://accounts.intellitracer.be/userinfo \
  -H "Authorization: Bearer eyJ..."

Requirements

Failure patterns

A 401 on the token exchange usually means the redirect_uri at /oauth/authorize doesn't match the one at /oauth/token. They have to be identical, byte for byte.

invalid_grant means your code_verifier doesn't match the code_challenge you sent earlier. Store the verifier server-side tied to a session, not in a cookie the browser can lose.