OAuth Flow
Routix gebruikt OAuth 2.1 om externe applicaties te authenticeren. Er zijn twee flows beschikbaar, afhankelijk van je integratietype.
Flow 1: Authorization Code + PKCE
Wordt gebruikt wanneer je applicatie namens een Routix-gebruiker moet handelen. De gebruiker moet expliciet toestemming geven en de organisatie selecteren.
Verloop
Externe App Routix Auth Gebruiker
│ │ │
├── redirect naar /authorize ─►│ │
│ (client_id, scope, │ │
│ code_challenge, state) │ │
│ ├── consent scherm ──────►│
│ │ (app naam, scopes, │
│ │ org selector) │
│ │◄── goedkeuren + org ────┤
│ │ │
│◄── redirect met ?code= ─────┤ │
│ │ │
├── POST /oauth/token ────────►│ │
│ (code, code_verifier) │ │
│◄── { access_token } ────────┤ │
│ │ │
├── GET /api/v1/{vestiging}/..►│ (API Proxy) │
│◄── { data: [...] } ─────────┤ │Stap 1: Genereer PKCE Challenge
Genereer een code verifier en challenge voordat je de flow start:
// Genereer code_verifier (43–128 tekens)
const array = new Uint8Array(32);
crypto.getRandomValues(array);
const codeVerifier = btoa(String.fromCharCode(...array))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
// Genereer code_challenge (SHA-256, base64url)
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier));
const codeChallenge = btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');Stap 2: Redirect naar Authorize
Redirect de browser van de gebruiker naar:
https://api.routix.nl/auth/v1/oauth/authorize
?client_id=YOUR_CLIENT_ID
&redirect_uri=https://jouwapp.nl/callback
&response_type=code
&code_challenge=BASE64URL_SHA256_HASH
&code_challenge_method=S256
&scope=routix:accounts:read routix:orders:read
&state=RANDOM_STATE_VALUE| Parameter | Verplicht | Beschrijving |
|---|---|---|
client_id | Ja | De client ID van je OAuth-app (UUID) |
redirect_uri | Ja | Moet exact overeenkomen met een geregistreerde redirect URI |
response_type | Ja | Altijd code |
code_challenge | Ja | Base64url-encoded SHA-256 hash van je code_verifier |
code_challenge_method | Ja | Altijd S256 |
scope | Ja | Spatie-gescheiden lijst van gevraagde scopes |
state | Aanbevolen | Willekeurige string om CSRF-aanvallen te voorkomen |
Stap 3: Gebruiker Consent
Routix toont een consent-scherm waar de gebruiker:
- De applicatienaam en alle gevraagde scopes ziet
- Selecteert voor welke organisatie toegang wordt verleend (multi-tenant)
- De aanvraag goedkeurt of afwijst
Bij goedkeuring:
- Er wordt een record aangemaakt in
oauth_client_authorizationsdie de app koppelt aan de geselecteerde organisatie - De gebruiker wordt teruggestuurd naar je
redirect_urimet?code=xxx&state=xxx
Stap 4: Wissel Code in voor Tokens
curl -X POST https://api.routix.nl/auth/v1/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "authorization_code",
"code": "AUTHORIZATION_CODE",
"redirect_uri": "https://jouwapp.nl/callback",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "routix_a1b2c3d4...",
"code_verifier": "YOUR_ORIGINAL_CODE_VERIFIER"
}'Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer",
"expires_in": 3600,
"refresh_token": "v1.MjQ0ZWY5..."
}Stap 5: Vernieuw Tokens
Access tokens verlopen na 1 uur. Gebruik de refresh token om een nieuwe op te halen:
curl -X POST https://api.routix.nl/auth/v1/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "refresh_token",
"refresh_token": "v1.MjQ0ZWY5..."
}'Refresh tokens zijn eenmalig met rotatie. Elke refresh geeft een nieuwe refresh token terug.
Token Claims (Authorization Code)
De JWT wordt verrijkt door een Custom Access Token Hook die toevoegt:
{
"sub": "user-uuid",
"aud": "authenticated",
"role": "authenticated",
"organization_id": "org-uuid",
"oauth_client_id": "client-uuid",
"oauth_scopes": ["routix:accounts:read", "routix:orders:read"]
}Deze claims worden afgedwongen door Row Level Security (RLS) in de database.
Flow 2: Client Credentials
Wordt gebruikt voor server-to-server (machine-to-machine) communicatie. Geen gebruikersinteractie nodig.
Verloop
Jouw Server Routix
│ │
├── POST /oauth-token ────────►│
│ (client_id, client_secret) │
│◄── { access_token } ────────┤
│ │
├── GET /api/v1/{vestiging}/..►│
│◄── { data: [...] } ─────────┤Token Aanvragen
curl -X POST https://api.routix.nl/functions/v1/oauth-token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "client_credentials",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "routix_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
}'Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer",
"expires_in": 3600,
"scope": "routix:accounts:read routix:orders:read",
"organization_id": "org-uuid",
"branch_ids": ["branch-uuid-1", "branch-uuid-2"]
}Hoe Het Werkt
- Je server stuurt
client_id+client_secretnaar het token endpoint - Routix verifieert het secret door SHA-256 hashes te vergelijken
- Een JWT wordt gebouwd met de
organization_id,allowed_scopesenallowed_branch_idsvan de app - De JWT wordt gesigned met HS256 en teruggestuurd
- Gebruik het token om de API proxy aan te roepen
Token Claims (Client Credentials)
{
"iss": "supabase",
"aud": "authenticated",
"role": "authenticated",
"sub": "app-uuid",
"organization_id": "org-uuid",
"oauth_client_id": "client-uuid",
"oauth_scopes": ["routix:accounts:read"],
"oauth_branch_ids": ["branch-uuid-1"],
"oauth_app_name": "Mijn ERP Sync"
}Vergelijking
| Aspect | Authorization Code + PKCE | Client Credentials |
|---|---|---|
| Gebruikersinteractie | Ja (consent scherm) | Nee |
| Token endpoint | /auth/v1/oauth/token | /functions/v1/oauth-token |
| Org binding | Geselecteerd door gebruiker bij consent | Uit app-registratie |
| Refresh tokens | Ja (30 dagen, roterend) | Nee — vraag nieuw token aan |
| Use case | Gebruikersgerichte apps, marketplace | Backend sync, BI, automatisering |
Beveiligingsrichtlijnen
| Richtlijn | Waarom |
|---|---|
| Gebruik altijd PKCE met S256 | Verplicht voor Authorization Code flow. Plain challenges worden niet ondersteund. |
| Bewaar secrets veilig | Het client secret wordt maar één keer getoond. Gebruik een secrets manager — zet het nooit in broncode. |
Valideer de state parameter | Voorkomt CSRF-aanvallen tijdens de redirect-flow. |
| Vraag minimale scopes aan | Vraag alleen de scopes aan die je applicatie daadwerkelijk nodig heeft. |
| Roteer secrets regelmatig | Gebruik het secret rotation endpoint om periodiek je client secret te vernieuwen. |
| Handel token-expiry af | Tokens verlopen na 1 uur. Refresh (Auth Code flow) of vraag een nieuw token aan (Client Credentials). |
| Stel secrets nooit bloot in frontend-code | De Client Credentials flow mag alleen vanuit een backend-server worden gebruikt. |