Skip to main content

Authentication

Every Clone endpoint requires IsAuthenticated. Two credential shapes are accepted, both detected automatically by the server.

Credential shapes

ShapeHeaderLifetimeUsed by
JWTAuthorization: Bearer <access>60 minutes (refreshable)Web, Desktop, browser dashboards.
API keyX-Clone-API-Key: clone_<token>Long-lived (rotate manually)CLI, MCP server (stdio), CI, scripts.

The MCP server (apps/mcp/src/index.ts) chooses the right header automatically based on the token shape — values starting with clone_ go in X-Clone-API-Key, anything else is sent as Authorization: Bearer ….

Endpoints (/api/auth/)

MethodPathPurpose
POST/api/auth/signup/Create a new account.
POST/api/auth/login/Log in with email + password. Returns { access, refresh, user }.
POST/api/auth/logout/Invalidate the current refresh token.
POST/api/auth/token/refresh/Exchange a refresh token for a fresh access token.
GET/api/auth/me/Return the authenticated user.
POST/api/auth/invite/validate/Validate an invite code before signup.
POST/api/auth/waitlist/Add an email to the public waitlist.
GET / POST/api/auth/keys/List the user's API keys / issue a new one.
GET / DELETE/api/auth/keys/<uuid>/Inspect or revoke a specific API key.

JWT flow

# 1. Login.
curl -sS -X POST https://api.clone.is/api/auth/login/ \
-H "Content-Type: application/json" \
-d '{"email":"you@example.com","password":"…"}'
# → { "access": "<jwt>", "refresh": "<jwt>", "user": {...} }

# 2. Use the access token. (60-min lifetime.)
curl -sS https://api.clone.is/api/auth/me/ \
-H "Authorization: Bearer <access>"

# 3. When access expires, exchange the refresh.
curl -sS -X POST https://api.clone.is/api/auth/token/refresh/ \
-H "Content-Type: application/json" \
-d '{"refresh":"<refresh>"}'
# → { "access": "<new jwt>" }

Browser surfaces (Web, Desktop) refresh access tokens on a 50-minute timer so the user never sees a forced re-login. Servers and headless scripts should use API keys instead.

API-key flow

# 1. Issue a key.
curl -sS -X POST https://api.clone.is/api/auth/keys/ \
-H "Authorization: Bearer <access>" \
-H "Content-Type: application/json" \
-d '{"label":"my-laptop"}'
# → { "id": "<uuid>", "key": "clone_xxxxxxxxxxxxxx", "label": "my-laptop", "created_at": "..." }
#
# IMPORTANT: the raw key is only returned once. Copy it now or revoke and re-issue.

# 2. Use it.
curl -sS https://api.clone.is/api/auth/me/ \
-H "X-Clone-API-Key: clone_xxxxxxxxxxxxxx"

# 3. Revoke it later.
curl -sS -X DELETE https://api.clone.is/api/auth/keys/<uuid>/ \
-H "Authorization: Bearer <access>"

Multi-tenant MCP

When the MCP server runs in MCP_TRANSPORT=http (production at clone.is/mcp), each MCP session is bound to whatever credential the client provided on its initialize request. One MCP instance therefore serves many users — the server forwards the per-session token upstream rather than holding a shared one. See Apps → MCP.

What goes wrong

SymptomCause
401 UnauthorizedMissing header, expired access token, revoked API key.
401 immediately after token issueClock skew on the producing host (JWT iat/exp rely on accurate time).
Endpoint works in browser but 401 from MCPBrowser session uses cookies; MCP needs an explicit X-Clone-API-Key or Authorization: Bearer.