0
# View Protection
1
2
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.
3
4
## Capabilities
5
6
### Protected Resource Decorator
7
8
Basic OAuth2 authentication decorator that protects views by requiring valid access tokens.
9
10
```python { .api }
11
def protected_resource(scopes=None, validator_cls=OAuth2Validator, server_cls=Server):
12
"""
13
Decorator to protect views with OAuth2 authentication.
14
15
Args:
16
scopes: List of required OAuth2 scopes (optional)
17
validator_cls: Custom OAuth2 validator class
18
server_cls: Custom OAuth2 server class
19
20
Returns:
21
Decorated view function that requires OAuth2 authentication
22
23
Usage:
24
@protected_resource()
25
def my_view(request):
26
# Access token required to reach here
27
user = request.resource_owner # User from token or None
28
return JsonResponse({'user': user.username if user else None})
29
30
@protected_resource(scopes=['read'])
31
def read_data(request):
32
# Requires 'read' scope
33
return JsonResponse({'data': 'protected content'})
34
"""
35
```
36
37
### Read/Write Protected Resource Decorator
38
39
OAuth2 decorator with automatic read/write scope assignment based on HTTP method.
40
41
```python { .api }
42
def rw_protected_resource(scopes=None, validator_cls=OAuth2Validator, server_cls=Server):
43
"""
44
Decorator that automatically assigns read/write scopes based on HTTP method.
45
GET, HEAD, OPTIONS methods require 'read' scope.
46
All other methods require 'write' scope.
47
48
Args:
49
scopes: Additional required scopes beyond read/write
50
validator_cls: Custom OAuth2 validator class
51
server_cls: Custom OAuth2 server class
52
53
Returns:
54
Decorated view function with automatic scope assignment
55
56
Usage:
57
@rw_protected_resource()
58
def api_endpoint(request):
59
# GET requests need 'read' scope
60
# POST/PUT/DELETE requests need 'write' scope
61
if request.method == 'POST':
62
return JsonResponse({'created': True})
63
return JsonResponse({'data': 'content'})
64
"""
65
```
66
67
### Generic Protected Views
68
69
Class-based views that provide OAuth2 protection with various scope configurations.
70
71
```python { .api }
72
class ProtectedResourceView(View):
73
"""
74
Basic OAuth2 protected view requiring valid access token.
75
76
Attributes:
77
required_scopes: List of required OAuth2 scopes
78
raise_exception: Whether to raise exception on auth failure
79
server_class: OAuth2 server class to use
80
validator_class: OAuth2 validator class to use
81
"""
82
83
required_scopes = []
84
raise_exception = False
85
server_class = Server
86
validator_class = OAuth2Validator
87
88
class ScopedProtectedResourceView(ProtectedResourceView):
89
"""
90
OAuth2 protected view with specific scope requirements.
91
Subclasses must define required_scopes attribute.
92
"""
93
94
required_scopes = None # Must be overridden
95
96
class ReadWriteScopedResourceView(ProtectedResourceView):
97
"""
98
OAuth2 protected view with automatic read/write scope assignment.
99
Safe methods (GET, HEAD, OPTIONS) require read scope.
100
Unsafe methods require write scope.
101
"""
102
103
def get_scopes(self, request):
104
"""
105
Get required scopes based on request method.
106
107
Returns:
108
List of scopes required for this request
109
"""
110
111
class ClientProtectedResourceView(ProtectedResourceView):
112
"""
113
OAuth2 protected view for client credentials flow.
114
Designed for server-to-server authentication.
115
"""
116
117
class ClientProtectedScopedResourceView(ClientProtectedResourceView):
118
"""
119
OAuth2 protected view for client credentials with specific scopes.
120
"""
121
122
required_scopes = None # Must be overridden
123
```
124
125
### OAuth2 Token Middleware
126
127
Django middleware that adds OAuth2 token information to requests.
128
129
```python { .api }
130
class OAuth2TokenMiddleware:
131
"""
132
Middleware that processes OAuth2 tokens and adds token info to request.
133
134
Adds the following attributes to HttpRequest:
135
- oauth2_error: Dict containing OAuth2 error information if any
136
- resource_owner: User associated with the token (if valid)
137
138
Should be added to MIDDLEWARE setting in Django configuration.
139
"""
140
141
def __init__(self, get_response):
142
"""Initialize middleware with Django response handler"""
143
144
def __call__(self, request):
145
"""Process request and add OAuth2 token information"""
146
```
147
148
## Usage Examples
149
150
### Basic View Protection
151
152
```python
153
from oauth2_provider.decorators import protected_resource
154
from django.http import JsonResponse
155
156
@protected_resource()
157
def protected_api(request):
158
"""View requiring any valid OAuth2 token"""
159
return JsonResponse({
160
'message': 'Hello authenticated user!',
161
'user': request.resource_owner.username if request.resource_owner else 'Anonymous'
162
})
163
164
@protected_resource(scopes=['read'])
165
def read_only_api(request):
166
"""View requiring 'read' scope"""
167
return JsonResponse({'data': 'This is read-only data'})
168
169
@protected_resource(scopes=['write'])
170
def write_api(request):
171
"""View requiring 'write' scope"""
172
if request.method == 'POST':
173
return JsonResponse({'created': True})
174
return JsonResponse({'error': 'Method not allowed'}, status=405)
175
```
176
177
### Read/Write Scope Views
178
179
```python
180
from oauth2_provider.decorators import rw_protected_resource
181
182
@rw_protected_resource()
183
def auto_scope_api(request):
184
"""
185
Automatically assigns scopes:
186
- GET/HEAD/OPTIONS: requires 'read' scope
187
- POST/PUT/DELETE/PATCH: requires 'write' scope
188
"""
189
if request.method == 'GET':
190
return JsonResponse({'data': 'Retrieved data'})
191
elif request.method == 'POST':
192
return JsonResponse({'message': 'Data created'})
193
elif request.method == 'PUT':
194
return JsonResponse({'message': 'Data updated'})
195
elif request.method == 'DELETE':
196
return JsonResponse({'message': 'Data deleted'})
197
198
@rw_protected_resource(scopes=['admin'])
199
def admin_api(request):
200
"""Requires both read/write scopes AND admin scope"""
201
return JsonResponse({'message': 'Admin operation completed'})
202
```
203
204
### Class-Based Protected Views
205
206
```python
207
from oauth2_provider.views.generic import (
208
ProtectedResourceView,
209
ScopedProtectedResourceView,
210
ReadWriteScopedResourceView
211
)
212
from django.http import JsonResponse
213
214
class BasicProtectedView(ProtectedResourceView):
215
"""Basic OAuth2 protected view"""
216
217
def get(self, request):
218
return JsonResponse({
219
'user': request.resource_owner.username if request.resource_owner else None
220
})
221
222
class AdminOnlyView(ScopedProtectedResourceView):
223
"""View requiring admin scope"""
224
required_scopes = ['admin']
225
226
def get(self, request):
227
return JsonResponse({'message': 'Admin access granted'})
228
229
class AutoScopeView(ReadWriteScopedResourceView):
230
"""View with automatic read/write scope assignment"""
231
232
def get(self, request):
233
# Requires 'read' scope
234
return JsonResponse({'data': 'Read operation'})
235
236
def post(self, request):
237
# Requires 'write' scope
238
return JsonResponse({'message': 'Write operation'})
239
```
240
241
### Custom Authentication Logic
242
243
```python
244
from oauth2_provider.decorators import protected_resource
245
from oauth2_provider.oauth2_validators import OAuth2Validator
246
from oauthlib.oauth2 import Server
247
248
class CustomValidator(OAuth2Validator):
249
"""Custom OAuth2 validator with additional business logic"""
250
251
def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs):
252
# Custom scope validation logic
253
return super().validate_scopes(client_id, scopes, client, request, *args, **kwargs)
254
255
@protected_resource(
256
scopes=['api'],
257
validator_cls=CustomValidator,
258
server_cls=Server
259
)
260
def custom_protected_view(request):
261
"""View with custom validator and server classes"""
262
return JsonResponse({'message': 'Custom authentication successful'})
263
```
264
265
### Middleware Configuration
266
267
```python
268
# settings.py
269
MIDDLEWARE = [
270
'django.middleware.security.SecurityMiddleware',
271
'django.contrib.sessions.middleware.SessionMiddleware',
272
'corsheaders.middleware.CorsMiddleware',
273
'django.middleware.common.CommonMiddleware',
274
'django.middleware.csrf.CsrfViewMiddleware',
275
'oauth2_provider.middleware.OAuth2TokenMiddleware', # Add OAuth2 middleware
276
'django.contrib.auth.middleware.AuthenticationMiddleware',
277
'django.contrib.messages.middleware.MessageMiddleware',
278
'django.middleware.clickjacking.XFrameOptionsMiddleware',
279
]
280
```
281
282
### Error Handling
283
284
```python
285
from oauth2_provider.decorators import protected_resource
286
from django.http import JsonResponse, HttpResponseForbidden
287
288
@protected_resource(scopes=['read'])
289
def error_handling_view(request):
290
"""View demonstrating error handling"""
291
try:
292
# Your protected logic here
293
return JsonResponse({'data': 'success'})
294
except Exception as e:
295
return JsonResponse({'error': str(e)}, status=500)
296
297
# OAuth2 errors are automatically handled:
298
# - Missing token: HTTP 401 Unauthorized
299
# - Invalid token: HTTP 401 Unauthorized
300
# - Insufficient scopes: HTTP 403 Forbidden
301
# - Expired token: HTTP 401 Unauthorized
302
```
303
304
### Integration with Custom User Models
305
306
```python
307
from oauth2_provider.decorators import protected_resource
308
from django.contrib.auth import get_user_model
309
310
User = get_user_model()
311
312
@protected_resource()
313
def user_profile_view(request):
314
"""Access custom user model through OAuth2 token"""
315
user = request.resource_owner
316
if user:
317
return JsonResponse({
318
'id': user.id,
319
'username': user.username,
320
'email': user.email,
321
# Access custom user fields
322
'custom_field': getattr(user, 'custom_field', None)
323
})
324
return JsonResponse({'error': 'No user found'}, status=400)
325
```