Authentication
Clione has two authentication modes, each used for different endpoints.
1. JWT (dashboard / admin)
Issued after a user logs in. Sent as httpOnly cookies + Authorization: Bearer header for cross-origin compatibility.
POST /api/v1/auth/login— email + password → returns access + refresh tokensPOST /api/v1/auth/refresh— refresh access tokenPOST /api/v1/auth/logout— blacklists the current tokensGET /api/v1/auth/me— current user
Supports:
- Account lockout (5 failed attempts → 15 min cooldown)
- Refresh-token rotation (old tokens blacklisted)
- Optional 2FA (TOTP + email codes) at
POST /api/v1/auth/2fa/* - Google OAuth at
POST /api/v1/auth/google
Used by the dashboard and by T42 staff tools. Not for storefront integrations.
2FA flow
When a user with totpEnabled=true logs in from a non-trusted device, the login response is:
{ "requires2FA": true, "tempToken": "...", "methods": ["totp", "email"] }
The client then calls POST /api/v1/auth/2fa/validate with:
{
"tempToken": "<from login>",
"token": "<6-digit code>",
"method": "totp",
"deviceFingerprint": "<optional — if present, creates a trusted device for 30d>"
}
Other 2FA endpoints:
| Method | Path | Purpose |
|---|---|---|
POST | /2fa/setup | Generate TOTP secret + QR code data URL |
POST | /2fa/verify-setup | Enable 2FA after user scans the QR |
POST | /2fa/disable | Disable 2FA (requires current TOTP code) + clears all trusted devices |
POST | /2fa/email-code | Email a 6-digit fallback code when the user lost their authenticator |
POST | /2fa/validate | Validate a code (TOTP or email) and exchange tempToken → full session |
GET | /2fa/trusted-devices | List the current user's trusted devices (id, userAgent, lastSeenAt, trustedUntil, fingerprintShort) |
DELETE | /2fa/trusted-devices/:id | Revoke a single trusted device (tenant-scoped — returns 404 if the device isn't yours) |
Trusted devices are created when the deviceFingerprint field is present on the /2fa/validate call. Each device gets a 30-day trust window; subsequent logins from the same fingerprint skip the 2FA prompt and bump lastSeenAt.
2. API keys (storefront + server-to-server)
Format: sk_live_<32-random-bytes-base64> (prefix sk_test_ for sandboxing).
Create keys from Admin → Apps → [App] → API Keys in the dashboard, or via:
POST /api/v1/admin/apps/:appId/keys
Authorization: Bearer <JWT>
Content-Type: application/json
{
"name": "Embed widget — shop.example.com",
"scopes": ["products:read"],
"allowedDomains": ["shop.example.com", "*.example.com"]
}
Only shown once on creation. After that you only see the key prefix.
Using the key
Send as header:
Authorization: Bearer sk_live_...
or:
X-API-Key: sk_live_...
Scopes
products:read— list and read products, categories, pages, FAQsproducts:write— reserved for future; not yet enforcedmcp:access— MCP protocol accessadmin— full admin (do not expose publicly)
Domain whitelist
For public-facing keys (embed widget), the allowedDomains field enforces Origin / Referer validation. Requests from non-whitelisted origins return 403.
Leave allowedDomains empty only for server-to-server use (no browser origin). Public keys with an empty whitelist log warnings and we'll tighten this in future releases.
Key rotation
- Revoke via
PATCH /api/v1/admin/keys/:id { "active": false }— existing requests using the key start returning401. - Generate a new key with the same scopes + domains, update the storefront, then revoke the old one.