Autenticación

Cliqer se autentica mediante una cookie de sesión httpOnly (session_token), emitida por POST /api/auth/login. Los navegadores la envían automáticamente en cada solicitud; las herramientas de servidor a servidor la guardan en un almacén de cookies. Los equipos con Studio 8K y Enterprise también pueden generar claves de API para el acceso de servidor a servidor, que se envían como Authorization: Bearer cliqer_<key>; consulta Autenticación con clave de API más abajo.
Modelo de sesión
| Credencial | Dónde se almacena | Cómo se obtiene |
|---|---|---|
Cookie de sesión (session_token) | Cookie httpOnly y Secure en tu host de Cliqer | La establece POST /api/auth/login y el servidor la rota automáticamente |
La cookie es HttpOnly, por lo que no se puede leer desde JavaScript. Su duración sigue el TTL de la sesión del servidor y se renueva de forma transparente cada vez que llamas a GET /api/auth/me. No hay un endpoint de renovación aparte al que llamar: para mantener activo un cliente de larga duración, basta con llamar a GET /api/auth/me periódicamente.
Ciclo de vida de la sesión
Uso de la sesión
Los navegadores adjuntan la cookie automáticamente. En los clientes de servidor a servidor, conserva la cookie que establece la respuesta del inicio de sesión y envíala en las solicitudes posteriores:
# 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?;
Autenticación con clave de API
Los equipos con Studio 8K y Enterprise pueden generar claves de API de larga duración para el acceso de servidor a servidor, como alternativa a guardar las credenciales de inicio de sesión de un usuario. Créalas y revócalas en el panel, en Equipo → Claves de API (/dash/team/api-keys); para generarlas hay que ser el propietario del equipo (o tener un rol personalizado con el permiso de claves de API). La gestión de claves solo está disponible en los planes Studio 8K y Enterprise.


La clave se muestra una sola vez al crearla, con el formato cliqer_<prefix>_<secret>. Guárdala de forma segura: no se puede recuperar, así que si la pierdes, revócala y crea otra. Envíala como token Bearer en cualquier endpoint REST que acepte una sesión:
curl -X GET "$BASE_URL/api/licenses/status?license_key=YOUR_LICENSE_KEY" \
-H "Authorization: Bearer cliqer_ab12cd34_your_secret_here"
Las claves de API solo funcionan en los endpoints REST. El WebSocket, incluido el control de diapositivas, requiere un token de sesión.
La clave se autentica como el usuario que la creó, limitada al equipo y al plan de ese usuario. Una clave revocada o desconocida devuelve 401, exactamente igual que una sesión ausente. Los endpoints exclusivos de administradores siempre rechazan las claves de API con 403. Las claves están sujetas a los mismos límites de frecuencia que las solicitudes con sesión.
Iniciar sesión
Intercambia las credenciales del usuario por una sesión. Si la solicitud tiene éxito, la respuesta establece la cookie session_token.
Endpoint: POST /api/auth/login
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
email | string | Sí | Correo electrónico del usuario (también se acepta un nombre de usuario de WordPress) |
password | string | Sí | Contraseña del usuario |
otp | string | No | Código TOTP, si la cuenta usa una app de autenticación |
email_otp | string | No | PIN enviado por correo, si la cuenta usa 2FA por correo electrónico |
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?;
Respuesta (éxito): establece la cookie session_token y devuelve los metadatos de la sesión.
{
"data": {
"expires": 604800000
}
}
Respuesta (se requiere 2FA): una señal de control de flujo con 200, no un error. Vuelve a enviar POST /api/auth/login con las mismas credenciales más otp (app de autenticación) o email_otp (PIN enviado por correo).
{
"requires_otp": true,
"method": "totp",
"email_sent": false
}
method es totp, email o both. Con email, el servidor envía un PIN por correo y email_sent indica si ese correo se ha enviado. Una cuenta con both inicia sesión con el código de su app de autenticación (otp); el PIN enviado por correo no es un factor de inicio de sesión para ella.
401, igual que un código TOTP incorrecto o caducado. Un PIN enviado por correo incorrecto o caducado también devuelve 401 con el mismo mensaje que una contraseña incorrecta. Un miembro de una organización que exige inicio de sesión único recibe 403 y debe iniciar sesión con SSO (consulta las preguntas frecuentes sobre SSO).Rotación de la sesión
No hay un endpoint de renovación aparte. El servidor rota la cookie session_token por ti: cuando a una sesión le queda poco para caducar, GET /api/auth/me responde con un Set-Cookie nuevo junto con el perfil. Un cliente que mantiene un almacén de cookies y llama a GET /api/auth/me periódicamente conserva la sesión iniciada indefinidamente, sin ningún trabajo adicional.
Usuario actual
Devuelve el perfil del usuario autenticado, o { "data": null } cuando no hay una sesión válida (compatible con el acceso anónimo, nunca devuelve 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?;
Respuesta:
{
"data": {
"id": "uuid",
"email": "user@example.com",
"first_name": "Jane",
"last_name": "Doe",
"role": "uuid",
"tfa_method": "none",
"is_admin": false
}
}
Cerrar sesión
Finaliza la sesión de la cookie actual y elimina la cookie del navegador.
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?;
Respuesta:
{
"success": true
}
Buenas prácticas de seguridad
- Usa solo HTTPS: la cookie de sesión es
Secure; todas las solicitudes deben usar TLS 1.2+. - Nunca leas la cookie desde JavaScript: es
HttpOnlypor diseño; mantenla así. - Renueva de forma proactiva: en las pestañas que permanecen abiertas mucho tiempo, llama a
GET /api/auth/meantes de que caduque la sesión; el servidor rota la cookie por ti. - Cierra sesión para revocar:
POST /api/auth/logoutfinaliza la sesión en el servidor de inmediato. - Guarda credenciales, no sesiones: las integraciones de servidor a servidor deben guardar las credenciales de inicio de sesión en un gestor de secretos y volver a establecer una sesión, nunca conservar la cookie en el código fuente.