Authenticatie

Cliqer authenticeert met een httpOnly sessiecookie (session_token), aangemaakt door POST /api/auth/login. Browsers sturen die automatisch mee bij elk verzoek; server-to-servertools bewaren hem in een cookie jar. Teams met Studio 8K en Enterprise kunnen daarnaast API-sleutels aanmaken voor server-to-servertoegang, verstuurd als Authorization: Bearer cliqer_<key>, zie Authenticatie met API-sleutels hieronder.
Sessiemodel
| Referentie | Waar die staat | Hoe je die krijgt |
|---|---|---|
Sessiecookie (session_token) | httpOnly, Secure cookie op je Cliqer-host | Ingesteld door POST /api/auth/login, automatisch geroteerd door de server |
De cookie is HttpOnly en dus niet leesbaar vanuit JavaScript. De levensduur volgt de TTL van de serversessie en wordt ongemerkt verlengd telkens wanneer je GET /api/auth/me aanroept. Er is geen apart refresh-endpoint om aan te roepen: een langlevende client netjes ingelogd houden betekent periodiek GET /api/auth/me aanroepen.
Sessielevenscyclus
De sessie gebruiken
Browsers voegen de cookie automatisch toe. Bewaar bij server-to-serverclients de cookie die het login-antwoord instelt en stuur die mee bij volgende verzoeken:
# Save the session cookie on login, then reuse it
curl -X POST "$BASE_URL/api/auth/login" \
-H "Content-Type: application/json" \
-c cookies.txt \
-d '{"email":"user@example.com","password":"your_password"}'
curl -X GET "$BASE_URL/api/auth/me" -b cookies.txt
// Same-origin $fetch sends the httpOnly cookie automatically.
// Cross-origin fetch must opt in with credentials: 'include'.
const me = await $fetch('/api/auth/me')
import requests
session = requests.Session() # persists the session cookie
session.post('$BASE_URL/api/auth/login',
json={'email': 'user@example.com', 'password': 'your_password'})
me = session.get('$BASE_URL/api/auth/me').json()
jar, _ := cookiejar.New(nil)
client := &http.Client{Jar: jar}
// Login stores the session cookie in the jar; later requests reuse it.
client.Post("$BASE_URL/api/auth/login", "application/json", body)
resp, _ := client.Get("$BASE_URL/api/auth/me")
$ch = curl_init('$BASE_URL/api/auth/login');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'email' => 'user@example.com', 'password' => 'your_password'
]));
curl_exec($ch);
// reqwest with cookie_store persists the session cookie across calls
let client = reqwest::Client::builder()
.cookie_store(true)
.build()?;
client.post("$BASE_URL/api/auth/login")
.json(&serde_json::json!({"email": "user@example.com", "password": "your_password"}))
.send().await?;
let me = client.get("$BASE_URL/api/auth/me").send().await?;
Authenticatie met API-sleutels
Teams met Studio 8K en Enterprise kunnen langlevende API-sleutels aanmaken voor server-to-servertoegang, als alternatief voor het bewaren van de inloggegevens van een gebruiker. Je maakt ze aan en trekt ze in via het dashboard onder Team → API-sleutels (/dash/team/api-keys); voor het aanmaken is de teameigenaar nodig (of een aangepaste rol met de machtiging voor API-sleutels). Sleutelbeheer is alleen beschikbaar in de abonnementen Studio 8K en Enterprise.


