Skip to main content

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 ID
  • UVB_URL (optional): UVB server URL, defaults to http://localhost:8080
  • UVB_API_KEY (optional): API key for server-to-server authentication
  • UVB_COOKIE_NAME (optional): Cookie name for session token, defaults to uvb_session
  • UVB_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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

uvb_django-0.2.0.tar.gz (9.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

uvb_django-0.2.0-py3-none-any.whl (10.3 kB view details)

Uploaded Python 3

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

Hashes for uvb_django-0.2.0.tar.gz
Algorithm Hash digest
SHA256 96375a7f91ca7ba9645fda38a1dd9683e7853401b086404755baea22c8d0fec5
MD5 a99046877703d070aaff3ef6fc3827e1
BLAKE2b-256 20d28322fdce0f9b804458e9d5c4eb8df8ad95607cd69e61cd50d6c1387b5c7a

See more details on using hashes here.

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

Hashes for uvb_django-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6ece12f03b82415a824b1c2a8ad364973ff8f2dff56dea0e0363466994579cb6
MD5 688bfc88f097b73f34e0e852ff04d22e
BLAKE2b-256 c3ea76bc3fb11a97b24374a30867294155ddbf9af7b1164a718472e19247dd55

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page