Skip to content

Latest commit

 

History

History
479 lines (366 loc) · 15.6 KB

AUTHENTICATION_RBAC.md

File metadata and controls

479 lines (366 loc) · 15.6 KB

Authentication and RBAC System

Issue #744: Complete Authentication and RBAC System

This document describes AutoBot's authentication and Role-Based Access Control (RBAC) system.

Overview

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

Architecture

┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐
│   API Request   │────▶│ auth_middleware  │────▶│  SecurityLayer  │
└─────────────────┘     └──────────────────┘     └─────────────────┘
                               │                         │
                               ▼                         ▼
                        ┌──────────────┐         ┌─────────────┐
                        │  auth_rbac   │         │ Audit Log   │
                        └──────────────┘         └─────────────┘

Key Components

1. Authentication Middleware (src/auth_middleware.py)

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
)

2. RBAC Permission System (src/auth_rbac.py)

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
)

3. Per-Route Enforcement for User-Management Routes (Issue #15737)

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.

Roles

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

Permissions follow the naming convention: category.action or category.resource.action

Permission Categories

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

mcp.external (#11542)

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.

Usage Examples

Basic Authentication

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"]}

Admin-Only Endpoints

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": [...]}

Fine-Grained Permission Control

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": [...]}

Role-Based Access

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"}

OR-Based Permissions

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": {...}}

Authentication Flow

1. JWT Token Authentication

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}              │
  │◀─────────────────────────│

2. Session-Based Authentication

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)

Security Features

Account Lockout

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")

Audit Logging

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"}
}

Single-User Mode

For development/personal use, authentication can be bypassed:

export AUTOBOT_SINGLE_USER_MODE=true

In single-user mode:

  • All requests are treated as admin
  • No authentication required
  • Permission checks are bypassed

Configuration

Environment Variables

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

Config File (config/config.yaml)

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"

Testing

Run the RBAC tests:

python -m pytest tests/security/test_auth_rbac.py -v

Run the authentication security tests:

python scripts/utilities/test-authentication-security.py

Error Codes

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

Related Files

  • src/auth_middleware.py - Core authentication middleware
  • src/auth_rbac.py - RBAC permission system
  • src/security_layer.py - Security layer with permission checking
  • tests/security/test_auth_rbac.py - RBAC tests
  • scripts/utilities/test-authentication-security.py - Security tests

Migration Notes

From Guest Fallback

The guest role fallback has been removed (Issue #744). All endpoints now require explicit authentication. To migrate:

  1. Add Depends(get_current_user) to all endpoint parameters
  2. Add Issue #744: Requires authenticated user. to docstrings
  3. For admin endpoints, add Depends(check_admin_permission)

Adding New Permissions

  1. Add permission to Permission enum in src/auth_rbac.py
  2. Add to appropriate role in ROLE_PERMISSIONS
  3. Add tests in tests/security/test_auth_rbac.py
  4. Document in this file

Self-Registration & Signup

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.

Endpoint

POST /auth/signup

No authentication header is required.

Request Body

{
  "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

Response

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.

Error Responses

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

Deployment Modes (correction, Issue #15737)

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.

Onboarding Flow

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.

Related Documentation

  • 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