Welcome to axioms-drf-py documentation!#
OAuth2/OIDC authentication and authorization for Django REST Framework APIs. Supports authentication and claim-based fine-grained authorization (scopes, roles, permissions) using JWT tokens.
Works with access tokens issued by various authorization servers including AWS Cognito, Auth0, Okta, Microsoft Entra, Keyclock, etc.
Note
Using Flask or FastAPI? This package is specifically for Django REST Framework. For Flask applications, use axioms-flask-py. For FastAPI applications, use axioms-fastapi.
When to use axioms-drf-py?#
Use axioms-drf-py in your Django REST Framework backend to securely validate JWT access
tokens issued by OAuth2/OIDC authorization servers like AWS Cognito,
Auth0,
Okta, Microsoft Entra, Keyclock,
etc. Clients - such as single-page applications (React, Vue), mobile apps, or AI agents—obtain access tokens from the authorization server and send them to your backend. In response, axioms-drf-py fetches JSON Web Key Set (JWKS) from the issuer, validates token signatures, enforces audience/issuer claims, and provides scope, role, and permission-based authorization for your API endpoints.
How it is different?#
Unlike other DRF plugins, axioms-drf-py focuses exclusively on protecting resource servers, by letting authorization servers do what they do best. This separation of concerns raises the security bar by:
Delegates authorization to battle-tested OAuth2/OIDC providers
Works seamlessly with any OAuth2/OIDC ID with simple configuration
Enterprise-ready defaults using current JWT and OAuth 2.1 best practices
Features#
JWT token validation with automatic public key retrieval from JWKS endpoints
Algorithm validation to prevent algorithm confusion attacks (only secure asymmetric algorithms allowed)
Issuer validation (
issclaim) to prevent token substitution attacksAuthentication classes for standard DRF integration
Permission classes for claim-based authorization:
scopes,roles, andpermissionsObject-level permission classes for resource ownership verification
Support for both OR and AND logic in authorization checks
Middleware for automatic token extraction and validation
Flexible configuration with support for custom JWKS and issuer URLs
Simple integration with Django REST Framework Resource Server or API backends
Support for custom claim and/or namespaced claims names to support different authorization servers
Prerequisites#
Python 3.10+
Django 3.2+
Django REST Framework 3.12+
An OAuth2/OIDC authorization server (AWS Cognito, Auth0, Okta, Microsoft Entra, etc.) that can issue JWT access tokens
Installation#
Install the package using pip:
pip install axioms-drf-py
Quick Start#
Add the middleware to your Django settings:
MIDDLEWARE = [
'axioms_drf.middleware.AccessTokenMiddleware',
# ... other middleware
]
Configure your Django settings with required variables:
Option A: Using .env file
Create a .env file in your project root:
AXIOMS_AUDIENCE=your-api-audience
# Set Issuer and JWKS URLs directly (optional, but recommended for security)
AXIOMS_ISS_URL=https://your-auth.domain.com
AXIOMS_JWKS_URL=https://your-auth.domain.com/.well-known/jwks.json
# Optionally, you can set the auth domain and let the SDK construct the URLs
# AXIOMS_DOMAIN=your-auth.domain.com
Then load in your settings.py:
import environ
env = environ.Env()
environ.Env.read_env()
# Required
AXIOMS_AUDIENCE = env('AXIOMS_AUDIENCE')
AXIOMS_ISS_URL = env('AXIOMS_ISS_URL', default=None)
AXIOMS_JWKS_URL = env('AXIOMS_JWKS_URL', default=None)
# AXIOMS_DOMAIN = env('AXIOMS_DOMAIN', default=None)
Option B: Direct Configuration
Configure directly in your settings.py:
# Required settings
AXIOMS_AUDIENCE = 'your-api-audience'
# Set Issuer and JWKS URLs directly (optional, but recommended for security)
AXIOMS_ISS_URL = 'https://your-auth.domain.com'
AXIOMS_JWKS_URL = 'https://your-auth.domain.com/.well-known/jwks.json'
# Optionally, you can set the auth domain and let the SDK construct the URLs
# AXIOMS_DOMAIN = 'your-auth.domain.com'
Enable JWKS Manager (Recommended)
For optimal performance with automatic background refresh of JWKS keys, add the JWKS manager app to your INSTALLED_APPS:
For WSGI applications (standard Django):
INSTALLED_APPS = [
# ... other apps
'axioms_drf.apps.JWKSManagerWSGIConfig', # Add this line
# ... your apps
]
For ASGI applications (async Django):
INSTALLED_APPS = [
# ... other apps
'axioms_drf.apps.JWKSManagerASGIConfig', # Add this line for ASGI
# ... your apps
]
Optional Configuration:
Customize JWKS caching behavior in settings.py:
# Optional: JWKS manager configuration (defaults shown)
AXIOMS_JWKS_REFRESH_INTERVAL = 3600 # Refresh JWKS every 1 hour (seconds)
AXIOMS_JWKS_CACHE_TTL = 7200 # Cache JWKS for 2 hours (must be >= 2x refresh_interval)
Note
If you don’t add the JWKS manager app, the SDK will automatically fall back to on-demand fetching with simple caching. This works but isn’t optimal for production.
Use authentication and permission classes in your views:
from rest_framework.views import APIView
from rest_framework.response import Response
from axioms_drf.authentication import HasValidAccessToken
from axioms_drf.permissions import HasAccessTokenScopes
class ProtectedView(APIView):
authentication_classes = [HasValidAccessToken]
permission_classes = [HasAccessTokenScopes]
access_token_scopes = ['read:data']
def get(self, request):
return Response({'message': 'This is protected'})
class AdminView(APIView):
authentication_classes = [HasValidAccessToken]
permission_classes = [HasAccessTokenRoles]
access_token_roles = ['admin']
def get(self, request):
return Response({'message': 'Admin access'})
Configuration#
The SDK supports the following configuration options in Django settings:
AXIOMS_AUDIENCE(required): Your resource identifier or API audienceAXIOMS_DOMAIN(optional): Your auth domain - constructs issuer and JWKS URLsAXIOMS_ISS_URL(optional): Full issuer URL for validating theissclaim (recommended for security)AXIOMS_JWKS_URL(optional): Full URL to your JWKS endpoint
Configuration Hierarchy:
AXIOMS_DOMAIN→ constructs →AXIOMS_ISS_URL(if not explicitly set)AXIOMS_ISS_URL→ constructs →AXIOMS_JWKS_URL(if not explicitly set)
Important
You must provide at least one of: AXIOMS_DOMAIN, AXIOMS_ISS_URL, or AXIOMS_JWKS_URL.
For most use cases, setting only AXIOMS_DOMAIN is sufficient. The SDK will automatically construct the issuer URL and JWKS endpoint URL.
Guard Your DRF Views#
Use the following authentication and permission classes to protect your API views:
Authentication Classes#
Authentication Class |
Description |
Parameters |
|---|---|---|
|
Validates JWT access token from Authorization header. Performs token signature validation, expiry datetime validation, token audience validation, and issuer validation (if configured). Should be set as the authentication class on protected views. |
None |
|
Alias for |
None |
|
Allows POST requests without authentication, requires valid token for other methods. |
None |
|
Allows GET requests without authentication, requires valid token for other methods. |
None |
Permission Classes#
Claim-Based Permissions#
Permission Class |
Description |
View Attributes |
|---|---|---|
|
Check scopes in |
|
|
Check roles in |
|
|
Check permissions in |
|
Note
Method-Level Authorization: All claim-based permission classes support method-level and ViewSet action-Specific authorization
using Python’s @property decorator. This allows you to define different authorization requirements for each HTTP method (GET, POST, PATCH, DELETE)
on the View or different permissions for each action (list, retrieve, create, update, destroy) of ViewSet. See the examples section for implementation details.
Object-Level Permissions#
Permission Class |
Description |
View Attributes |
|---|---|---|
|
Verifies that the token’s |
|
|
Allows safe methods (GET, HEAD, OPTIONS) for all authenticated users, restricts unsafe methods (POST, PUT, PATCH, DELETE) to owners only. |
|
OR vs AND Logic#
Permission classes support both OR logic (any claim) and AND logic (all claims) through different view attributes. You can also combine both for complex authorization requirements.
OR Logic (Default) - Requires ANY of the specified claims:
from rest_framework.views import APIView
from rest_framework.response import Response
from axioms_drf.authentication import HasValidAccessToken
from axioms_drf.permissions import HasAccessTokenScopes
class DataView(APIView):
authentication_classes = [HasValidAccessToken]
permission_classes = [HasAccessTokenScopes]
access_token_scopes = ['read:data', 'write:data'] # OR logic
def get(self, request):
# User needs EITHER 'read:data' OR 'write:data' scope
return Response({'data': 'success'})
class AdminView(APIView):
authentication_classes = [HasValidAccessToken]
permission_classes = [HasAccessTokenRoles]
access_token_roles = ['admin', 'superuser'] # OR logic
def get(self, request):
# User needs EITHER 'admin' OR 'superuser' role
return Response({'users': []})
AND Logic - Requires ALL of the specified claims:
class SecureView(APIView):
authentication_classes = [HasValidAccessToken]
permission_classes = [HasAccessTokenScopes]
access_token_all_scopes = ['read:data', 'write:data'] # AND logic
def post(self, request):
# User needs BOTH 'read:data' AND 'write:data' scopes
return Response({'status': 'created'})
class SuperAdminView(APIView):
authentication_classes = [HasValidAccessToken]
permission_classes = [HasAccessTokenRoles]
access_token_all_roles = ['admin', 'superuser'] # AND logic
def get(self, request):
# User needs BOTH 'admin' AND 'superuser' roles
return Response({'message': 'super admin access'})
Mixed Logic - Combine OR and AND requirements:
class MixedView(APIView):
authentication_classes = [HasValidAccessToken]
permission_classes = [HasAccessTokenScopes]
access_token_any_scopes = ['read:data', 'read:all'] # Needs read:data OR read:all
access_token_all_scopes = ['openid', 'profile'] # AND needs BOTH openid AND profile
def get(self, request):
# User needs: (read:data OR read:all) AND (openid AND profile)
return Response({'data': 'complex authorization'})
Complete Examples#
For complete working examples including ViewSet-specific permissions, method-level authorization, and object-level permissions, check out: