Audit Logging
Auth provides comprehensive audit logging for compliance and security monitoring.
Overview
What is Logged
Every authorization change is recorded:
Role creation/deletion
Permission grants/revokes
Membership additions/removals
Configuration changes
Audit Log Format
Each log entry contains:
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
Configuration
Enable Audit Logging
export AUTH_ENABLE_AUDIT_LOGGING=true
Audit logging is enabled by default.
Database Schema
CREATE TABLE auth_audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
client_key TEXT NOT NULL,
action TEXT NOT NULL,
entity_type TEXT NOT NULL,
entity_id TEXT,
details TEXT,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_client_key (client_key),
INDEX idx_timestamp (timestamp),
INDEX idx_action (action)
);
Querying Audit Logs
Python API
from auth.audit import query_audit_logs
# Get all logs for a client
logs = query_audit_logs(client_key='abc-123')
# Get logs by action
deletions = query_audit_logs(action='delete')
# Get logs by entity type
role_changes = query_audit_logs(entity_type='role')
# Get recent logs (last 24 hours)
recent = query_audit_logs(hours=24)
# Get logs in date range
logs = query_audit_logs(
start_date='2025-01-01',
end_date='2025-01-31'
)
SQL Queries
-- All permission grants in last week
SELECT * FROM auth_audit_log
WHERE action = 'grant'
AND entity_type = 'permission'
AND timestamp > datetime('now', '-7 days')
ORDER BY timestamp DESC;
-- All changes by specific client
SELECT * FROM auth_audit_log
WHERE client_key = 'abc-123'
ORDER BY timestamp DESC;
-- Role deletions
SELECT * FROM auth_audit_log
WHERE action = 'delete'
AND entity_type = 'role';
Monitoring
Real-time Monitoring
import time
from auth.audit import query_audit_logs
def monitor_audit_logs():
last_check = time.time()
while True:
# Check for new logs every minute
time.sleep(60)
logs = query_audit_logs(since_timestamp=last_check)
for log in logs:
if is_suspicious(log):
alert_security_team(log)
last_check = time.time()
Alerting
def is_suspicious(log):
"""Detect suspicious activities"""
suspicious_patterns = [
# Multiple rapid changes
{'action': 'delete', 'count_threshold': 10, 'window_minutes': 5},
# Permission grants to admin role
{'entity_type': 'permission', 'entity_id': 'admin'},
# After-hours changes
{'hour_range': (22, 6)},
]
# Implement detection logic
return check_patterns(log, suspicious_patterns)
def alert_security_team(log):
"""Send alert"""
send_email(
to='security@example.com',
subject='Suspicious Auth Activity',
body=f'Suspicious activity detected: {log}'
)
Compliance
GDPR Compliance
Audit logs support GDPR requirements:
def export_user_audit_trail(user_email):
"""Export all audit logs related to a user"""
logs = query_audit_logs(entity_id=user_email)
return {
'user': user_email,
'audit_trail': [
{
'timestamp': log['timestamp'],
'action': log['action'],
'details': log['details']
}
for log in logs
]
}
SOC 2 Compliance
Audit logs provide evidence for SOC 2 controls:
def generate_access_report(start_date, end_date):
"""Generate access control report"""
logs = query_audit_logs(
start_date=start_date,
end_date=end_date
)
return {
'period': f'{start_date} to {end_date}',
'total_changes': len(logs),
'by_action': group_by(logs, 'action'),
'by_client': group_by(logs, 'client_key'),
'critical_changes': filter_critical(logs)
}
Retention
Log Retention Policy
def cleanup_old_logs(retention_days=90):
"""Remove logs older than retention period"""
from auth.database import SessionLocal
from auth.models.sql import AuthAuditLog
from datetime import datetime, timedelta
db = SessionLocal()
try:
cutoff_date = datetime.now() - timedelta(days=retention_days)
deleted = db.query(AuthAuditLog).filter(
AuthAuditLog.timestamp < cutoff_date
).delete()
db.commit()
print(f"Deleted {deleted} old audit logs")
finally:
db.close()
Archival
def archive_old_logs(archive_days=30):
"""Archive logs to S3 before deletion"""
import boto3
import json
logs = query_audit_logs(older_than_days=archive_days)
s3 = boto3.client('s3')
archive_file = f'audit_logs_{datetime.now().isoformat()}.json'
s3.put_object(
Bucket='auth-audit-archives',
Key=archive_file,
Body=json.dumps(logs, indent=2)
)
# Now safe to delete
cleanup_old_logs(retention_days=archive_days)
Next Steps
Security - Security best practices
Production Guide - Production monitoring
Troubleshooting - Common issues