Skip to content

Authentication

Two surfaces, two auth schemes.

/v1/* — API keys

Public inference endpoints accept either form (the proxy checks both, in order):

Header Format Notes
Authorization Bearer <key> Standard OpenAI-style
x-api-key <key> Anthropic-style; preferred when both are present

Successful authentication updates last_used_at on the key row at most once per minute (write coalescing); per-request token totals are added on response close.

curl https://proxy.worv.ai/v1/models \
  -H "Authorization: Bearer $API_KEY"
Failure mode Response
Missing header / invalid key 401 {"error":"Unauthorized"}
Key marked is_active = 0 401 {"error":"Unauthorized"}

API keys are minted from the admin console (see Admin · API Keys). They are opaque, treat them as secrets, and they cannot be retrieved again after deletion.

/api/* — Session login + CSRF

Admin-console endpoints use a standard cookie session backed by Flask's signed-session cookie (HttpOnly, Secure, SameSite=Lax, 365 day lifetime).

Login flow.

# 1. Authenticate; receive session cookie + csrf_token in JSON body.
curl -c cookies.txt https://admin.proxy.worv.ai/api/login \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": "<password>"}'
# {"ok": true, "username": "admin", "csrf_token": "<hex>"}

# 2. Subsequent state-changing calls (POST/PATCH/DELETE) MUST include
#    X-CSRF-Token (or "_csrf" in the JSON body).
curl -b cookies.txt https://admin.proxy.worv.ai/api/keys \
  -H "Content-Type: application/json" \
  -H "X-CSRF-Token: <hex>" \
  -d '{"name": "claude-code-laptop"}'
Endpoint Method Auth
/api/login POST None — rate-limited to 10/min/IP
/api/logout POST Session + CSRF
/api/me GET Session (returns 401 if anonymous)

GET, HEAD, OPTIONS requests are exempt from CSRF; unauthenticated calls are also exempt (only the login itself can be unauthenticated).

POST /api/login returns 429 Too Many Requests once the per-IP limit is exhausted (10/minute, sliding window).

Security headers

All responses include the following hardening headers:

X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; frame-ancestors 'none'

The /health path is exempt from the HTTPS-redirect middleware so that load balancers can probe over plain HTTP.