Concepts

This page explains the core concepts and architecture of the Auth authorization system.

Core Concepts

Role-Based Access Control (RBAC)

Auth implements a classic RBAC model with three main entities:

Users

Individuals or entities that need access to resources. Users are identified by unique identifiers (typically email addresses or usernames).

Roles

Named collections of permissions. Roles represent job functions or access levels (e.g., “admin”, “editor”, “viewer”).

Permissions

Atomic capabilities or actions that can be performed (e.g., “edit_content”, “delete_user”, “view_reports”).

The RBAC Model

Users (membership)> Roles (permission)> Permissions

Example:
alice@example.com > admin > manage_users
                             > edit_content
                             > view_content

bob@example.com > editor > edit_content
                            > view_content

Users are assigned to roles via memberships, and roles are granted permissions. A user inherits all permissions from their assigned roles.

Client Keys

Every application or service using Auth must have a unique client key (UUID4). This provides:

Isolation

Different clients have separate authorization scopes. One client’s roles and permissions don’t affect another’s.

Multi-tenancy

Multiple applications can share the same Auth instance without interfering with each other.

Security

Client keys act as API authentication tokens when using the REST interface.

Example:

import uuid

# Each app/service gets its own client key
webapp_key = str(uuid.uuid4())
api_service_key = str(uuid.uuid4())
workflow_engine_key = str(uuid.uuid4())

# They operate in isolated scopes
webapp_auth = Authorization(webapp_key)
api_auth = Authorization(api_service_key)

Architecture

Layered Architecture

Auth follows a clean, layered architecture:

 
          API Layer (Flask)                  
  - REST endpoints                           
  - Request validation                       
  - Response formatting                      
,
                  
 ?
       Service Layer (Business Logic)        
  - Authorization rules                      
  - Permission checking                      
  - Audit logging                            
,
                  
 ?
      Data Access Layer (DAL)                
  - SQLAlchemy ORM                           
  - Database abstraction                     
  - Encryption/Decryption                    
,
                  
 ?
         Database (SQLite/PostgreSQL)        
  - User data                                
  - Role & Permission mappings               
  - Audit logs                               


Components

Authorization Service (``AuthorizationService``)

Core business logic for RBAC operations. Handles permission checks, role assignments, and membership management.

Database Layer (``database.py``)

SQLAlchemy-based ORM models and database connections. Supports both SQLite and PostgreSQL.

REST API (``routes.py``)

Flask routes providing HTTP endpoints for all authorization operations.

Client Libraries
  • Authorization - Direct Python library interface

  • EnhancedAuthClient - REST API client for remote access

Security Components
  • JWT Authentication

  • Field-level encryption

  • Input validation and sanitization

  • Audit logging

Data Model

Database Tables

auth_group

Stores roles with their descriptions.

CREATE TABLE auth_group (
    id INTEGER PRIMARY KEY,
    creator TEXT NOT NULL,
    role TEXT NOT NULL,
    description TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(creator, role)
);
auth_permission

Maps permissions to roles.

CREATE TABLE auth_permission (
    id INTEGER PRIMARY KEY,
    creator TEXT NOT NULL,
    group_id INTEGER NOT NULL,
    name TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (group_id) REFERENCES auth_group(id) ON DELETE CASCADE,
    UNIQUE(creator, group_id, name)
);
auth_membership

Maps users to roles.

CREATE TABLE auth_membership (
    id INTEGER PRIMARY KEY,
    creator TEXT NOT NULL,
    group_id INTEGER NOT NULL,
    user TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (group_id) REFERENCES auth_group(id) ON DELETE CASCADE,
    UNIQUE(creator, group_id, user)
);
auth_audit_log

Records all authorization changes for compliance and auditing.

