Saltar al contenido principal

Autenticación

Clione tiene dos modos de autenticación, cada uno para distintos endpoints.

1. JWT (sesiones del dashboard)​

Se emite cuando un usuario inicia sesión. Viaja en cookies httpOnly y también se acepta como cabecera Authorization: Bearer <token> para clientes cross-origin.

  • POST /api/v1/auth/login — email + contraseña → tokens de acceso y de refresh
  • POST /api/v1/auth/refresh — cambia el refresh token por un par nuevo; el refresh token anterior queda en la blacklist
  • POST /api/v1/auth/logout — mete en la blacklist los tokens de acceso y de refresh actuales
  • GET /api/v1/auth/me — usuario actual
  • POST /api/v1/auth/google — inicio de sesión con un ID token de Google

Protección del login:

  • Bloqueo de cuenta por IP del cliente + cuenta: tras 5 intentos fallidos, esa pareja queda bloqueada 1 minuto, y cada bloqueo posterior de la misma pareja dobla la espera, hasta un máximo de 60 minutos.
  • Rate limit de 10 intentos de login fallidos por IP cada 15 minutos.

Las sesiones JWT son para el dashboard. No las uses para integraciones de storefront: usa una API key.

Flujo 2FA​

Cuando un usuario con 2FA por TOTP o por email inicia sesión desde un dispositivo que no es de confianza, la respuesta del login es:

{ "requires2FA": true, "tempToken": "...", "methods": ["totp", "email", "recovery"] }

methods indica con qué puede responder el usuario: totp solo si tiene configurada una app de autenticación, email siempre, y recovery mientras le queden códigos de recuperación sin usar.

Después, el cliente llama a POST /api/v1/auth/2fa/validate:

{
"tempToken": "<from login>",
"token": "<6-digit code or recovery code>",
"method": "totp",
"deviceFingerprint": "<optional — if present, the device is trusted for 30 days>"
}

Un código válido devuelve la sesión completa (accessToken, refreshToken, user, tenants) y fija las cookies.

Todos los endpoints de 2FA cuelgan de /api/v1/auth:

MétodoRutaAuthPara qué
POST/2fa/setupJWTGenera un secreto TOTP; devuelve secret, qrCodeDataUrl, otpauthUrl
POST/2fa/verify-setupJWTActiva TOTP con { secret, token } cuando el usuario ha escaneado el QR
POST/2fa/email/send-codeJWTEnvía un código al usuario con sesión, para activar el 2FA por email
POST/2fa/email/enableJWTActiva el 2FA por email con { token }
POST/2fa/disableJWTDesactiva un método con { token, method } (totp o email). Si no queda ninguno, se borran los dispositivos de confianza y los códigos de recuperación
GET/2fa/recovery-codes/statusJWT{ remaining }: códigos de recuperación sin usar
POST/2fa/recovery-codes/regenerateJWTEmite un lote nuevo con { password }
POST/2fa/email-codetempTokenEnvía por email un código de 6 dígitos durante el login, cuando el usuario no tiene la app de autenticación a mano
POST/2fa/validatetempTokenValida un código totp, email o recovery y cambia el tempToken por una sesión completa
GET/2fa/trusted-devicesJWTLista los dispositivos de confianza del usuario (id, userAgent, trustedUntil, lastSeenAt, createdAt, fingerprintShort)
DELETE/2fa/trusted-devices/:idJWTRevoca un dispositivo de confianza (404 si no es tuyo)

Al activar el primer método de 2FA se devuelve, una sola vez, un lote de recoveryCodes. Los códigos por email caducan a los 10 minutos.

2. API keys (storefront + servidor a servidor)​

Las keys tienen la forma sk_live_<random>. La API reconoce los prefijos sk_live_ y sk_test_.

Las keys las crea un owner o un admin, desde Ajustes de la tienda → Claves API en el dashboard, o con:

POST /api/v1/dashboard/org/api-keys
Authorization: Bearer <JWT>
Content-Type: application/json

