Skip to main content

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 tokens
  • POST /api/v1/auth/refresh — refresh access token
  • POST /api/v1/auth/logout — blacklists the current tokens
  • GET /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:

MethodPathPurpose
POST/2fa/setupGenerate TOTP secret + QR code data URL
POST/2fa/verify-setupEnable 2FA after user scans the QR
POST/2fa/disableDisable 2FA (requires current TOTP code) + clears all trusted devices
POST/2fa/email-codeEmail a 6-digit fallback code when the user lost their authenticator
POST/2fa/validateValidate a code (TOTP or email) and exchange tempToken → full session
GET/2fa/trusted-devicesList the current user's trusted devices (id, userAgent, lastSeenAt, trustedUntil, fingerprintShort)
DELETE/2fa/trusted-devices/:idRevoke 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, FAQs
  • products:write — reserved for future; not yet enforced
  • mcp:access — MCP protocol access
  • admin — 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 returning 401.
  • Generate a new key with the same scopes + domains, update the storefront, then revoke the old one.