OAuth2 Provider for Django web applications with complete server functionality, token management, and authorization endpoints.
—
Django OAuth Toolkit provides decorators and middleware for protecting Django views with OAuth2 authentication and scope-based authorization. These tools integrate seamlessly with Django's view system and provide flexible access control.
Basic OAuth2 authentication decorator that protects views by requiring valid access tokens.
def protected_resource(scopes=None, validator_cls=OAuth2Validator, server_cls=Server):
"""
Decorator to protect views with OAuth2 authentication.
Args:
scopes: List of required OAuth2 scopes (optional)
validator_cls: Custom OAuth2 validator class
server_cls: Custom OAuth2 server class
Returns:
Decorated view function that requires OAuth2 authentication
Usage:
@protected_resource()
def my_view(request):
# Access token required to reach here
user = request.resource_owner # User from token or None
return JsonResponse({'user': user.username if user else None})
@protected_resource(scopes=['read'])
def read_data(request):
# Requires 'read' scope
return JsonResponse({'data': 'protected content'})
"""OAuth2 decorator with automatic read/write scope assignment based on HTTP method.
def rw_protected_resource(scopes=None, validator_cls=OAuth2Validator, server_cls=Server):
"""
Decorator that automatically assigns read/write scopes based on HTTP method.
GET, HEAD, OPTIONS methods require 'read' scope.
All other methods require 'write' scope.
Args:
scopes: Additional required scopes beyond read/write
validator_cls: Custom OAuth2 validator class
server_cls: Custom OAuth2 server class
Returns:
Decorated view function with automatic scope assignment
Usage:
@rw_protected_resource()
def api_endpoint(request):
# GET requests need 'read' scope
# POST/PUT/DELETE requests need 'write' scope
if request.method == 'POST':
return JsonResponse({'created': True})
return JsonResponse({'data': 'content'})
"""Class-based views that provide OAuth2 protection with various scope configurations.
class ProtectedResourceView(View):
"""
Basic OAuth2 protected view requiring valid access token.
Attributes:
required_scopes: List of required OAuth2 scopes
raise_exception: Whether to raise exception on auth failure
server_class: OAuth2 server class to use
validator_class: OAuth2 validator class to use
"""
required_scopes = []
raise_exception = False
server_class = Server
validator_class = OAuth2Validator
class ScopedProtectedResourceView(ProtectedResourceView):
"""
OAuth2 protected view with specific scope requirements.
Subclasses must define required_scopes attribute.
"""
required_scopes = None # Must be overridden
class ReadWriteScopedResourceView(ProtectedResourceView):
"""
OAuth2 protected view with automatic read/write scope assignment.
Safe methods (GET, HEAD, OPTIONS) require read scope.
Unsafe methods require write scope.
"""
def get_scopes(self, request):
"""
Get required scopes based on request method.
Returns:
List of scopes required for this request
"""
class ClientProtectedResourceView(ProtectedResourceView):
"""
OAuth2 protected view for client credentials flow.
Designed for server-to-server authentication.
"""
class ClientProtectedScopedResourceView(ClientProtectedResourceView):
"""
OAuth2 protected view for client credentials with specific scopes.
"""
required_scopes = None # Must be overriddenDjango middleware that adds OAuth2 token information to requests.
class OAuth2TokenMiddleware:
"""
Middleware that processes OAuth2 tokens and adds token info to request.
Adds the following attributes to HttpRequest:
- oauth2_error: Dict containing OAuth2 error information if any
- resource_owner: User associated with the token (if valid)
Should be added to MIDDLEWARE setting in Django configuration.
"""
def __init__(self, get_response):
"""Initialize middleware with Django response handler"""
def __call__(self, request):
"""Process request and add OAuth2 token information"""from oauth2_provider.decorators import protected_resource
from django.http import JsonResponse
@protected_resource()
def protected_api(request):
"""View requiring any valid OAuth2 token"""
return JsonResponse({
'message': 'Hello authenticated user!',
'user': request.resource_owner.username if request.resource_owner else 'Anonymous'
})
@protected_resource(scopes=['read'])
def read_only_api(request):
"""View requiring 'read' scope"""
return JsonResponse({'data': 'This is read-only data'})
@protected_resource(scopes=['write'])
def write_api(request):
"""View requiring 'write' scope"""
if request.method == 'POST':
return JsonResponse({'created': True})
return JsonResponse({'error': 'Method not allowed'}, status=405)from oauth2_provider.decorators import rw_protected_resource
@rw_protected_resource()
def auto_scope_api(request):
"""
Automatically assigns scopes:
- GET/HEAD/OPTIONS: requires 'read' scope
- POST/PUT/DELETE/PATCH: requires 'write' scope
"""
if request.method == 'GET':
return JsonResponse({'data': 'Retrieved data'})
elif request.method == 'POST':
return JsonResponse({'message': 'Data created'})
elif request.method == 'PUT':
return JsonResponse({'message': 'Data updated'})
elif request.method == 'DELETE':
return JsonResponse({'message': 'Data deleted'})
@rw_protected_resource(scopes=['admin'])
def admin_api(request):
"""Requires both read/write scopes AND admin scope"""
return JsonResponse({'message': 'Admin operation completed'})from oauth2_provider.views.generic import (
ProtectedResourceView,
ScopedProtectedResourceView,
ReadWriteScopedResourceView
)
from django.http import JsonResponse
class BasicProtectedView(ProtectedResourceView):
"""Basic OAuth2 protected view"""
def get(self, request):
return JsonResponse({
'user': request.resource_owner.username if request.resource_owner else None
})
class AdminOnlyView(ScopedProtectedResourceView):
"""View requiring admin scope"""
required_scopes = ['admin']
def get(self, request):
return JsonResponse({'message': 'Admin access granted'})
class AutoScopeView(ReadWriteScopedResourceView):
"""View with automatic read/write scope assignment"""
def get(self, request):
# Requires 'read' scope
return JsonResponse({'data': 'Read operation'})
def post(self, request):
# Requires 'write' scope
return JsonResponse({'message': 'Write operation'})from oauth2_provider.decorators import protected_resource
from oauth2_provider.oauth2_validators import OAuth2Validator
from oauthlib.oauth2 import Server
class CustomValidator(OAuth2Validator):
"""Custom OAuth2 validator with additional business logic"""
def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs):
# Custom scope validation logic
return super().validate_scopes(client_id, scopes, client, request, *args, **kwargs)
@protected_resource(
scopes=['api'],
validator_cls=CustomValidator,
server_cls=Server
)
def custom_protected_view(request):
"""View with custom validator and server classes"""
return JsonResponse({'message': 'Custom authentication successful'})# settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'oauth2_provider.middleware.OAuth2TokenMiddleware', # Add OAuth2 middleware
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]from oauth2_provider.decorators import protected_resource
from django.http import JsonResponse, HttpResponseForbidden
@protected_resource(scopes=['read'])
def error_handling_view(request):
"""View demonstrating error handling"""
try:
# Your protected logic here
return JsonResponse({'data': 'success'})
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)
# OAuth2 errors are automatically handled:
# - Missing token: HTTP 401 Unauthorized
# - Invalid token: HTTP 401 Unauthorized
# - Insufficient scopes: HTTP 403 Forbidden
# - Expired token: HTTP 401 Unauthorizedfrom oauth2_provider.decorators import protected_resource
from django.contrib.auth import get_user_model
User = get_user_model()
@protected_resource()
def user_profile_view(request):
"""Access custom user model through OAuth2 token"""
user = request.resource_owner
if user:
return JsonResponse({
'id': user.id,
'username': user.username,
'email': user.email,
# Access custom user fields
'custom_field': getattr(user, 'custom_field', None)
})
return JsonResponse({'error': 'No user found'}, status=400)Install with Tessl CLI
npx tessl i tessl/pypi-django-oauth-toolkit