CREATE TABLE auth_audit_log (
    id INTEGER PRIMARY KEY,
    client_key TEXT NOT NULL,
    action TEXT NOT NULL,
    entity_type TEXT NOT NULL,
    entity_id TEXT,
    details TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Permission Checking

How Permission Checks Work

When you call user_has_permission(user, permission):

  1. Find all roles the user belongs to (via auth_membership)

  2. For each role, query all permissions (via auth_permission)

  3. Check if the requested permission exists in the result set

  4. Return True if found, False otherwise

Example:

# Setup
auth.add_role('editor')
auth.add_permission('editor', 'edit_content')
auth.add_membership('bob@example.com', 'editor')

# Check
can_edit = auth.user_has_permission('bob@example.com', 'edit_content')
# Returns: True

# Behind the scenes:
# 1. Query: bob@example.com ? ['editor']
# 2. Query: 'editor' ? ['edit_content', ...]
# 3. Check: 'edit_content' in ['edit_content', ...] ? True

Query Methods

Auth provides several ways to query authorization data:

User-centric queries:

  • get_user_roles(user) - Get all roles for a user

  • get_user_permissions(user) - Get all permissions for a user

  • user_has_permission(user, permission) - Check if user has permission

Role-centric queries:

  • get_role_members(role) - Get all users in a role

  • get_permissions(role) - Get all permissions for a role

  • has_permission(role, permission) - Check if role has permission

Permission-centric queries:

  • which_users_can(permission) - Find all users with a permission

  • which_roles_can(permission) - Find all roles with a permission

Audit Trail

Every authorization change is logged to the audit table:

Logged Actions:

  • Role creation/deletion

  • Permission grant/revoke

  • Membership addition/removal

Audit Record Fields:

  • client_key - Which client made the change

  • action - Type of operation (create, delete, grant, revoke)

  • entity_type - What was modified (role, permission, membership)

  • entity_id - Identifier of the entity

  • details - Additional context (JSON)

  • timestamp - When the change occurred

Example audit log query:

from auth.audit import query_audit_logs

# Get all changes by a client
logs = query_audit_logs(client_key='abc-123')

# Get all permission grants
logs = query_audit_logs(action='grant', entity_type='permission')

Security Model

Defense in Depth

Auth employs multiple security layers:

  1. Client Authentication - UUID4 keys for API access

  2. JWT Tokens - Optional token-based authentication

  3. Input Validation - Sanitization of all user inputs

  4. SQL Injection Protection - ORM-based queries

  5. Encryption - Optional field-level encryption

  6. Audit Logging - Complete change history

Encryption

Auth supports deterministic field-level encryption:

What’s Encrypted:

  • User identifiers in memberships

  • Permission names

  • Optional role descriptions

Why Deterministic:

  • Allows querying encrypted data

  • Same plaintext = same ciphertext

  • Enables efficient database operations

Trade-offs:

  • Pattern analysis possible (acceptable for usernames/permissions)

  • Full database functionality maintained

  • No performance impact on queries

See Encryption for detailed information.

Best Practices

Role Design

Keep roles granular:

# Good: Specific roles
auth.add_role('content_editor')
auth.add_role('content_reviewer')
auth.add_role('content_publisher')

# Avoid: Too broad
auth.add_role('content_manager')  # Too many permissions

Use hierarchical naming:

auth.add_role('admin.system')
auth.add_role('admin.department')
auth.add_role('user.premium')
auth.add_role('user.basic')

Permission Design

Use verb-noun naming:

# Good: Clear action + resource
auth.add_permission('role', 'create_user')
auth.add_permission('role', 'delete_post')
auth.add_permission('role', 'view_analytics')

# Avoid: Ambiguous
auth.add_permission('role', 'users')
auth.add_permission('role', 'manage')

Keep permissions atomic:

# Good: Specific permissions
'read_file'
'write_file'
'delete_file'

# Avoid: Combined permissions
'file_management'  # Too broad

Client Key Management

Generate unique keys per service:

import uuid

# Different key for each service
web_app_key = str(uuid.uuid4())
mobile_app_key = str(uuid.uuid4())
admin_panel_key = str(uuid.uuid4())

Store keys securely:

  • Use environment variables

  • Never commit to version control

  • Rotate keys periodically

  • Use secrets management systems in production

Next Steps