Context is Everything logo

6. Auth & Permissions

Authentication Mechanisms

Sasha Studio uses four authentication mechanisms, applied by middleware in server/middleware/auth.js.

1. JWT Token Authentication

Middleware: authenticateToken()

  • Generation: generateToken(user) creates a JWT containing { userId, username, isAdmin }
  • Expiry: None. Tokens do not expire.
  • Secret: JWT_SECRET env var (falls back to hardcoded dev default)
  • Transport: Authorization: Bearer <token> header
  • Storage: Client stores in localStorage
  • Validation: Standard JWT verify, then user lookup by userId from token payload

2. API Key Authentication

Middleware: authenticateApiKey()

  • Format: sk_<random> prefix
  • Storage: bcrypt hash in api_keys.key_hash
  • Transport: X-API-Key: <key> header or Authorization: Bearer sk_<key>
  • Validation: Iterate all active keys, bcrypt compare each (note: O(n) in number of keys)
  • Usage tracking: last_used_at updated on each successful use
  • Signing: Each key has an HMAC signing_secret for callback payload verification

3. Localhost Bypass

Middleware: authenticateTokenOrLocalhost(), authenticateApiKeyOrLocalhost()

  • Detection: Request IP checked against: 127.0.0.1, ::1, ::ffff:127.0.0.1, localhost
  • Extended detection: Also matches 172.*, 10.*, 192.168.* (Docker networking)
  • Purpose: Allows Claude CLI process (running locally in container) to call APIs without auth
  • Usage: Cron execution, MCP CLI listing, internal operations

4. WebSocket Authentication

Middleware: authenticateWebSocket()

  • JWT token passed as query parameter on WebSocket connection
  • Validated same as HTTP JWT
  • Connection rejected if invalid

Authorization Model

Role Model

Binary admin/non-admin. No roles, no RBAC, no permissions matrix.

Check Implementation
Is admin? user.is_admin === 1 (or isAdmin from JWT)
Is authenticated? Valid JWT in request
Is localhost? IP address check

Middleware Combinations

Middleware Auth Required Admin Required Localhost OK
authenticateToken Yes No No
authenticateAdmin Yes Yes No
authenticateTokenOrLocalhost Yes No Yes (bypasses)
authenticateApiKey API key No No
authenticateApiKeyOrLocalhost API key No Yes (bypasses)

Route Protection Summary

Route Group Middleware Access Level
/api/auth/login,register,forgot-password,reset-password None Public
/api/auth/user,change-password,logout authenticateToken Authenticated
/api/files/*, /api/docs/*, /api/git/* authenticateToken Authenticated
/api/skills/*, /api/scheduler/* authenticateToken Authenticated
/api/meetings/* authenticateToken Authenticated
/api/profile/* authenticateToken Authenticated
/api/setup/* authenticateToken Authenticated
/api/admin/* authenticateAdmin Admin only
/api/admin/cloud-connections/* authenticateAdmin Admin only
/api/admin/cloud-mounts/* authenticateAdmin Admin only
/api/v1/meetings/* authenticateApiKey API key
/api/v1/projects/* authenticateApiKey API key
/api/v1/tasks/* authenticateApiKeyOrLocalhost API key or localhost
/api/cron/* localhost check Localhost only
/api/mcp/cli/list localhost check Localhost only
/api/mcp/:service/* authenticateToken Authenticated

Session Management

Architecture Decision

Session management underwent a major overhaul (2025-01-24). Key changes:

  • Removed: Database session validation layer
  • Source of truth: Filesystem (Claude CLI session dirs), not browser storage
  • Resilience: System survives browser data clearing and container restarts
  • Permissions: Default to bypassPermissions (all permissions on)

Session Lifecycle

  1. User sends first message → new Claude CLI process spawned
  2. Session ID generated (UUID)
  3. Session directory created on filesystem
  4. Subsequent messages reuse the same CLI process
  5. Session recorded in sessions table for analytics
  6. No explicit session expiry (CLI process may be killed by timeout)

JWT Token Lifecycle

  1. Generated on login (no expiry set)
  2. Stored in client localStorage
  3. Sent on every API request
  4. Valid until server restart changes JWT_SECRET (if using default)
  5. No refresh token mechanism
  6. No token revocation mechanism

Tenant Model

Single-tenant per deployment. Each Docker container serves one organization. There is no tenant isolation within a single instance -- all users in a deployment share:

  • The same database
  • The same file system
  • The same Claude CLI configuration
  • The same knowledge base
  • The same API keys and integrations

Admin Access

Admin users can:

  • Create, edit, delete other users
  • Configure AI providers (Claude direct, AWS Bedrock)
  • View analytics and usage reports
  • Manage cloud drive connections and mounts
  • Configure MCP integrations
  • Manage output styles
  • Send welcome emails
  • Refresh analytics stats

Admin is determined by is_admin column in users table. First registered user is automatically admin.

Security Considerations

Password Storage

  • bcrypt with default salt rounds
  • No password complexity requirements enforced server-side
  • Password change requires current password

Token Security

  • JWT secret should be set via JWT_SECRET env var
  • Default fallback secret exists for development (insecure in production)
  • No token rotation or refresh mechanism
  • No session invalidation on password change

API Key Security

  • Keys hashed with bcrypt (irreversible)
  • Full key shown once at creation, never retrievable again
  • Keys can be deactivated via is_active flag
  • O(n) comparison on each request (scales poorly with many keys)

Rate Limiting

  • express-rate-limit middleware
  • Configurable: RATE_LIMIT_MAX (default 100), RATE_LIMIT_WINDOW_MS (default 15 min)
  • Can be disabled: RATE_LIMIT_ENABLED=false

Security Headers

  • Helmet.js enabled by default (HELMET_ENABLED=true)
  • Can be disabled for development