Roles and Privileges Assessment and Recommendation¶
Date: 2025-10-17 Author: Claude Code Status: Recommendation - Pending Implementation
Executive Summary¶
This document assesses the current roles and privileges implementation in S5 Slidefactory for both API Keys and User Accounts, identifies issues with the current dual approach, and recommends a unified scope-based authorization system.
Recommendation: Adopt scope-based authorization (currently used for API keys) for ALL access control - both API keys and user sessions.
Current State Analysis¶
API Keys Implementation¶
Location: app/api/models.py, app/api/auth.py, cli/commands/api_key.py
Architecture: - Fully database-backed with api_keys table - Scope-based permissions using JSON array - Fine-grained, action-level authorization - Full lifecycle management via CLI
Features:
# API Key Model
class ApiKey(Base):
scopes = Column(JSON) # e.g., ["templates:read", "presentations:generate", "*"]
is_active = Column(Boolean)
expires_at = Column(DateTime)
usage_count = Column(Integer)
rate_limit_per_hour = Column(Integer)
Authorization Pattern:
# Scope checking
def has_scope(self, scope: str) -> bool:
scopes_list = self.scopes if self.scopes else []
return scope in scopes_list or "*" in scopes_list
# Usage
if not api_key.has_scope("templates:write"):
raise HTTPException(403, "Missing scope")
Predefined Scopes (app/api/auth.py:209-232): - * - Full access - templates:read, templates:write, templates:delete - presentations:generate, presentations:read - workflows:read, workflows:execute - results:read, results:write, results:delete
Management Tools: - CLI: python -m cli api-key create/list/test/revoke - Security: SHA256 hashing, key prefix for identification - Tracking: Last used timestamp, usage count
User Accounts Implementation¶
Location: app/auth/users/models.py, app/auth/auth.py, app/config.py
Architecture: DUAL STORAGE (Problem!) 1. Database: app_users table (app/auth/users/models.py:8-18) 2. Config File: Hardcoded ALLOWED_USERS dict (app/config.py:38-92)
Features:
# User Model (Database - Currently UNUSED)
class User(Base):
__tablename__ = "app_users"
email = Column(String(255), unique=True)
attributes = Column(JSON) # JSON blob for permissions
active = Column(Boolean)
# Config File (Currently ACTIVE)
ALLOWED_USERS = {
"user@example.com": {
"id": -100,
"attributes": {
"checks": ["view", "admin"],
"prompts": ["view", "admin"],
"contexts": ["view", "admin"],
"users": ["view", "admin"],
"files": ["view", "admin"],
"workflows": ["esg2", "poc_ideation"]
}
}
}
Authorization Pattern:
# Workflow-level access check
user_workflows = user_config.get('attributes', {}).get('workflows', [])
if workflow_folder not in user_workflows:
return None
# Resource-level permissions (app/filemanager/workflow_files/permissions.py)
def check_template_permission(user, workflow_folder, action):
user_permissions = get_user_workflow_permissions(user, workflow_folder)
template_perms = user_permissions.get('templates', [])
return action in template_perms
Session-Based Auth: - FastAPI Sessions with in-memory backend - Cookie-based authentication - SessionData contains user attributes - verifier dependency for protected endpoints
Permission Checking Patterns¶
Current State - Inconsistent:
- API Endpoints with API key support (app/api/presentations.py):
- Uses
verify_api_access(allows both API key and session) - API keys check scopes
-
Sessions check workflow attributes
-
Web UI Endpoints (app/filemanager/workflow_files/router.py:46):
- Uses
verifier(session only) - Custom permission checking logic
-
Manual mapping in
permissions.py -
Hybrid Permission Checking (app/filemanager/workflow_files/permissions.py:13-48):
def check_template_permission(user, workflow_folder, action): # Check if API key if isinstance(user, ApiKeyAuth): scope_map = {'view': 'templates:read', 'upload': 'templates:write'} return user.has_scope(scope_map.get(action)) # Check if session user user_permissions = get_user_workflow_permissions(user, workflow_folder) return action in user_permissions.get('templates', [])
Issues with Current Approach¶
1. Conflicting User Storage¶
- Problem: Users defined in both database table AND config file
- Impact: Database table is unused, all auth happens via config
- Risk: Database schema exists but provides no value
2. Inconsistent Permission Models¶
- API Keys: Scope-based (
templates:read) - Users: Attribute-based (
attributes.workflows,attributes.checks) - Impact: Duplicate permission checking logic, harder to maintain
3. No Unified RBAC¶
- Problem: "admin" is per-resource, not a role
- Example: User can be admin for "checks" but not for "prompts"
- Impact: Cannot easily grant "admin everywhere" or "editor everywhere"
4. Limited Permission Granularity¶
- Problem: Users get workflow-level access, actions are binary (view/admin)
- Missing: Cannot grant "read templates but not delete" for workflows
- Impact: Either full admin or just view, no middle ground
5. Hardcoded Permission Logic¶
- Problem:
permissions.pyhas manual action→scope mapping - Example:
{'view': 'templates:read', 'upload': 'templates:write'} - Impact: Adding new resources requires code changes in multiple places
6. Config File Scalability¶
- Problem: Users hardcoded in config.py
- Impact: Requires code deployment to add/remove users
- Risk: User credentials in version control
Requirements Analysis¶
Based on user input:
- Access Type: Equal mix of API and web UI access
- Authorization Model: Scope-based preferred over roles (simpler)
- Storage: Database only (no config file)
- Granularity:
- ✅ Workflow-level (control which workflows user can access)
- ✅ Action-level (read, write, execute, delete)
- ✅ Resource-level (templates, outputs, contexts)
- ❌ Object-level (not required)
Recommendation: Unified Scope-Based Authorization¶
Overview¶
Adopt the scope-based approach (currently used for API keys) for ALL access control - both API keys and user sessions.
Why Scope-Based?¶
- Already 50% Implemented: API key system works well, extend it
- Unified Model: Single permission system for API keys and users
- Simplicity: Easier to understand than role hierarchies
- Flexibility: Fine-grained permissions without complex roles
- Database-First: Aligns with requirement for database-only storage
- Scalable: Adding new resources/actions is straightforward
- Industry Standard: OAuth 2.0, Google APIs, AWS IAM all use scopes
Proposed Scope Hierarchy¶
Format: <resource>:<action> or <resource>:<workflow_folder>:<action>
# Global Scopes (all workflows)
* # Full access (superadmin)
# Resource-Level Scopes (all workflows)
templates:read # Read all templates
templates:write # Create/update all templates
templates:delete # Delete any template
presentations:read # View all presentations
presentations:generate # Generate presentations
workflows:read # View all workflows
workflows:execute # Execute any workflow
results:read # View all results
results:write # Create/update results
results:delete # Delete results
contexts:read # View all contexts
contexts:write # Manage contexts
users:read # View users
users:write # Manage users (admin function)
# Workflow-Specific Scopes (granular control)
workflows:esg2:read # View specific workflow
workflows:esg2:execute # Execute specific workflow
workflows:presales_poc:execute # Execute specific workflow
templates:esg2:read # Read templates for specific workflow
templates:esg2:write # Manage templates for specific workflow
templates:presales_poc:write # Manage templates for specific workflow
# Scope Resolution (most specific wins)
1. Check workflow-specific: templates:esg2:write
2. Fallback to global: templates:write
3. Fallback to wildcard: *
Example Scope Assignments¶
Superadmin User:
ESG Team Member (workflow-specific):
[
"workflows:esg2:read",
"workflows:esg2:execute",
"templates:esg2:read",
"presentations:read",
"results:read"
]
ESG Administrator (workflow admin):
[
"workflows:esg2:read",
"workflows:esg2:execute",
"workflows:esg3:read",
"workflows:esg3:execute",
"templates:esg2:write",
"templates:esg3:write",
"contexts:write",
"presentations:generate",
"presentations:read",
"results:write"
]
API Integration (automation):
Platform Administrator:
Implementation Plan¶
Phase 1: Database Schema Changes¶
Goal: Add scopes to users, prepare for config migration
Tasks: 1. Add scopes JSONB column to app_users table 2. Create Alembic migration 3. Add indexes for performance 4. Create conversion utility: attributes → scopes
Migration Script:
-- Add scopes column
ALTER TABLE app_users ADD COLUMN scopes JSONB NOT NULL DEFAULT '[]'::jsonb;
-- Add index for scope queries
CREATE INDEX idx_app_users_scopes ON app_users USING gin(scopes);
-- Add index for active users
CREATE INDEX idx_app_users_active ON app_users(active) WHERE active = true;
Conversion Logic (attributes → scopes):
def convert_attributes_to_scopes(attributes: dict) -> List[str]:
scopes = []
# Convert workflow access
for workflow in attributes.get('workflows', []):
scopes.append(f"workflows:{workflow}:read")
scopes.append(f"workflows:{workflow}:execute")
# Convert resource permissions
resource_map = {
'checks': 'checks',
'prompts': 'prompts',
'contexts': 'contexts',
'users': 'users',
'files': 'templates'
}
for attr_key, scope_resource in resource_map.items():
perms = attributes.get(attr_key, [])
if 'view' in perms:
scopes.append(f"{scope_resource}:read")
if 'admin' in perms:
scopes.append(f"{scope_resource}:write")
scopes.append(f"{scope_resource}:delete")
return scopes
Files to Modify: - Create: alembic/versions/YYYYMMDD_add_user_scopes.py - Create: scripts/migrate_users_to_scopes.py
Phase 2: Auth Layer Unification¶
Goal: Create unified authorization interface
Tasks: 1. Create BaseAuth mixin with scope checking 2. Extend both ApiKeyAuth and SessionData from BaseAuth 3. Update SessionData to load scopes from database 4. Ensure verify_api_access works seamlessly for both
Architecture:
# Base authorization interface
class BaseAuth:
scopes: List[str] = []
def has_scope(self, scope: str) -> bool:
"""Check if has specific scope or wildcard."""
return scope in self.scopes or "*" in self.scopes
def has_workflow_access(self, workflow: str, action: str = "read") -> bool:
"""Check workflow-specific access with fallback to global."""
# 1. Try workflow-specific scope
if self.has_scope(f"workflows:{workflow}:{action}"):
return True
# 2. Fall back to global workflow scope
if self.has_scope(f"workflows:{action}"):
return True
# 3. Fall back to wildcard
return self.has_scope("*")
def has_resource_access(self, resource: str, action: str, workflow: str = None) -> bool:
"""Check resource access with optional workflow scoping."""
# 1. Try workflow-specific resource scope
if workflow and self.has_scope(f"{resource}:{workflow}:{action}"):
return True
# 2. Fall back to global resource scope
if self.has_scope(f"{resource}:{action}"):
return True
# 3. Fall back to wildcard
return self.has_scope("*")
# API Key Auth (already has scopes from database)
class ApiKeyAuth(BaseAuth):
def __init__(self, api_key: ApiKey, user_id: str, user_email: str = None):
self.api_key = api_key
self.scopes = api_key.scopes or []
# ... rest of init
# Session Data (add scopes from user)
class SessionData(BaseModel, BaseAuth):
id: Optional[int] = None
email: Optional[str] = None
scopes: List[str] = [] # NEW: Load from database
# ... rest of fields
User Login Update:
# In auth router, when creating session
async def login(username: str, password: str):
# Verify credentials
user = db.query(User).filter(User.email == username).first()
if not user or not verify_password(password, user.password):
raise HTTPException(401, "Invalid credentials")
# Create session with scopes
session_data = SessionData(
id=user.id,
email=user.email,
name=user.name,
scopes=user.scopes or [], # Load from database
attributes=user.attributes or {}
)
# Store session
await backend.create(session_id, session_data)
Files to Modify: - Modify: app/api/auth.py (add BaseAuth) - Modify: app/auth/auth.py (update SessionData) - Modify: app/auth/router.py (load scopes on login)
Phase 3: Application Layer Updates¶
Goal: Replace all permission checks with scope validation
Tasks: 1. Create scope checking decorators/dependencies 2. Update all check_*_permission() functions 3. Replace Depends(verifier) with Depends(verify_api_access) everywhere 4. Remove custom permission mapping logic
Scope Checking Helpers:
# In app/api/auth.py
def require_scope(*required_scopes: str):
"""Dependency that requires one or more scopes."""
async def _verify(
user: Union[SessionData, ApiKeyAuth] = Depends(verify_api_access)
) -> Union[SessionData, ApiKeyAuth]:
# Check if user has ANY of the required scopes
for scope in required_scopes:
if user.has_scope(scope):
return user
raise HTTPException(
status_code=403,
detail=f"Missing required scope. Need one of: {', '.join(required_scopes)}"
)
return _verify
def require_workflow_access(workflow_param: str = "workflow_folder", action: str = "read"):
"""Dependency that checks workflow-specific access."""
async def _verify(
user: Union[SessionData, ApiKeyAuth] = Depends(verify_api_access),
workflow: str = Path(..., alias=workflow_param)
) -> Union[SessionData, ApiKeyAuth]:
if not user.has_workflow_access(workflow, action):
raise HTTPException(
status_code=403,
detail=f"Missing workflow access: {workflow}:{action}"
)
return user
return _verify
Updated Endpoint Examples:
# API endpoint with scope check
@router.post("/api/presentations/generate")
async def generate_presentation(
request: PresentationGenerateRequest,
user: Union[SessionData, ApiKeyAuth] = Depends(require_scope("presentations:generate"))
):
# User guaranteed to have presentations:generate scope
pass
# Workflow-specific template endpoint
@router.get("/api/workflow-files/workflows/{workflow_folder}/templates")
async def list_workflow_templates(
workflow_folder: str,
user: Union[SessionData, ApiKeyAuth] = Depends(require_workflow_access(action="read"))
):
# User guaranteed to have workflows:{workflow_folder}:read scope
# Additional check for templates
if not user.has_resource_access("templates", "read", workflow_folder):
raise HTTPException(403, "Cannot read templates for this workflow")
templates = await list_templates(workflow_folder, user)
return templates
# Flexible scope checking
@router.post("/api/workflow-files/workflows/{workflow_folder}/templates")
async def upload_template(
workflow_folder: str,
file: UploadFile,
user: Union[SessionData, ApiKeyAuth] = Depends(verify_api_access)
):
# Check workflow-specific OR global template write scope
if not user.has_resource_access("templates", "write", workflow_folder):
raise HTTPException(
403,
f"Missing scope: templates:{workflow_folder}:write or templates:write"
)
template = await upload_template(workflow_folder, file, user)
return template
Simplified Permissions Module:
# app/filemanager/workflow_files/permissions.py - SIMPLIFIED
def check_template_permission(user: Union[SessionData, ApiKeyAuth],
workflow_folder: str,
action: str) -> bool:
"""Check if user can perform template action."""
action_map = {'view': 'read', 'upload': 'write', 'delete': 'delete'}
scope_action = action_map.get(action, action)
return user.has_resource_access("templates", scope_action, workflow_folder)
def check_output_permission(user: Union[SessionData, ApiKeyAuth],
workflow_folder: str,
action: str) -> bool:
"""Check if user can perform output action."""
action_map = {'view': 'read', 'download': 'read', 'delete': 'delete'}
scope_action = action_map.get(action, action)
return user.has_resource_access("outputs", scope_action, workflow_folder)
# Remove get_user_workflow_permissions - no longer needed
# Remove get_user_accessible_workflows - replaced by scope checking
Files to Modify: - Modify: app/api/auth.py (add helpers) - Modify: app/filemanager/workflow_files/permissions.py (simplify) - Modify: app/filemanager/workflow_files/router.py (use new dependencies) - Modify: app/api/presentations.py (already uses verify_api_access) - Modify: app/api/templates.py (add scope checks) - Modify: app/api/results.py (add scope checks) - Modify: All other routers with Depends(verifier)
Phase 4: User Management System¶
Goal: Create tools for managing users and scopes
Tasks: 1. Create CLI commands for user management 2. Create admin web UI for user management 3. Create scope presets/templates for common roles 4. Migration utility to import users from config
CLI Commands:
# User management
python -m cli user create <email> --name "John Doe" --password "secret"
python -m cli user list
python -m cli user update <email> --scopes "templates:read,presentations:generate"
python -m cli user add-scope <email> "workflows:esg2:execute"
python -m cli user remove-scope <email> "workflows:esg2:execute"
python -m cli user deactivate <email>
python -m cli user activate <email>
# Scope presets
python -m cli user create <email> --preset admin
python -m cli user create <email> --preset workflow-user --workflow esg2
python -m cli user create <email> --preset api-only
# Migration
python -m cli migrate-users-from-config
CLI Implementation:
# cli/commands/user.py
SCOPE_PRESETS = {
'admin': ['*'],
'workflow-admin': lambda workflow: [
f'workflows:{workflow}:read',
f'workflows:{workflow}:execute',
f'templates:{workflow}:write',
f'presentations:generate',
f'results:write'
],
'workflow-user': lambda workflow: [
f'workflows:{workflow}:read',
f'workflows:{workflow}:execute',
f'templates:{workflow}:read',
f'presentations:read',
f'results:read'
],
'api-only': [
'presentations:generate',
'presentations:read',
'results:read'
]
}
def create_user(email: str, name: str, password: str,
scopes: List[str] = None, preset: str = None,
workflow: str = None):
"""Create a new user."""
from app.auth.users.models import User
from app.util.database import SessionLocal
import bcrypt
db = SessionLocal()
try:
# Check if user exists
existing = db.query(User).filter(User.email == email).first()
if existing:
print_error(f"User {email} already exists")
return
# Determine scopes
if preset:
if preset in ['workflow-admin', 'workflow-user']:
if not workflow:
print_error("--workflow required for this preset")
return
user_scopes = SCOPE_PRESETS[preset](workflow)
else:
user_scopes = SCOPE_PRESETS[preset]
else:
user_scopes = scopes or []
# Hash password
password_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
# Create user
user = User(
email=email,
name=name,
password=password_hash,
scopes=user_scopes,
active=True
)
db.add(user)
db.commit()
print_success(f"User {email} created successfully!")
print_field("Scopes", ', '.join(user_scopes))
finally:
db.close()
Admin Web UI (future): - User list with search/filter - User detail page with scope management - Scope builder UI with checkboxes - Workflow assignment interface - Activity logs
Files to Create: - Create: cli/commands/user.py - Create: scripts/migrate_users_from_config.py - Create: app/auth/users/router.py (admin API) - Create: app/auth/users/templates/ (admin UI)
Phase 5: Configuration Cleanup¶
Goal: Remove hardcoded users from config
Tasks: 1. Run migration script to import users from config 2. Update config.py to remove ALLOWED_USERS 3. Add environment variable for emergency admin 4. Update documentation
Emergency Admin Pattern:
# In config.py - keep ONE emergency admin for bootstrapping
EMERGENCY_ADMIN_EMAIL = os.environ.get("EMERGENCY_ADMIN_EMAIL")
EMERGENCY_ADMIN_PASSWORD = os.environ.get("EMERGENCY_ADMIN_PASSWORD")
# In auth router
async def login(username: str, password: str):
# Check emergency admin first (if configured)
if (settings.EMERGENCY_ADMIN_EMAIL and
username == settings.EMERGENCY_ADMIN_EMAIL and
password == settings.EMERGENCY_ADMIN_PASSWORD):
return SessionData(
email=username,
name="Emergency Admin",
scopes=["*"] # Full access
)
# Otherwise check database
user = db.query(User).filter(User.email == username).first()
# ... normal auth flow
Files to Modify: - Modify: app/config.py (remove ALLOWED_USERS, add emergency admin) - Modify: app/auth/router.py (add emergency admin check) - Update: README.md (document new user management) - Update: CLAUDE.md (update auth section)
Phase 6: Testing & Validation¶
Goal: Ensure all functionality still works
Tasks: 1. Unit tests for scope checking logic 2. Integration tests for auth flows 3. Manual testing checklist 4. Permission audit
Test Cases:
# tests/unit/auth/test_scopes.py
def test_scope_checking():
"""Test basic scope checking."""
auth = BaseAuth()
auth.scopes = ["templates:read", "presentations:generate"]
assert auth.has_scope("templates:read") == True
assert auth.has_scope("templates:write") == False
assert auth.has_scope("presentations:generate") == True
def test_wildcard_scope():
"""Test wildcard scope grants everything."""
auth = BaseAuth()
auth.scopes = ["*"]
assert auth.has_scope("anything") == True
assert auth.has_scope("workflows:esg2:execute") == True
def test_workflow_specific_scope():
"""Test workflow-specific access."""
auth = BaseAuth()
auth.scopes = ["workflows:esg2:read", "workflows:esg2:execute"]
assert auth.has_workflow_access("esg2", "read") == True
assert auth.has_workflow_access("esg2", "execute") == True
assert auth.has_workflow_access("esg3", "read") == False
def test_resource_access_with_workflow():
"""Test resource access with workflow scoping."""
auth = BaseAuth()
auth.scopes = ["templates:esg2:write", "templates:read"]
# Has workflow-specific write
assert auth.has_resource_access("templates", "write", "esg2") == True
# Does not have workflow-specific write for other workflow
assert auth.has_resource_access("templates", "write", "esg3") == False
# Has global read
assert auth.has_resource_access("templates", "read", "esg3") == True
def test_scope_migration():
"""Test converting old attributes to scopes."""
attributes = {
"workflows": ["esg2", "presales_poc"],
"templates": ["view", "admin"],
"contexts": ["view"]
}
scopes = convert_attributes_to_scopes(attributes)
assert "workflows:esg2:read" in scopes
assert "workflows:esg2:execute" in scopes
assert "workflows:presales_poc:read" in scopes
assert "templates:read" in scopes
assert "templates:write" in scopes
assert "contexts:read" in scopes
assert "contexts:write" not in scopes
Manual Testing Checklist: - [ ] API key authentication still works - [ ] Session authentication still works - [ ] Template upload/download permissions enforced - [ ] Workflow execution permissions enforced - [ ] Presentation generation permissions enforced - [ ] Admin users can manage all resources - [ ] Limited users cannot access restricted resources - [ ] Workflow-specific permissions work correctly - [ ] CLI user management commands work - [ ] Migration script converts all existing users correctly
Files to Create: - Create: tests/unit/auth/test_scopes.py - Create: tests/integration/test_auth_flows.py - Create: tests/manual_test_checklist.md
Migration Guide¶
For Existing Users¶
Before Migration:
# User in config.py
"user@example.com": {
"id": -100,
"attributes": {
"workflows": ["esg2"],
"templates": ["view", "admin"]
}
}
After Migration:
# User in database
{
"email": "user@example.com",
"scopes": [
"workflows:esg2:read",
"workflows:esg2:execute",
"templates:esg2:read",
"templates:esg2:write",
"templates:esg2:delete"
]
}
Deployment Steps¶
- Deploy Code (with backward compatibility)
- Auth layer supports both old and new formats
- New endpoints use scopes
-
Old endpoints still work with config
-
Run Migration
-
Validate
- Test login with migrated users
- Verify permissions work correctly
-
Check API key access still works
-
Switch Over
- Set EMERGENCY_ADMIN_EMAIL in environment
- Remove ALLOWED_USERS from config.py
-
Redeploy
-
Monitor
- Watch for authentication errors
- Verify all users can access their workflows
- Check API key usage
Rollback Plan¶
If issues arise:
- Immediate: Restore ALLOWED_USERS in config.py
- Redeploy: Old code still works with config
- Fix Issues: Debug scope assignment problems
- Retry: Fix and re-run migration
Benefits Summary¶
For Users¶
- Single, consistent permission model
- Easy to understand: "I have scope X, I can do X"
- Flexible workflow access control
- Self-service via CLI (for admins)
For Developers¶
- Unified auth checking:
user.has_scope("templates:write") - Less code: Remove custom permission logic
- Easier to extend: Add new scopes without changing code
- Better security: Database-backed, not config file
For Administrators¶
- Easy user management via CLI
- Audit trail in database
- Fine-grained control without complexity
- Can grant/revoke individual permissions
For System¶
- Database-backed: Scalable, no code deploys for user changes
- Performance: Indexed JSONB queries are fast
- Standard pattern: Industry-standard approach
- API parity: Same auth for API and web UI
Open Questions / Future Enhancements¶
1. Role Templates¶
Should we add predefined roles in addition to scopes?
Pros: Easier user management ("make them an editor") Cons: Adds complexity, scopes are already flexible
Recommendation: Start with scopes only, add roles if needed later
2. Scope Hierarchy¶
Should templates:write automatically include templates:read?
Current: No hierarchy, must grant both Alternative: Write implies read
Recommendation: Keep flat for now, explicit is better than implicit
3. Audit Logging¶
Should we log all permission checks?
Pros: Security audit trail, debugging Cons: Database bloat, performance impact
Recommendation: Log failed permission checks only
4. Time-Based Scopes¶
Should scopes have expiration times?
Example: Grant temporary access for contractors
Recommendation: Not in MVP, could add to User model later
5. Scope Groups¶
Should we support scope groups/bundles?
Example: @esg-team = all ESG workflow scopes
Recommendation: Not needed, scopes are already flexible
Conclusion¶
The unified scope-based authorization system provides:
- Consistency: Same auth model for API keys and users
- Simplicity: Easy to understand and manage
- Flexibility: Fine-grained permissions without complexity
- Scalability: Database-first, no code deploys for user changes
- Security: Industry-standard pattern with full audit trail
Next Steps: 1. Review and approve this plan 2. Implement Phase 1 (database schema) 3. Implement Phase 2 (auth layer) 4. Implement Phase 3 (application layer) 5. Implement Phase 4 (user management) 6. Deploy and migrate users
Timeline Estimate: - Phase 1: 1 day (schema + migration) - Phase 2: 2 days (auth layer refactoring) - Phase 3: 3 days (update all endpoints) - Phase 4: 2 days (CLI + admin tools) - Phase 5: 1 day (cleanup) - Phase 6: 2 days (testing) - Total: ~2 weeks with thorough testing
Risk Level: Medium - Large refactoring touching auth system - Mitigated by: Backward compatibility, thorough testing, rollback plan