Files
GearBox/docs/authentication.md
Jean-Luc Makiola e34a2cad11
Some checks failed
CI / ci (push) Failing after 11s
docs: add authentication, API reference, and MCP server guides
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 14:00:03 +02:00

5.7 KiB

Authentication

GearBox uses a public-read, authenticated-write model. All GET endpoints are publicly accessible with no credentials required. Any request that modifies data (POST, PUT, PATCH, DELETE) requires authentication.

This is a single-user app. There is exactly one admin account.

Table of Contents


First-Time Setup

When no users exist, all write endpoints return 403 with { "error": "setup_required" }. To create the admin account, visit /login in the browser and complete the setup form, or call the setup endpoint directly:

POST /api/auth/setup
Content-Type: application/json

{
  "username": "admin",
  "password": "yourpassword"
}

Requirements:

  • username: any non-empty string
  • password: minimum 6 characters

This endpoint only works when no users exist. Subsequent calls return 403 { "error": "Setup already completed" }.

On success, a session cookie is set and 201 is returned:

{ "username": "admin" }

Web UI Authentication

Sessions use an httpOnly cookie named gearbox_session.

Property Value
Cookie name gearbox_session
httpOnly true
sameSite Lax
path /
Max age 30 days

The session expiry is automatically refreshed on each authenticated request. As long as the app is used at least once every 30 days, the session stays active.

Passwords are hashed with argon2 via Bun.password.

Changing Your Password

Requires an active session cookie.

PUT /api/auth/password
Content-Type: application/json

{
  "currentPassword": "oldpassword",
  "newPassword": "newpassword"
}

API Keys

API keys are intended for programmatic access (scripts, MCP clients, integrations). They are managed under Settings > API Keys in the web UI, or via the API endpoints listed below.

Key behavior

  • Keys are shown once at creation time. Store them securely.
  • Keys are stored as an argon2 hash. Only the 8-character prefix is stored in plaintext for display and lookup purposes.
  • Pass the key via the X-API-Key request header on any write request.
POST /api/items
X-API-Key: gbk_a1b2c3d4...
Content-Type: application/json

{ "name": "Revelate Tangle", "categoryId": 2 }

If both a session cookie and an X-API-Key header are present, the API key is checked first.


Auth Middleware Behavior

The middleware applied to /api/* (excluding /api/auth/*) follows these rules:

  1. GET requests — always allowed, no auth check.
  2. No users exist — returns 403 { "error": "setup_required" }.
  3. X-API-Key header present — verified against stored hashes; 401 on failure.
  4. gearbox_session cookie present — verified against sessions table; refreshed on success; 401 on failure.
  5. Neither credential present — returns 401 { "error": "Authentication required" }.

The /api/auth/* routes handle their own auth logic and are excluded from the global middleware.


Auth API Reference

GET /api/auth/me

Returns the current session state. Always public.

Response when logged in:

{
  "user": { "id": 1 },
  "setupRequired": false
}

Response when logged out, setup complete:

{
  "user": null,
  "setupRequired": false
}

Response when no users exist:

{
  "user": null,
  "setupRequired": true
}

POST /api/auth/setup

Create the first admin account. Only works when no users exist.

Request:

{
  "username": "admin",
  "password": "yourpassword"
}

Response: 201

{ "username": "admin" }

Sets gearbox_session cookie.


POST /api/auth/login

Log in with username and password.

Request:

{
  "username": "admin",
  "password": "yourpassword"
}

Response: 200

{ "username": "admin" }

Sets gearbox_session cookie. Returns 401 on invalid credentials.


POST /api/auth/logout

Clear the current session. No request body needed.

Response:

{ "ok": true }

Clears the gearbox_session cookie and deletes the session from the database.


PUT /api/auth/password

Change the admin password. Requires an active session cookie (not API key).

Request:

{
  "currentPassword": "oldpassword",
  "newPassword": "newpassword"
}

Response:

{ "ok": true }

Returns 401 if currentPassword is incorrect.


GET /api/auth/keys

List all API keys. Returns name, prefix, and creation timestamp — never the full key.

Requires auth.

Response:

[
  {
    "id": 1,
    "name": "Claude Code",
    "prefix": "gbk_a1b2",
    "createdAt": "2025-03-01T10:00:00.000Z"
  }
]

POST /api/auth/keys

Create a new API key. The full key is returned once and cannot be retrieved again.

Requires auth.

Request:

{ "name": "Claude Code" }

Response: 201

{
  "id": 1,
  "name": "Claude Code",
  "key": "gbk_a1b2c3d4e5f6g7h8i9j0...",
  "prefix": "gbk_a1b2"
}

DELETE /api/auth/keys/:id

Revoke an API key by ID. Requires auth.

Response:

{ "ok": true }

Frontend Behavior

  • A login button is shown in the top-right corner of the UI (Gitea-style).
  • The floating action button (FAB) for adding items is hidden when not logged in.
  • Edit and delete actions on items, threads, and setups require auth. Unauthenticated users see read-only views.
  • When setupRequired is true, the UI redirects to the setup flow.