Skip to content

Authentication & Permissions

Bonsai Backend uses two authentication mechanisms: JWT tokens for operator REST API access, and API keys for WebSocket conversation sessions.

Operator Authentication (JWT)

Login

Operators authenticate by posting credentials to the login endpoint:

bash
curl -X POST http://localhost:3000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{ "id": "operator", "password": "your-password" }'

The response includes an access token and a refresh token:

json
{
  "accessToken": "eyJhbG...",
  "refreshToken": "eyJhbG...",
  "operatorId": "operator",
  "roles": ["super_admin"]
}

Using Tokens

Include the access token in the Authorization header:

Authorization: Bearer eyJhbG...

Refreshing Tokens

When the access token expires, use the refresh token to obtain a new pair:

bash
curl -X POST http://localhost:3000/api/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{ "refreshToken": "eyJhbG..." }'

Role-Based Access Control (RBAC)

Operators are assigned one or more roles. Each role carries a set of permissions in entity:action format.

Roles

RoleDescription
super_adminFull system access — all permissions
content_managerManage content entities: projects, agents, stages, classifiers, transformers, tools, knowledge, providers, API keys
supportView projects and conversations, manage users and issues
developerRead-only access to most entities plus system configuration
viewerRead-only access to most entities

Permissions

Permissions follow the entity:action pattern:

EntityReadWriteDelete
Operatoroperator:readoperator:writeoperator:delete
Useruser:readuser:writeuser:delete
Projectproject:readproject:writeproject:delete
Agentagent:readagent:writeagent:delete
Stagestage:readstage:writestage:delete
Classifierclassifier:readclassifier:writeclassifier:delete
Context Transformercontext_transformer:readcontext_transformer:writecontext_transformer:delete
Tooltool:readtool:writetool:delete
Global Actionglobal_action:readglobal_action:writeglobal_action:delete
Guardrailguardrail:readguardrail:writeguardrail:delete
Knowledgeknowledge:readknowledge:writeknowledge:delete
Conversationconversation:readconversation:writeconversation:delete
Issueissue:readissue:writeissue:delete
Providerprovider:readprovider:writeprovider:delete
API Keyapi_key:readapi_key:writeapi_key:delete
Environmentenvironment:readenvironment:writeenvironment:delete
Migrationmigration:exportmigration:import
Systemsystem:config
Auditaudit:read
Analyticsanalytics:read

Defense in Depth

Security is enforced at two layers:

  1. Controller layercheckPermissions() validates the request before processing
  2. Service layerrequirePermission() ensures security even when services are called internally

API Keys

API keys provide authentication for WebSocket conversation sessions. They are scoped to a project and used by client applications (web apps, mobile apps, kiosks).

Creating an API Key

bash
curl -X POST http://localhost:3000/api/projects/my-project/api-keys \
  -H "Authorization: Bearer eyJhbG..." \
  -H "Content-Type: application/json" \
  -d '{ "name": "Production Web App" }'

The response includes the full key — this is the only time it's shown:

json
{
  "id": "key-uuid",
  "name": "Production Web App",
  "key": "bonsai_abcdef123456...",
  "keyPreview": "bonsai_abc...456",
  "isActive": true
}

Using API Keys

API keys are used exclusively for WebSocket authentication. The client sends the key in the auth message after connecting:

json
{
  "type": "auth",
  "apiKey": "bonsai_abcdef123456..."
}

Managing API Keys

  • ListGET /api/projects/:projectId/api-keys
  • Deactivate — Update isActive to false to disable without deleting
  • DeleteDELETE /api/projects/:projectId/api-keys/:id

Deactivated keys are immediately rejected on WebSocket authentication attempts.

Initial Setup

When the system has no operator accounts, the setup endpoint is available:

bash
POST /api/setup/initial-operator

This creates the first super operator account. The endpoint is disabled after the first operator is created.

Audit Logs

All write operations are recorded in the audit log with:

  • Who performed the action (userId)
  • What action was taken (action)
  • Which entity was affected (entityId, entityType)
  • Previous state (oldEntity) and new state (newEntity)
  • Timestamp

Audit logs are read-only and accessible to users with the audit:read permission.

Released under the Apache-2.0 License.