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 when your integration uses a single set of credentials of its own. Typical: a payroll export job, a nightly sync, a dashboard with one service account. Credentials live in your own secrets store.
- OAuth 2.1 + PKCE when you sign end users into your app and fetch data on their behalf. Typical: a SaaS product, a mobile app, a partner portal. You never hold customer passwords.
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.
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:
curl https://api.geodynamics.dev/api/v1/resourcegroups \ -H "Authorization: Basic YWxpY2V8YWNtZTpodW50ZXIy"
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}` } },
); 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
- Create a dedicated service user inside IntelliTracer. Give it only the privileges your integration needs. Don't reuse a personal account.
- Store credentials in a secrets manager (Vault, AWS Secrets Manager, CF Secrets, Doppler). Never in source control.
- Rotate passwords on your standard policy. The old header stops working as soon as the old password is invalidated in IntelliTracer.
- All requests must use HTTPS with TLS 1.2 or higher.
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:
- Your application name.
- The redirect URI(s) where users land after login.
Flow
- Generate a
code_verifier(32 random bytes, base64url) and derive acode_challengefrom it (SHA-256, base64url). - Redirect the user to
/oauth/authorizewithresponse_type=code, yourclient_id,redirect_uri,state,code_challenge, andcode_challenge_method=S256. - The user signs in. The authority redirects back to your
redirect_uriwithcodeandstate. - Exchange the code at
/oauth/tokenfor anaccess_token, sending your originalcode_verifieralong. - Use
Authorization: Bearer <access_token>against the authority's/userinfoand (optionally) againstportal.geodynamics.eu/api/public/v1/application-info.
Authorize redirect:
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:
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:
curl https://accounts.intellitracer.be/userinfo \ -H "Authorization: Bearer eyJ..."
Requirements
- PKCE with
S256is required. Plain code challenges are rejected. - Always request the scopes
openid email profile. - Keep
stateandcode_verifierserver-side between the redirect and the callback. Verify the returnedstatematches. - The email field in
/userinfois namede-mailwith a hyphen, notemail. Don't forget that parsing step. - Keep your
client_secretserver-side only. Never in browser bundles or mobile apps.
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.