Authentication
Three credential types
Section titled “Three credential types”| Type | Who | Lifetime | Where used |
|---|---|---|---|
| Session cookie | Users (dashboard) | 7 days absolute, 30 min sliding idle | Browser |
| API key | Users (CLI, scripts) | No expiry; revocable | Authorization: Bearer z4k_... |
| Agent token + HMAC secret | Agents | No expiry; revocable | WebSocket handshake (token) + frame signing (HMAC secret) |
Login (session)
Section titled “Login (session)”POST /api/v1/auth/loginContent-Type: application/json
{ "email": "...", "password": "..." }Response sets z4j_session=...; HttpOnly; Secure; SameSite=Lax. Subsequent requests send the cookie automatically. The session is rejected after Z4J_SESSION_ABSOLUTE_LIFETIME_SECONDS (default 604800 = 7 days) or after Z4J_SESSION_IDLE_TIMEOUT_SECONDS (default 1800 = 30 minutes) of inactivity, whichever fires first.
Logout
Section titled “Logout”POST /api/v1/auth/logoutInvalidates the session server-side.
Other session endpoints
Section titled “Other session endpoints”| Endpoint | Purpose |
|---|---|
GET /api/v1/auth/me | Current user + project memberships. |
PATCH /api/v1/auth/me | Update display name etc. |
POST /api/v1/auth/change-password | Authenticated password change. |
GET /api/v1/auth/sessions | List the user’s active sessions. |
POST /api/v1/auth/sessions/{session_id}/revoke | Revoke a single session. |
Password reset
Section titled “Password reset”POST /api/v1/auth/password-reset/request # public; body: {"email": "..."}POST /api/v1/auth/password-reset/confirm # public; body: {"token": "...", "password": "..."}The reset token TTL is 30 minutes. The request endpoint never reveals whether the email exists — it returns success either way.
API keys
Section titled “API keys”Create from Dashboard, Settings, API keys. Tokens begin with z4k_ and are shown once at creation.
curl -H "Authorization: Bearer z4k_..." \ https://z4j.example.com/api/v1/projects/billing-prod/tasksManagement endpoints:
| Endpoint | Purpose |
|---|---|
GET /api/v1/api-keys | List the caller’s keys (prefix + scopes only; plaintext never re-emitted). |
POST /api/v1/api-keys | Mint a new key. Response carries the plaintext once. |
DELETE /api/v1/api-keys/{key_id} | Revoke a key. |
GET /api/v1/api-keys/scopes | Catalog of valid scopes. |
API keys can be project-scoped (bound to one project’s slug at mint time) or unscoped. A project-scoped key automatically filters GET /projects to that one project and is rejected on cross-project paths.
Agent tokens
Section titled “Agent tokens”Minted via the agents API — POST /api/v1/projects/{slug}/agents returns both a bearer token (WebSocket handshake) and an hmac_secret (per-frame signing). Agents refuse to start without the HMAC secret.
Rate limits on auth (per IP, 1-minute window)
Section titled “Rate limits on auth (per IP, 1-minute window)”| Endpoint | Cap |
|---|---|
POST /auth/login | 20 hits / minute |
POST /auth/password-reset/request and /confirm | 10 hits / minute (combined bucket) |
GET /invitations/preview and POST /invitations/accept | 30 hits / minute (combined bucket) |
Per-account lockout runs in parallel with the per-IP login cap: repeated wrong passwords on a single account lock that account for a cooling-off window regardless of source IP. See security rate limits for the design rationale.
MFA / SSO / OAuth2
Section titled “MFA / SSO / OAuth2”Not currently shipped. The recommended interim approach is to put z4j behind an authenticated reverse proxy (oauth2-proxy, Cloudflare Access, Pomerium) so the SSO layer authenticates the user before they reach z4j’s login form.