# 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
