Avnology ID
SDKsPython SDKGuides

Django Integration

Protect Django views with Avnology ID authentication and authorization.

Django Integration

Setup

pip install avnology-id django

Settings

# settings.py
AVNOLOGY_ID = {
    "BASE_URL": "https://api.id.avnology.com",
    "CLIENT_ID": os.environ["AVNOLOGY_CLIENT_ID"],
    "CLIENT_SECRET": os.environ["AVNOLOGY_CLIENT_SECRET"],
}

Client singleton

# lib/avnology.py
from django.conf import settings
from avnology_id import AvnologyId

_client = None

def get_client() -> AvnologyId:
    global _client
    if _client is None:
        config = settings.AVNOLOGY_ID
        _client = AvnologyId(
            base_url=config["BASE_URL"],
            client_id=config["CLIENT_ID"],
            client_secret=config["CLIENT_SECRET"],
        )
    return _client

Authentication middleware

# middleware/auth.py
from django.http import JsonResponse
from lib.avnology import get_client

class AvnologyAuthMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        self.exempt_paths = ["/health", "/webhooks/"]

    def __call__(self, request):
        if any(request.path.startswith(p) for p in self.exempt_paths):
            return self.get_response(request)

        auth_header = request.headers.get("Authorization", "")
        if not auth_header.startswith("Bearer "):
            return JsonResponse({"error": "missing_token"}, status=401)

        token = auth_header.removeprefix("Bearer ")
        client = get_client()
        result = client.oauth.introspect_token(token=token)

        if not result.active:
            return JsonResponse({"error": "invalid_token"}, status=401)

        request.avnology_user_id = result.sub
        request.avnology_scopes = result.scope.split()
        request.avnology_org_id = result.org_id

        return self.get_response(request)

Register in settings

# settings.py
MIDDLEWARE = [
    "middleware.auth.AvnologyAuthMiddleware",
    # ... other middleware
]

View decorator

# decorators.py
from functools import wraps
from django.http import JsonResponse
from lib.avnology import get_client

def require_permission(relation, object_fn):
    def decorator(view_func):
        @wraps(view_func)
        def wrapper(request, *args, **kwargs):
            obj = object_fn(request, *args, **kwargs)
            client = get_client()

            allowed = client.permissions.check(
                subject=f"user:{request.avnology_user_id}",
                relation=relation,
                object=obj,
            )
            if not allowed:
                return JsonResponse({"error": "forbidden"}, status=403)

            return view_func(request, *args, **kwargs)
        return wrapper
    return decorator

# Usage:
@require_permission("editor", lambda req, pk: f"project:{pk}")
def update_project(request, pk):
    # User has editor permission
    ...

Webhook view

# views.py
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from avnology_id.webhook import verify_signature

@csrf_exempt
def webhook_handler(request):
    if request.method != "POST":
        return HttpResponse(status=405)

    signature = request.headers.get("X-Avnology-Signature", "")
    if not verify_signature(request.body, signature, WEBHOOK_SECRET):
        return JsonResponse({"error": "invalid_signature"}, status=401)

    event = json.loads(request.body)
    # Process event...

    return HttpResponse(status=200)

See also

On this page