{
"name": "Headless storefront — shop.example.com",
"scopes": ["products:read"],
"allowedDomains": ["shop.example.com", "*.example.com"],
"kind": "server",
"tenantPlatformId": "<store id>"
}

La respuesta (201) trae la key completa en key. Solo se muestra una vez. Después solo verás su prefijo.

tenantPlatformId es opcional: con él, la key solo funciona para esa tienda; sin él, funciona para todas las tiendas de la organización.

kind es browser (por defecto) o server; mira Tipos de key más abajo.

Endpoints relacionados (JWT; owner o admin para escribir):

MétodoRutaPara qué
GET/api/v1/dashboard/org/api-keysLista todas las keys de la organización, con su tienda, scopes, tipo y último uso
PATCH/api/v1/dashboard/org/api-keys/:idCambia active, allowedDomains o name. Los scopes no se pueden cambiar: crea una key nueva
DELETE/api/v1/dashboard/org/api-keys/:idBorra la key

Usar la key​

Envíala como cabecera:

Authorization: Bearer sk_live_...

o:

X-API-Key: sk_live_...

Scopes​

Los scopes que puedes poner en una key:

ScopeQué abre
products:readLeer productos, categorías, colecciones, páginas y FAQs, y los endpoints públicos de storefront (señales SEO, schema graph, widget de FAQ)
products:writeLanzar syncs por API
promotions:readLectura del motor de promociones
mcp:publicEl endpoint MCP de solo lectura de la tienda, para agentes de IA

Una petición a la que le falta un scope recibe 403 con "message": "Insufficient scopes" y la lista required. Todos los endpoints salvo los de promociones devuelven además la lista missing.

Whitelist de dominios​

allowedDomains limita qué hosts de storefront pueden usar la key en los endpoints del widget de FAQ (/public/faq) y de señales SEO. Los patrones son hosts exactos (shop.example.com) o comodines (*.example.com, que también acepta example.com). Una petición cuyo host no esté en la lista recibe 403.

Una key con la lista vacía acepta cualquier origen, y una key browser con la lista vacía tampoco pasa la comprobación de contexto de navegador de abajo. Rellena la lista en toda key que vaya en un storefront.

Tipos de key: browser vs server​

kindQuién la tieneQué comprueba la API
browser (por defecto)La página de tu storefront: embed.js, el widget de FAQ, el Schema Injector. Cualquiera puede leerla en el código fuente de la página.En los endpoints del widget de FAQ y de señales SEO, Origin/Referer tiene que estar en allowedDomains y, si esa lista no está vacía, la petición tiene que venir de un navegador real (cabeceras Sec-Fetch-* + un User-Agent Mozilla). Un curl o un fetch desde Node se rechaza con 403.
serverTu servidor: un loader de Hydrogen, Next.js o Nuxt, un BFF, un paso de build. Nunca llega al navegador.Sin comprobación de contexto de navegador. allowedDomains sigue aplicando: el servidor indica el storefront para el que renderiza en la cabecera X-Clione-Storefront-Host (host pelado, host:port o URL completa), o reenvía el Origin/Referer de la página. El host tiene que estar en la lista; con allowlist y sin host declarado, la petición da 403.

Con @clione/seo, pon storefrontHost y la cabecera se envía en cada llamada:

const clione = createClioneSeo({
apiUrl: process.env.CLIONE_API_URL!,
apiKey: process.env.CLIONE_API_KEY!, // a `server` key
platform: 'shopify',
storefrontHost: 'shop.example.com', // → X-Clione-Storefront-Host
})

HTTP directo:

GET /api/v1/public/seo-signals/shopify/product/12345
Authorization: Bearer sk_live_...
X-Clione-Storefront-Host: shop.example.com

No uses nunca una key server en código de cliente: se salta a propósito la protección de navegador.

Rotación de keys​

  1. Crea una key nueva con los mismos scopes, dominios y tienda.
  2. Actualiza tu storefront o tu servidor para que la use.
  3. Revoca la antigua con PATCH /api/v1/dashboard/org/api-keys/:id { "active": false } o bórrala. Las peticiones que la usen empiezan a devolver 401.