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