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.1.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.1-py3-none-any.whl (10.3 kB view details)

Uploaded Python 3

File details

Details for the file uvb_django-0.2.1.tar.gz.

File metadata

  • Download URL: uvb_django-0.2.1.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.1.tar.gz
Algorithm Hash digest
SHA256 2b2649df18ca4de944ee974827fb3f850b4c4e91a64b87bc96876842300e1817
MD5 a450cae32a4f9d16f47b019843d951ac
BLAKE2b-256 fe48a03a02ba47b3c7d201f215b5e4173e1c23163576b4430dc21666624f3743

See more details on using hashes here.

File details

Details for the file uvb_django-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: uvb_django-0.2.1-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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 19da17a734c5c55f81f4b9afb5961ca95ca60205f4aa722dbc316145570816ff
MD5 227462ff6edfcaaef61a5b1cc61f1d87
BLAKE2b-256 84c5e435f6807fd99a2dd7215aa5c66f9149c9ef5ed1f048b09ceeee12f2d241

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