Python/Django Integration Guide
Complete guide for integrating Python Django applications with CAS SSO
Enhanced Architecture: Our CAS system now features modular Admin/User/Public separation with Livewire components, PostgreSQL multi-schema design, and enhanced JWT authentication.
Setup time: 15 minutes
Difficulty: Medium
Python 3.8+ / Django 3.2+
1. Installation
pip install requests pyjwt cryptography psycopg2-binary django
Project Structure
your-django-project/
├── cas_client/
│ ├── __init__.py
│ ├── client.py # CAS client service
│ ├── middleware.py # CAS authentication middleware
│ └── views.py # CAS authentication views
├── settings/
│ └── cas_config.py # CAS configuration
└── requirements.txt # Dependencies
2. Configuration
Django Settings
Add to your settings.py:
# CAS Configuration - Enhanced Architecture
CAS_SERVER_URL = os.environ.get('CAS_SERVER_URL', 'https://your-cas-server.com')
CAS_CLIENT_ID = os.environ.get('CAS_CLIENT_ID')
CAS_CLIENT_SECRET = os.environ.get('CAS_CLIENT_SECRET')
CAS_SIGNATURE_SECRET = os.environ.get('CAS_SIGNATURE_SECRET')
CAS_CALLBACK_URL = os.environ.get('CAS_CALLBACK_URL')
# New Route Endpoints - Updated Architecture
CAS_ENDPOINTS = {
'sso_token': '/api/sso/token', # Enhanced client credentials
'sso_validate': '/api/sso/validate', # Token validation
'callback': '/auth/sso/callback', # SSO callback
'user_dashboard': '/user/dashboard', # User dashboard
'logout': '/auth/logout' # Logout endpoint
}
# Enhanced Security Settings
CAS_SECURITY = {
'token_expiry': 3600, # 1 hour
'hmac_algorithm': 'sha256', # HMAC-SHA256
'ip_whitelist_enabled': True, # IP whitelist support
'audit_logging': True, # Comprehensive logging
'rate_limiting': True # Rate limiting
}
# PostgreSQL Schema Configuration
CAS_DATABASE = {
'schemas': {
'admin': 'cas_admin',
'user': 'cas_user',
'public': 'cas_public',
'audit': 'cas_audit'
}
}
# Add to MIDDLEWARE
MIDDLEWARE = [
# ... existing middleware
'cas_client.middleware.CasAuthenticationMiddleware',
]
# Add to INSTALLED_APPS
INSTALLED_APPS = [
# ... existing apps
'cas_client',
]
Environment Variables
# CAS Configuration - Enhanced Architecture
CAS_SERVER_URL=https://your-cas-server.com
CAS_CLIENT_ID=your-client-id
CAS_CLIENT_SECRET=your-client-secret
CAS_SIGNATURE_SECRET=your-signature-secret
CAS_CALLBACK_URL=http://your-app.com/auth/cas/callback
# Database Configuration
CAS_DB_HOST=127.0.0.1
CAS_DB_PORT=5432
CAS_DB_NAME=cas_system
CAS_DB_USER=cas_user
CAS_DB_PASSWORD=secure_password
3. CAS Client Service
Create cas_client/client.py:
import requests
import hashlib
import hmac
import json
import time
from urllib.parse import urlencode
from django.conf import settings
import jwt
class CasClient:
def __init__(self):
self.base_url = settings.CAS_SERVER_URL
self.client_id = settings.CAS_CLIENT_ID
self.client_secret = settings.CAS_CLIENT_SECRET
self.signature_secret = settings.CAS_SIGNATURE_SECRET
def generate_signature(self, data):
"""Generate HMAC-SHA256 signature for enhanced security"""
sorted_keys = sorted(data.keys())
query_string = '&'.join([f"{key}={data[key]}" for key in sorted_keys])
return hmac.new(
self.signature_secret.encode('utf-8'),
query_string.encode('utf-8'),
hashlib.sha256
).hexdigest()
def request_token(self, username):
"""Enhanced token request with HMAC signature"""
timestamp = int(time.time())
request_data = {
'client_id': self.client_id,
'client_secret': self.client_secret,
'username': username,
'timestamp': timestamp
}
# Generate HMAC-SHA256 signature
signature = self.generate_signature(request_data)
request_data['signature'] = signature
try:
response = requests.post(
f"{self.base_url}{settings.CAS_ENDPOINTS['sso_token']}",
json=request_data,
headers={
'Content-Type': 'application/json',
'X-CAS-Client-ID': self.client_id
},
timeout=30
)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
raise Exception(f"Failed to obtain CAS token: {str(e)}")
def validate_token(self, token):
"""Enhanced token validation"""
request_data = {
'token': token,
'timestamp': int(time.time())
}
signature = self.generate_signature(request_data)
request_data['signature'] = signature
try:
response = requests.post(
f"{self.base_url}{settings.CAS_ENDPOINTS['sso_validate']}",
json=request_data,
headers={
'Content-Type': 'application/json',
'X-CAS-Client-ID': self.client_id
},
timeout=30
)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
return {'valid': False, 'error': f'Token validation failed: {str(e)}'}
def get_login_url(self, return_url=None):
"""Generate SSO login URL"""
params = {
'client_id': self.client_id,
'redirect_uri': settings.CAS_CALLBACK_URL,
}
if return_url:
params['return_url'] = return_url
return f"{self.base_url}/auth/sso?{urlencode(params)}"
def process_callback(self, callback_data):
"""Process SSO callback with enhanced validation"""
try:
token = callback_data.get('token')
if not token:
return {'success': False, 'error': 'Missing token'}
validation_result = self.validate_token(token)
if validation_result.get('valid'):
return {
'success': True,
'user': validation_result.get('user'),
'token': token,
'return_url': callback_data.get('return_url', '/')
}
else:
return {
'success': False,
'error': validation_result.get('error', 'Invalid token')
}
except Exception as e:
return {'success': False, 'error': str(e)}
# Global instance
cas_client = CasClient()
4. Authentication Middleware
Create cas_client/middleware.py:
from django.shortcuts import redirect
from django.urls import reverse
from django.http import JsonResponse
from django.contrib.auth.models import AnonymousUser
from .client import cas_client
class CasAuthenticationMiddleware:
"""Enhanced CAS Authentication Middleware"""
def __init__(self, get_response):
self.get_response = get_response
# Protected URL patterns that require CAS authentication
self.protected_patterns = [
'/user/dashboard',
'/admin/systems',
'/api/profile',
'/protected/'
]
def __call__(self, request):
# Check if the request path requires CAS authentication
if self.requires_cas_auth(request.path):
if not self.is_authenticated(request):
return self.handle_unauthenticated(request)
response = self.get_response(request)
return response
def requires_cas_auth(self, path):
"""Check if path requires CAS authentication"""
return any(path.startswith(pattern) for pattern in self.protected_patterns)
def is_authenticated(self, request):
"""Check if user is authenticated via CAS"""
cas_user = request.session.get('cas_user')
cas_token = request.session.get('cas_token')
if not cas_user or not cas_token:
return False
# Validate existing token
try:
validation = cas_client.validate_token(cas_token)
if validation.get('valid'):
# Update request with user info
request.cas_user = cas_user
request.cas_token = cas_token
return True
else:
# Clear invalid session
request.session.pop('cas_user', None)
request.session.pop('cas_token', None)
return False
except Exception:
return False
def handle_unauthenticated(self, request):
"""Handle unauthenticated requests"""
# Store return URL for after authentication
request.session['cas_return_url'] = request.get_full_path()
# Check if this is an AJAX request
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return JsonResponse({
'error': 'Authentication required',
'login_url': cas_client.get_login_url(request.get_full_path())
}, status=401)
# Redirect to CAS login
login_url = cas_client.get_login_url(request.get_full_path())
return redirect(login_url)
def cas_required(view_func):
"""Decorator for views that require CAS authentication"""
def wrapper(request, *args, **kwargs):
cas_user = request.session.get('cas_user')
cas_token = request.session.get('cas_token')
if not cas_user or not cas_token:
return redirect(cas_client.get_login_url(request.get_full_path()))
# Validate token
validation = cas_client.validate_token(cas_token)
if not validation.get('valid'):
request.session.pop('cas_user', None)
request.session.pop('cas_token', None)
return redirect(cas_client.get_login_url(request.get_full_path()))
# Add user info to request
request.cas_user = cas_user
request.cas_token = cas_token
return view_func(request, *args, **kwargs)
return wrapper
5. Views Implementation
Create cas_client/views.py:
from django.shortcuts import render, redirect
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from django.contrib import messages
from .client import cas_client
from .middleware import cas_required
import json
@csrf_exempt
@require_http_methods(["GET"])
def cas_callback(request):
"""Enhanced SSO callback handler"""
try:
token = request.GET.get('token')
return_url = request.GET.get('return_url', '/user/dashboard')
if not token:
messages.error(request, 'Authentication failed: Missing token')
return redirect('/auth/login')
# Process callback with enhanced validation
result = cas_client.process_callback({
'token': token,
'return_url': return_url
})
if result['success']:
# Store user session
request.session['cas_user'] = result['user']
request.session['cas_token'] = result['token']
messages.success(request, f"Welcome back, {result['user']['username']}!")
return redirect(result['return_url'])
else:
messages.error(request, f"Authentication failed: {result['error']}")
return redirect('/auth/login')
except Exception as e:
messages.error(request, f'Callback processing failed: {str(e)}')
return redirect('/auth/login')
@require_http_methods(["POST"])
def cas_logout(request):
"""Enhanced logout handler"""
# Clear CAS session
request.session.pop('cas_user', None)
request.session.pop('cas_token', None)
request.session.flush()
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return JsonResponse({
'success': True,
'message': 'Logged out successfully',
'redirect': '/auth/login'
})
messages.success(request, 'You have been logged out successfully')
return redirect('/auth/login')
@cas_required
def user_dashboard(request):
"""Enhanced user dashboard with new architecture"""
user = request.cas_user
context = {
'user': user,
'features': [
'Client System Linking',
'One-Click SSO Login',
'Profile Management',
'System Access Control'
],
'architecture': {
'separation': 'Admin/User/Public',
'database': 'PostgreSQL Multi-Schema',
'performance': 'Livewire Optimized'
}
}
return render(request, 'cas_client/dashboard.html', context)
@cas_required
def user_profile_api(request):
"""Protected API endpoint for user profile"""
user = request.cas_user
return JsonResponse({
'success': True,
'profile': {
'id': user['id'],
'username': user['username'],
'email': user.get('email'),
'roles': user.get('roles', []),
'last_login': user.get('last_login'),
'client_systems': user.get('client_systems', [])
}
})
def health_check(request):
"""Health check with architecture info"""
return JsonResponse({
'status': 'healthy',
'timestamp': str(timezone.now()),
'services': {
'cas_client': 'connected',
'session_store': 'active',
'authentication': 'enabled'
},
'architecture': {
'type': 'CAS Client - Django/Python',
'version': '2.0',
'features': [
'Enhanced JWT Authentication',
'HMAC-SHA256 Signature Validation',
'Admin/User/Public Route Separation',
'PostgreSQL Multi-Schema Support'
]
}
})
6. URL Configuration
Create cas_client/urls.py:
from django.urls import path
from . import views
app_name = 'cas_client'
urlpatterns = [
# Enhanced authentication URLs
path('auth/cas/callback/', views.cas_callback, name='cas_callback'),
path('auth/logout/', views.cas_logout, name='cas_logout'),
# Enhanced user dashboard - new architecture
path('user/dashboard/', views.user_dashboard, name='user_dashboard'),
# Protected API endpoints
path('api/profile/', views.user_profile_api, name='user_profile_api'),
# Health check
path('health/', views.health_check, name='health_check'),
]
Add to your main urls.py:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('cas_client.urls')),
# ... other URL patterns
]
7. Template Example
Create templates/cas_client/dashboard.html:
User Dashboard - CAS Client
👤 User Dashboard
Enhanced CAS Architecture
username
System Architecture
Clean Separation
Modular Design
PostgreSQL
Enterprise Database
Optimized
High Performance
Available Features
Single Sign-On Authentication
JWT Token Management
Secure Client Authentication
Comprehensive Audit Logging
8. Testing Your Integration
Test Checklist
- CAS middleware intercepts protected URLs
- SSO authentication flow completes
- Token validation and signature verification
- User dashboard (/user/dashboard) accessible
- Session management works correctly
- Logout functionality clears session