Een sleutel wordt eenmalig getoond bij het aanmaken, in de vorm cliqer_<prefix>_<secret>. Bewaar hem veilig: hij kan niet worden hersteld, dus trek hem in en maak een nieuwe aan als je hem kwijtraakt. Stuur hem als Bearer-token mee naar elk REST-endpoint dat een sessie accepteert:
curl -X GET "$BASE_URL/api/licenses/status?license_key=YOUR_LICENSE_KEY" \
-H "Authorization: Bearer cliqer_ab12cd34_your_secret_here"
API-sleutels werken alleen op de REST-endpoints. De WebSocket, inclusief diabesturing, vereist een sessietoken.
De sleutel authenticeert als de gebruiker die hem heeft aangemaakt, beperkt tot het team en abonnement van die gebruiker. Een ingetrokken of onbekende sleutel geeft 401 terug, precies zoals een ontbrekende sessie. Endpoints die alleen voor beheerders zijn, weigeren API-sleutels altijd met 403. Voor sleutels gelden dezelfde rate limits als voor sessieverzoeken.
Inloggen
Wissel gebruikersgegevens in voor een sessie. Bij succes stelt het antwoord de cookie session_token in.
Endpoint: POST /api/auth/login
| Parameter | Type | Verplicht | Beschrijving |
|---|---|---|---|
email | string | Ja | E-mailadres van de gebruiker (een WordPress-gebruikersnaam wordt ook geaccepteerd) |
password | string | Ja | Wachtwoord van de gebruiker |
otp | string | Nee | TOTP-code, als het account een authenticator-app gebruikt |
email_otp | string | Nee | Gemailde pincode, als het account 2FA via e-mail gebruikt |
curl -X POST "$BASE_URL/api/auth/login" \
-H "Content-Type: application/json" \
-c cookies.txt \
-d '{"email":"user@example.com","password":"your_password"}'
interface LoginResponse {
data: {
expires: number | null // session lifetime in ms
}
}
const res: LoginResponse = await $fetch('/api/auth/login', {
method: 'POST',
body: { email: 'user@example.com', password: 'your_password' }
})
// The session_token cookie is set by the response; no token is in the body.
import requests
session = requests.Session()
data = {'email': 'user@example.com', 'password': 'your_password'}
res = session.post('$BASE_URL/api/auth/login', json=data)
# The session cookie now lives in `session`.
jar, _ := cookiejar.New(nil)
client := &http.Client{Jar: jar}
payload := map[string]string{"email": "user@example.com", "password": "your_password"}
body, _ := json.Marshal(payload)
client.Post("$BASE_URL/api/auth/login", "application/json", bytes.NewBuffer(body))
$ch = curl_init('$BASE_URL/api/auth/login');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'email' => 'user@example.com',
'password' => 'your_password'
]));
$response = curl_exec($ch);
use serde::Serialize;
#[derive(Serialize)]
struct LoginRequest { email: String, password: String }
let client = reqwest::Client::builder().cookie_store(true).build()?;
let response = client
.post("$BASE_URL/api/auth/login")
.json(&LoginRequest { email: "user@example.com".into(), password: "your_password".into() })
.send()
.await?;
Antwoord (succes): stelt de cookie session_token in en geeft de sessiemetadata terug.
{
"data": {
"expires": 604800000
}
}
Antwoord (2FA vereist): een 200-signaal voor de controlflow, geen fout. Verstuur POST /api/auth/login opnieuw met dezelfde inloggegevens plus otp (authenticator) of email_otp (gemailde pincode).
{
"requires_otp": true,
"method": "totp",
"email_sent": false
}
method is totp, email of both. Bij email mailt de server een pincode en geeft email_sent aan of die e-mail is verstuurd. Een account met both logt in met de authenticatorcode (otp); de gemailde pincode is voor zo'n account geen inlogfactor.
401 terug, net als een onjuiste of verlopen TOTP-code. Een onjuiste of verlopen gemailde pincode geeft ook 401 terug, met dezelfde melding als een onjuist wachtwoord. Een lid van een organisatie die single sign-on vereist, krijgt 403 en moet inloggen met SSO (zie de SSO-FAQ).Sessierotatie
Er is geen apart refresh-endpoint. De server roteert de cookie session_token voor je: als een sessie dicht genoeg bij het verlopen is, antwoordt GET /api/auth/me met een nieuwe Set-Cookie naast het profiel. Een client die een cookie jar bijhoudt en periodiek GET /api/auth/me aanroept, blijft zonder extra werk onbeperkt ingelogd.
Huidige gebruiker
Geeft het profiel van de geauthenticeerde gebruiker terug, of { "data": null } als er geen geldige sessie is (geschikt voor anonieme gebruikers, nooit een 401).
Endpoint: GET /api/auth/me
curl -X GET "$BASE_URL/api/auth/me" -b cookies.txt
const { data } = await $fetch('/api/auth/me')
if (data) console.log(data.email, data.is_admin)
me = session.get('$BASE_URL/api/auth/me').json()
resp, _ := client.Get("$BASE_URL/api/auth/me")
$ch = curl_init('$BASE_URL/api/auth/me');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
$response = curl_exec($ch);
let me = client.get("$BASE_URL/api/auth/me").send().await?;
Antwoord:
{
"data": {
"id": "uuid",
"email": "user@example.com",
"first_name": "Jane",
"last_name": "Doe",
"role": "uuid",
"tfa_method": "none",
"is_admin": false
}
}
Uitloggen
Beëindigt de sessie voor de huidige cookie en verwijdert de cookie uit de browser.
Endpoint: POST /api/auth/logout
curl -X POST "$BASE_URL/api/auth/logout" -b cookies.txt -c cookies.txt
await $fetch('/api/auth/logout', { method: 'POST' })
session.post('$BASE_URL/api/auth/logout')
req, _ := http.NewRequest("POST", "$BASE_URL/api/auth/logout", nil)
client.Do(req)
$ch = curl_init('$BASE_URL/api/auth/logout');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_exec($ch);
client.post("$BASE_URL/api/auth/logout").send().await?;
Antwoord:
{
"success": true
}
Best practices voor beveiliging
- Gebruik alleen HTTPS: de sessiecookie is
Secure; alle verzoeken moeten TLS 1.2+ gebruiken. - Lees de cookie nooit uit met JavaScript: hij is bewust
HttpOnly; houd dat zo. - Vernieuw proactief: roep op langlevende tabbladen
GET /api/auth/meaan voordat de sessie verloopt; de server roteert de cookie voor je. - Log uit om in te trekken:
POST /api/auth/logoutbeëindigt de sessie direct aan de serverkant. - Bewaar inloggegevens, geen sessies: server-to-serverintegraties horen de inloggegevens in een secretmanager te bewaren en opnieuw een sessie op te zetten, en de cookie nooit in de broncode op te slaan.