Issue #744: Complete Authentication and RBAC System
This document describes AutoBot's authentication and Role-Based Access Control (RBAC) system.
AutoBot implements a comprehensive security system with:
- JWT-based authentication
- Session management with Redis persistence
- Role-Based Access Control (RBAC)
- Fine-grained permission decorators
- Audit logging
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ API Request │────▶│ auth_middleware │────▶│ SecurityLayer │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │
▼ ▼
┌──────────────┐ ┌─────────────┐
│ auth_rbac │ │ Audit Log │
└──────────────┘ └─────────────┘
Provides core authentication functionality:
from src.auth_middleware import (
get_current_user, # FastAPI dependency for user authentication
check_admin_permission, # Dependency for admin-only endpoints
auth_middleware, # Global middleware instance
)Provides fine-grained permission control:
from src.auth_rbac import (
Permission, # Permission enum
Role, # Role enum
require_permission, # Permission decorator
require_role, # Role decorator
require_any_permission, # OR-based permission check
)AuthenticationMiddleware above is a lazy_singleton
(auth_middleware.get_auth_middleware()), not a Starlette middleware — it is never passed to
app.add_middleware(). For /api/user-management/* routes (api/user_management/users.py,
teams.py, organizations.py) nothing at the route decorator calls it directly either: no
route there declares dependencies=[Depends(get_current_user)]. Authentication is enforced
transitively, through the dependency chain each route already needs for its data access:
get_user_service / get_team_service / get_organization_service (dependencies.py:306-326)
-> get_tenant_context (dependencies.py:157)
-> get_current_user (dependencies.py:53)
get_current_user calls get_auth_middleware().get_user_from_request(request) and raises
401 {"detail": "Authentication required"} when it returns None. That is the mechanism
behind the 401 a live probe of POST /api/user-management/users returns (#15736, #15737) —
found by tracing this chain, not by reading the route decorator.
The dependencies=[Depends(user_management_route_marker)] present on most of these routes is
not this gate: see that function's docstring (api/user_management/dependencies.py) — it
is a no-op, renamed from require_user_management_enabled because the old name read as a live
authorization check when it has unconditionally passed since #10636. Per-route authorization
posture (open / authenticated / admin-only) for every route this chain reaches is asserted in
api/user_management/user_management_route_posture_test.py; read that file, not this
paragraph, if the two ever disagree.
| Role | Description | Access Level |
|---|---|---|
admin |
Full system access | All permissions |
operator |
Execute operations | Execute but not manage |
analyst |
Analytics focus | View and export analytics |
editor |
Content creation | Create and modify content |
user |
Standard access | Basic read operations |
readonly |
View only | Read-only access |
Permissions follow the naming convention: category.action or category.resource.action
| Category | Examples | Description |
|---|---|---|
api |
api.read, api.write, api.admin |
Core API access |
knowledge |
knowledge.read, knowledge.write, knowledge.delete |
Knowledge base operations |
analytics |
analytics.view, analytics.export, analytics.manage |
Analytics access |
agent |
agent.view, agent.execute, agent.terminal |
Agent operations |
workflow |
workflow.view, workflow.create, workflow.execute |
Workflow management |
files |
files.view, files.upload, files.delete |
File operations |
security |
security.view, security.audit, security.manage |
Security features |
admin |
admin.users.*, admin.config.*, admin.system |
Administration |
mcp |
mcp.read, mcp.execute, mcp.manage |
MCP operations |
batch |
batch.view, batch.create, batch.execute |
Batch jobs |
sandbox |
sandbox.view, sandbox.execute, sandbox.manage |
Sandbox operations |
Gates every tool an admin-configured external MCP server exposes to chat — one
coarse grant, not one per server. Per-server granularity is
MCPServerConfig.allowed_roles, checked by the external bridge itself on top
of this gate, never instead of it. Admin-only by owner decision; not added to
any other role's default grants.
All endpoints require basic authentication:
from fastapi import APIRouter, Depends
from src.auth_middleware import get_current_user
router = APIRouter()
@router.get("/endpoint")
async def my_endpoint(
current_user: dict = Depends(get_current_user),
):
"""Requires authenticated user."""
return {"user": current_user["username"]}For endpoints requiring admin role:
from src.auth_middleware import check_admin_permission
@router.get("/admin/users")
async def list_users(
admin_check: bool = Depends(check_admin_permission),
):
"""Requires admin role."""
return {"users": [...]}For specific permission requirements:
from src.auth_rbac import Permission, require_permission
@router.get("/analytics/export")
async def export_analytics(
current_user: dict = Depends(get_current_user),
_: bool = Depends(require_permission(Permission.ANALYTICS_EXPORT)),
):
"""Requires analytics.export permission."""
return {"data": [...]}For endpoints requiring specific roles:
from src.auth_rbac import Role, require_role
@router.post("/workflows/execute")
async def execute_workflow(
current_user: dict = Depends(get_current_user),
_: bool = Depends(require_role(Role.ADMIN, Role.OPERATOR)),
):
"""Requires admin or operator role."""
return {"status": "executing"}When any of several permissions is acceptable:
from src.auth_rbac import require_any_permission
@router.get("/dashboard")
async def view_dashboard(
current_user: dict = Depends(get_current_user),
_: bool = Depends(require_any_permission(
Permission.ANALYTICS_VIEW,
Permission.ADMIN_SYSTEM,
)),
):
"""Requires either analytics.view OR admin.system permission."""
return {"dashboard": {...}}Client Server
│ │
│ POST /auth/login │
│ {username, password} │
│─────────────────────────▶│
│ │ Validate credentials
│ │ Create JWT token
│ {token: "eyJ..."} │
│◀─────────────────────────│
│ │
│ GET /api/endpoint │
│ Authorization: Bearer │
│─────────────────────────▶│
│ │ Verify JWT
│ │ Extract user data
│ {response} │
│◀─────────────────────────│
Sessions are stored in Redis with configurable TTL:
# Session creation
session_id = auth_middleware.create_session(user_data, request)
# Session retrieval
session = auth_middleware.get_session(session_id)
# Session invalidation (logout)
auth_middleware.invalidate_session(session_id)After 3 failed login attempts, the account is locked for 15 minutes:
# Check if account is locked
if auth_middleware.is_account_locked(username):
raise_auth_error("AUTH_0001", "Account temporarily locked")All authentication events are logged to data/audit.log:
{
"timestamp": "2026-02-03T14:00:00",
"user": "admin",
"action": "login_successful",
"outcome": "success",
"details": {"ip": "192.168.1.100", "role": "admin"}
}For development/personal use, authentication can be bypassed:
export AUTOBOT_SINGLE_USER_MODE=trueIn single-user mode:
- All requests are treated as admin
- No authentication required
- Permission checks are bypassed
| Variable | Description | Default |
|---|---|---|
AUTOBOT_JWT_SECRET |
JWT signing secret (min 32 chars) | Auto-generated |
AUTOBOT_SINGLE_USER_MODE |
Enable single-user mode | true |
AUTOBOT_AUDIT_LOG_FILE |
Audit log file path | data/audit.log |
security_config:
enable_auth: true
session_timeout_minutes: 30
max_failed_attempts: 3
lockout_duration_minutes: 15
roles:
admin:
permissions:
- "files.*"
- "allow_goal_submission"
- "allow_kb_read"
- "allow_kb_write"
- "allow_shell_execute"
user:
permissions:
- "files.view"
- "files.download"
- "allow_goal_submission"
- "allow_kb_read"Run the RBAC tests:
python -m pytest tests/security/test_auth_rbac.py -vRun the authentication security tests:
python scripts/utilities/test-authentication-security.py| Code | Message | Description |
|---|---|---|
AUTH_0001 |
Account temporarily locked | Too many failed login attempts |
AUTH_0002 |
Authentication required | No valid token/session |
AUTH_0003 |
Insufficient permissions | User lacks required permission |
src/auth_middleware.py- Core authentication middlewaresrc/auth_rbac.py- RBAC permission systemsrc/security_layer.py- Security layer with permission checkingtests/security/test_auth_rbac.py- RBAC testsscripts/utilities/test-authentication-security.py- Security tests
The guest role fallback has been removed (Issue #744). All endpoints now require explicit authentication. To migrate:
- Add
Depends(get_current_user)to all endpoint parameters - Add
Issue #744: Requires authenticated user.to docstrings - For admin endpoints, add
Depends(check_admin_permission)
- Add permission to
Permissionenum insrc/auth_rbac.py - Add to appropriate role in
ROLE_PERMISSIONS - Add tests in
tests/security/test_auth_rbac.py - Document in this file
Issue #1801: Self-registration endpoint for new users
AutoBot supports user self-registration unconditionally (see "Deployment Modes" correction
below — no deployment mode disables it). New accounts are created through POST /auth/signup
without requiring an existing admin session.
POST /auth/signup
No authentication header is required.
{
"username": "jsmith",
"email": "jsmith@example.com",
"password": "Secret1234",
"display_name": "Jane Smith"
}| Field | Type | Required | Validation |
|---|---|---|---|
username |
string | Yes | 3–50 chars; alphanumeric, -, _ only; stored lowercase |
email |
string | Yes | Must contain @; max 255 chars; stored lowercase |
password |
string | Yes | 8–128 chars; must include at least one uppercase letter, one lowercase letter, and one digit |
display_name |
string | No | Free text; defaults to username if omitted |
200 OK on success:
{
"success": true,
"message": "Account created successfully. You can now log in.",
"username": "jsmith"
}The new account is assigned the user system role automatically. An admin must call PUT /user-management/users/{id}/role to elevate privileges.
| HTTP Status | Condition |
|---|---|
409 Conflict |
Username or email already exists |
422 Unprocessable Entity |
Field validation failure (see detail array) |
500 Internal Server Error |
Unexpected registration failure |
Earlier revisions of this document described a single_user / multi_user deployment mode
that gated signup and user-management behind 400/503 responses. That mode does not exist in
the current codebase: #10636/#10713 retired it outright, together with every bypass and gate
it powered. Concretely, none of the following happens today: POST /auth/signup never
returns 400 for a deployment mode, GET /user-management/users/me never returns a synthetic
admin (see Issue #2953 for that removal), and no /user-management/* route returns 503 for
"user management not enabled" — user_management_route_marker
(api/user_management/dependencies.py) is the retained, permanently-passing remnant of that
gate. AutoBot always runs full, Postgres-backed user management.
The only remaining mode axis for this subsystem is AUTOBOT_USER_MODE (single_company /
multi_company / provider — see user_management/config.py's UserManagementMode), which
toggles feature flags (team switcher, SSO, billing) but never disables authentication or the
signup endpoint. AUTOBOT_DEPLOYMENT_MODE is a separate, infrastructure-only setting
(hybrid / local / distributed) and has no bearing on user-management authorization.
New user visits /signup
│
▼
POST /auth/signup
username, email, password
│
├── 409 Conflict ──▶ "Username or email already taken"
├── 422 Validation ─▶ Show field-level errors
│
▼ 200 OK
Account created (role: user)
│
▼
POST /auth/login
username, password
│
▼ JWT token returned
Stored in localStorage as authToken
│
▼
Redirect to /dashboard
After first login the user has the user role. If admin privileges are needed, an existing admin must navigate to /admin/users and update the role via the inline dropdown or PUT /user-management/users/{id}/role.
- Admin panel guide and full user lifecycle:
docs/user/guides/user-management-guide.md - Role capabilities and permission matrix: Section 4 of the user management guide
- Password strength rules and change-password endpoint: Section 7 of the user management guide