Files
cairnquire/.project/specs/authentication-api.md
2026-06-04 08:50:34 -04:00

4.0 KiB

Authentication API

Milestone 3 uses first-party authentication only. Cairnquire does not implement OAuth in v1.

Browser Sessions

Browser users authenticate with passkeys or password fallback. Successful login sets an opaque cairnquire_session cookie. The raw session token is never stored; the database stores a SHA-256 hash and revocation metadata.

Endpoints:

  • GET /setup
  • POST /api/setup
  • GET /login
  • GET /password-reset?token=...
  • GET /account
  • GET /account/tokens/new
  • POST /api/auth/register/password
  • POST /api/auth/login/password
  • POST /api/auth/password/reset
  • POST /api/auth/logout
  • GET /api/auth/me
  • POST /api/auth/password/change
  • DELETE /api/auth/account
  • POST /api/auth/passkeys/register/begin
  • POST /api/auth/passkeys/register/finish?challengeId=...
  • POST /api/auth/passkeys/login/begin
  • POST /api/auth/passkeys/login/finish?challengeId=...

On an empty database, the one-time /setup wizard creates the first admin account and records whether public signup is enabled. Later public registrations are accepted only when that setting is enabled and always become viewer; role changes are admin-managed from the account screen. Public registration endpoints only create new accounts. Adding or changing credentials on an existing account requires an authenticated browser session.

API Tokens

Developer clients use bearer tokens:

Authorization: Bearer cq_pat_<id>_<secret>

The token is shown once. The server stores the token id and SHA-256 hash of the secret. Tokens can be scoped and revoked. Browser users create tokens at /account/tokens/new.

Endpoints:

  • GET /api/tokens
  • POST /api/tokens
  • DELETE /api/tokens/{id}

Supported scopes:

  • docs:read
  • docs:write
  • sync:read
  • sync:write
  • admin

Authorization is role plus scope: a token must have the required scope, and the owning user role must allow that operation.

Account Administration

Administrators update user roles and disabled-account state in one bulk form. Disabled accounts cannot authenticate with passwords, passkeys, existing sessions, or API tokens. At least one enabled administrator account must remain.

Administrators can send a password-reset email to a user who cannot log in. Each click creates a fresh one-hour link and invalidates that user's earlier unused reset links. Completing a reset changes the password, consumes the link, and revokes the user's existing browser sessions.

Endpoints:

  • PATCH /api/admin/auth-users
  • POST /api/admin/auth-users/{id}/password-reset
  • GET /password-reset?token=...
  • POST /api/auth/password/reset

Device Flow

CLI clients can avoid handling browser cookies by using the first-party device flow:

  1. Client calls POST /api/device/start.
  2. Server returns deviceCode, userCode, verificationUri, and polling interval.
  3. User opens the verification URL in a browser and signs in normally.
  4. User approves the code with POST /api/device/approve.
  5. Client polls POST /api/device/token.
  6. Server returns a normal bearer API token and consumes the device code.

This is intentionally inspired by OAuth device authorization, but it is not a general OAuth provider. There are no client registrations, redirect URIs, refresh tokens, consent screens, or third-party identity dependencies.

Route Protection

  • Setup-only until configured: initial setup page and endpoint.
  • Public: health, static assets, document reads, login and password-reset pages, password/passkey begin-login/register endpoints when signups are enabled, password-reset submission, device start/token polling.
  • Authenticated browser pages: account management and token creation screens.
  • Session or bearer token required: auth profile/logout, edit/save APIs, uploads, sync/content APIs, device approval.
  • Admin role required: admin APIs and API token management.

WebSocket authentication is session/bearer aware through the shared middleware model. API token creation and account management require a browser session so bearer tokens cannot mint more bearer tokens.