Django middleware for Universal Verification Broker (UVB)
Project description
uvb-django
Django middleware for Universal Verification Broker (UVB) authentication.
Installation
pip install uvb-django
Quick Start
1. Add Middleware
Add UVBAuthenticationMiddleware to your MIDDLEWARE in settings.py:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
# Add UVB middleware
'uvb_django.middleware.UVBAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
2. Configure Settings
Add UVB configuration to your settings.py:
# Required
UVB_TENANT_ID = 'my-tenant'
# Optional
UVB_URL = 'http://localhost:8080' # Default: http://localhost:8080
UVB_API_KEY = 'your-api-key' # For server-to-server auth
UVB_COOKIE_NAME = 'uvb_session' # Default: uvb_session
UVB_EXCLUDE_PATHS = ['/login', '/health', '/admin'] # Paths to skip auth
3. Use in Views
from django.http import HttpResponse, JsonResponse
from uvb_django import get_session, require_factors, uvb_required
# Access session in any view
def profile_view(request):
session = get_session(request)
if session:
return JsonResponse({
'user_id': session.user_id,
'factors': session.factors_verified
})
return JsonResponse({'error': 'Not authenticated'}, status=401)
# Require authentication with decorator
@uvb_required
def protected_view(request):
user_id = request.uvb_session.user_id
return HttpResponse(f"Hello {user_id}")
# Require specific MFA factors
@require_factors('totp', 'webauthn')
def admin_view(request):
return HttpResponse("Admin access granted")
API Reference
Middleware
UVBAuthenticationMiddleware
Django middleware that validates UVB sessions for all requests (except excluded paths).
Configuration (settings.py):
UVB_TENANT_ID(required): Your UVB tenant IDUVB_URL(optional): UVB server URL, defaults tohttp://localhost:8080UVB_API_KEY(optional): API key for server-to-server authenticationUVB_COOKIE_NAME(optional): Cookie name for session token, defaults touvb_sessionUVB_EXCLUDE_PATHS(optional): List of path prefixes to exclude from authentication
Request Extension:
After successful authentication, request.uvb_session contains a UVBSession object:
request.uvb_session.user_id # User identifier
request.uvb_session.tenant_id # Tenant identifier
request.uvb_session.session_id # Session identifier
request.uvb_session.factors_verified # List of verified factors
request.uvb_session.expires_at # Session expiration datetime
request.uvb_session.status # Session status
Decorators
@uvb_required
Decorator to require UVB authentication for a view.
from uvb_django import uvb_required
@uvb_required
def my_view(request):
# Access authenticated user
user_id = request.uvb_session.user_id
return HttpResponse(f"Hello {user_id}")
@require_factors(*factors)
Decorator to require specific MFA factors for a view.
from uvb_django import require_factors
@require_factors('totp', 'webauthn')
def admin_view(request):
return HttpResponse("Admin action completed")
Helper Functions
get_session(request)
Get the UVB session from a request.
from uvb_django import get_session
def my_view(request):
session = get_session(request)
if session:
return JsonResponse({'user_id': session.user_id})
return JsonResponse({'error': 'Not authenticated'}, status=401)
Examples
Class-Based Views
from django.views import View
from django.http import JsonResponse
from django.utils.decorators import method_decorator
from uvb_django import uvb_required, require_factors, get_session
class ProfileView(View):
@method_decorator(uvb_required)
def get(self, request):
session = get_session(request)
return JsonResponse({
'user_id': session.user_id,
'factors': session.factors_verified
})
class AdminView(View):
@method_decorator(require_factors('totp', 'webauthn'))
def delete(self, request, user_id):
return JsonResponse({'message': f'User {user_id} deleted'})
Django REST Framework
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from uvb_django import get_session
class UserProfileAPI(APIView):
def get(self, request):
session = get_session(request)
if not session:
return Response(
{'error': 'Not authenticated'},
status=status.HTTP_401_UNAUTHORIZED
)
return Response({
'user_id': session.user_id,
'tenant_id': session.tenant_id,
'factors_verified': session.factors_verified
})
Conditional MFA Requirements
from django.http import JsonResponse
from uvb_django import get_session, require_factors
def transfer_view(request):
session = get_session(request)
if not session:
return JsonResponse({'error': 'Not authenticated'}, status=401)
amount = request.POST.get('amount', 0)
# Require additional auth for large transfers
if int(amount) > 10000:
if not all(f in session.factors_verified for f in ['totp', 'webauthn']):
return JsonResponse({
'error': 'Additional authentication required',
'required': ['totp', 'webauthn'],
'verified': session.factors_verified
}, status=403)
return JsonResponse({'message': 'Transfer initiated'})
Custom Error Handling
# middleware.py
from uvb_django.middleware import UVBAuthenticationMiddleware
from django.http import JsonResponse
class CustomUVBMiddleware(UVBAuthenticationMiddleware):
def __call__(self, request):
# Custom logic before authentication
if request.path.startswith('/public/'):
return self.get_response(request)
# Call parent middleware
try:
return super().__call__(request)
except Exception as e:
# Custom error handling
return JsonResponse({
'error': 'Authentication failed',
'details': str(e)
}, status=401)
URL Patterns
# urls.py
from django.urls import path
from . import views
urlpatterns = [
# Public endpoints
path('health/', views.health_check),
path('login/', views.login),
# Protected endpoints (middleware applies)
path('api/profile/', views.profile_view),
path('api/data/', views.data_view),
# Admin endpoints with strict MFA
path('admin/users/', views.admin_users_view),
path('admin/delete/<int:user_id>/', views.admin_delete_view),
]
Multiple Factor Checks
from uvb_django import get_session
def sensitive_operation(request):
session = get_session(request)
if not session:
return JsonResponse({'error': 'Not authenticated'}, status=401)
# Check for at least one of multiple factors
has_strong_auth = any(
factor in session.factors_verified
for factor in ['webauthn', 'totp']
)
if not has_strong_auth:
return JsonResponse({
'error': 'Strong authentication required',
'message': 'Please verify with WebAuthn or TOTP'
}, status=403)
return JsonResponse({'message': 'Operation completed'})
Development
# Install development dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Type checking
mypy uvb_django
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file uvb_django-0.2.0.tar.gz.
File metadata
- Download URL: uvb_django-0.2.0.tar.gz
- Upload date:
- Size: 9.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
96375a7f91ca7ba9645fda38a1dd9683e7853401b086404755baea22c8d0fec5
|
|
| MD5 |
a99046877703d070aaff3ef6fc3827e1
|
|
| BLAKE2b-256 |
20d28322fdce0f9b804458e9d5c4eb8df8ad95607cd69e61cd50d6c1387b5c7a
|
File details
Details for the file uvb_django-0.2.0-py3-none-any.whl.
File metadata
- Download URL: uvb_django-0.2.0-py3-none-any.whl
- Upload date:
- Size: 10.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6ece12f03b82415a824b1c2a8ad364973ff8f2dff56dea0e0363466994579cb6
|
|
| MD5 |
688bfc88f097b73f34e0e852ff04d22e
|
|
| BLAKE2b-256 |
c3ea76bc3fb11a97b24374a30867294155ddbf9af7b1164a718472e19247dd55
|