Autenticazione

Cliqer esegue l'autenticazione tramite un cookie di sessione httpOnly (session_token), generato da POST /api/auth/login. I browser lo inviano automaticamente a ogni richiesta; gli strumenti server-to-server lo conservano in un cookie jar. I team Studio 8K ed Enterprise possono inoltre generare chiavi API per l'accesso server-to-server, inviate come Authorization: Bearer cliqer_<key>: vedi Autenticazione con chiave API più avanti.
Modello di sessione
| Credenziale | Dove si trova | Come si ottiene |
|---|---|---|
Cookie di sessione (session_token) | Cookie httpOnly e Secure sul tuo host Cliqer | Impostato da POST /api/auth/login, ruotato automaticamente dal server |
Il cookie è HttpOnly, quindi non è leggibile da JavaScript. La sua durata segue il TTL della sessione sul server e viene rinnovata in modo trasparente ogni volta che chiami GET /api/auth/me. Non esiste un endpoint di refresh separato da chiamare: per mantenere attivo correttamente un client di lunga durata basta chiamare periodicamente GET /api/auth/me.
Ciclo di vita della sessione
Utilizzo della sessione
I browser allegano il cookie automaticamente. Per i client server-to-server, conserva il cookie impostato dalla risposta di login e invialo nelle richieste successive:
# 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?;
Autenticazione con chiave API
I team Studio 8K ed Enterprise possono generare chiavi API di lunga durata per l'accesso server-to-server, in alternativa alla conservazione delle credenziali di accesso di un utente. Puoi crearle e revocarle nella dashboard in Team → Chiavi API (/dash/team/api-keys); per generarle serve il proprietario del team (o un ruolo personalizzato a cui è stato concesso il permesso per le chiavi API). La gestione delle chiavi è disponibile solo con i piani Studio 8K ed Enterprise.


Una chiave viene mostrata una sola volta al momento della creazione, nel formato cliqer_<prefix>_<secret>. Conservala in modo sicuro: non può essere recuperata, quindi se la perdi revocala e creane una nuova. Inviala come Bearer token a qualsiasi endpoint REST che accetta una sessione:
curl -X GET "$BASE_URL/api/licenses/status?license_key=YOUR_LICENSE_KEY" \
-H "Authorization: Bearer cliqer_ab12cd34_your_secret_here"
Le chiavi API funzionano solo sugli endpoint REST. Il WebSocket, incluso il controllo delle slide, richiede un token di sessione.
La chiave si autentica come l'utente che l'ha creata, con ambito limitato al team e al piano di quell'utente. Una chiave revocata o sconosciuta restituisce 401, esattamente come una sessione mancante. Gli endpoint riservati agli amministratori rifiutano sempre le chiavi API con 403. Le chiavi sono soggette agli stessi limiti di frequenza delle richieste con sessione.
Accesso
Scambia le credenziali dell'utente con una sessione. In caso di successo, la risposta imposta il cookie session_token.
Endpoint: POST /api/auth/login
| Parametro | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
email | string | Sì | Email dell'utente (è accettato anche un nome utente WordPress) |
password | string | Sì | Password dell'utente |
otp | string | No | Codice TOTP, se l'account usa un'app di autenticazione |
email_otp | string | No | PIN inviato via email, se l'account usa la 2FA via email |
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?;
Risposta (successo): imposta il cookie session_token e restituisce i metadati della sessione.
{
"data": {
"expires": 604800000
}
}
Risposta (2FA richiesta): un segnale di controllo del flusso con 200, non un errore. Invia di nuovo POST /api/auth/login con le stesse credenziali più otp (app di autenticazione) o email_otp (PIN inviato via email).
{
"requires_otp": true,
"method": "totp",
"email_sent": false
}
method è uno tra totp, email o both. Con email il server invia un PIN via email e email_sent indica se l'email è stata effettivamente inviata. Un account both accede con il codice dell'app di autenticazione (otp); per questo account il PIN inviato via email non è un fattore di accesso.
401, così come un codice TOTP errato o scaduto. Anche un PIN inviato via email errato o scaduto restituisce 401, con lo stesso messaggio di una password errata. Un membro di un'organizzazione che richiede il single sign-on riceve 403 e deve accedere con SSO (vedi le FAQ su SSO).Rotazione della sessione
Non esiste un endpoint di refresh separato. Il server ruota il cookie session_token al posto tuo: quando una sessione è abbastanza vicina alla scadenza, GET /api/auth/me risponde con un nuovo Set-Cookie insieme al profilo. Un client che mantiene un cookie jar e chiama periodicamente GET /api/auth/me resta connesso a tempo indeterminato senza alcun lavoro aggiuntivo.
Utente corrente
Restituisce il profilo dell'utente autenticato, oppure { "data": null } quando non c'è una sessione valida (compatibile con l'accesso anonimo, mai un 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?;
Risposta:
{
"data": {
"id": "uuid",
"email": "user@example.com",
"first_name": "Jane",
"last_name": "Doe",
"role": "uuid",
"tfa_method": "none",
"is_admin": false
}
}
Disconnessione
Termina la sessione associata al cookie corrente e lo rimuove dal 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?;
Risposta:
{
"success": true
}
Best practice di sicurezza
- Usa solo HTTPS: il cookie di sessione è
Secure; tutte le richieste devono usare TLS 1.2+. - Non leggere mai il cookie da JavaScript: è
HttpOnlyper scelta progettuale; lascialo così. - Rinnova in modo proattivo: nelle schede aperte a lungo, chiama
GET /api/auth/meprima che la sessione scada; il server ruota il cookie al posto tuo. - Disconnettiti per revocare:
POST /api/auth/logouttermina immediatamente la sessione lato server. - Conserva le credenziali, non le sessioni: le integrazioni server-to-server devono custodire le credenziali di accesso in un secret manager e ristabilire una sessione, senza mai salvare il cookie nel codice sorgente.