1
1
import json
2
2
3
- from django import http
3
+ from django import forms , http
4
+ from django .contrib .auth .decorators import login_required
5
+ from django .shortcuts import render
6
+ from django .urls import reverse
4
7
from django .utils .decorators import method_decorator
5
8
from django .views .decorators .csrf import csrf_exempt
6
9
from django .views .generic import View
7
10
from oauthlib .oauth2 import DeviceApplicationServer
11
+ from oauthlib .oauth2 .rfc8628 .errors import (
12
+ AccessDenied ,
13
+ ExpiredTokenError ,
14
+ )
8
15
9
16
from oauth2_provider .compat import login_not_required
10
- from oauth2_provider .models import DeviceCodeResponse , DeviceRequest , create_device
17
+ from oauth2_provider .models import Device , DeviceCodeResponse , DeviceRequest , create_device , get_device_model
11
18
from oauth2_provider .views .mixins import OAuthLibMixin
12
19
13
20
@@ -28,3 +35,44 @@ def post(self, request, *args, **kwargs):
28
35
create_device (device_request , device_response )
29
36
30
37
return http .JsonResponse (data = response , status = status , headers = headers )
38
+
39
+
40
+ class DeviceForm (forms .Form ):
41
+ user_code = forms .CharField (required = True )
42
+
43
+
44
+ # it's common to see in real world products
45
+ # device flow's only asking the user to sign in after they input the
46
+ # user code but since the user has to be signed in regardless to approve the
47
+ # device login we're making the decision here to require being logged in
48
+ # up front
49
+ @login_required
50
+ def device_user_code_view (request ):
51
+ form = DeviceForm (request .POST )
52
+
53
+ if request .method != "POST" :
54
+ return render (request , "oauth2_provider/device/user_code.html" , {"form" : form })
55
+
56
+ if not form .is_valid ():
57
+ return render (request , "oauth2_provider/device/user_code.html" , {"form" : form })
58
+
59
+ user_code : str = form .cleaned_data ["user_code" ]
60
+ device : Device = get_device_model ().objects .get (user_code = user_code )
61
+
62
+ if device is None :
63
+ form .add_error ("user_code" , "Incorrect user code" )
64
+ return render (request , "oauth2_provider/device/user_code.html" , {"form" : form })
65
+
66
+ if device .is_expired ():
67
+ device .status = device .EXPIRED
68
+ device .save (update_fields = ["status" ])
69
+ raise ExpiredTokenError
70
+
71
+ # User of device has already made their decision for this device
72
+ if device .status in (device .DENIED , device .AUTHORIZED ):
73
+ raise AccessDenied
74
+
75
+ # 308 to indicate we want to keep the redirect being a POST request
76
+ return http .HttpResponsePermanentRedirect (
77
+ reverse ("oauth2_provider:device-confirm" , kwargs = {"device_code" : device .device_code }), status = 308
78
+ )
0 commit comments