Skip to content

Commit c2d60d3

Browse files
committed
Add the device user code form
1 parent 42efa46 commit c2d60d3

File tree

4 files changed

+83
-3
lines changed

4 files changed

+83
-3
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!-- device_verification.html -->
2+
<!DOCTYPE html>
3+
<html>
4+
<head>
5+
<title>Device code</title>
6+
</head>
7+
<body>
8+
<h1>Enter code displayed on device</h1>
9+
<form method="post">
10+
{% csrf_token %}
11+
{{ form.as_p }}
12+
<button type="submit">Verify</button>
13+
</form>
14+
</body>
15+
</html>

oauth2_provider/urls.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
path("token/", views.TokenView.as_view(), name="token"),
1212
path("revoke_token/", views.RevokeTokenView.as_view(), name="revoke-token"),
1313
path("introspect/", views.IntrospectTokenView.as_view(), name="introspect"),
14-
path("device_authorization/", views.DeviceAuthorizationView.as_view(), name="device-authorization")
14+
path("device-authorization/", views.DeviceAuthorizationView.as_view(), name="device-authorization"),
15+
path("device/", views.device_user_code_view, name="device"),
1516
]
1617

1718

oauth2_provider/views/device.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
import json
22

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
7+
from django.utils import timezone
48
from django.utils.decorators import method_decorator
59
from django.views.decorators.csrf import csrf_exempt
610
from django.views.generic import View
711
from oauthlib.oauth2 import DeviceApplicationServer
12+
from oauthlib.oauth2.rfc8628.errors import (
13+
AccessDenied,
14+
ExpiredTokenError,
15+
)
816

917
from oauth2_provider.compat import login_not_required
10-
from oauth2_provider.models import DeviceCodeResponse, DeviceRequest, create_device
18+
from oauth2_provider.models import Device, DeviceCodeResponse, DeviceRequest, create_device, get_device_model
1119
from oauth2_provider.settings import oauth2_settings
1220
from oauth2_provider.views.mixins import OAuthLibMixin
1321

@@ -31,3 +39,44 @@ def post(self, request, *args, **kwargs):
3139
create_device(device_request, device_response)
3240

3341
return http.JsonResponse(data=response, status=status, headers=headers)
42+
43+
44+
class DeviceForm(forms.Form):
45+
user_code = forms.CharField(required=True)
46+
47+
48+
# it's common to see in real world products
49+
# device flow's only asking the user to sign in after they input the
50+
# user code but since the user has to be signed in regardless to approve the
51+
# device login we're making the decision here to require being logged in
52+
# up front
53+
@login_required
54+
def device_user_code_view(request):
55+
form = DeviceForm(request.POST)
56+
57+
if request.method != "POST":
58+
return render(request, "oauth2_provider/device/user_code.html", {"form": form})
59+
60+
if not form.is_valid():
61+
return render(request, "oauth2_provider/device/user_code.html", {"form": form})
62+
63+
user_code: str = form.cleaned_data["user_code"]
64+
device: Device = get_device_model().objects.get(user_code=user_code)
65+
66+
if device is None:
67+
form.add_error("user_code", "Incorrect user code")
68+
return render(request, "oauth2_provider/device/user_code.html", {"form": form})
69+
70+
if timezone.now() > device.expires:
71+
device.status = device.EXPIRED
72+
device.save(update_fields=["status"])
73+
raise ExpiredTokenError
74+
75+
# User of device has already made their decision for this device
76+
if device.status in (device.DENIED, device.AUTHORIZED):
77+
raise AccessDenied
78+
79+
# 308 to indicate we want to keep the redirect being a POST request
80+
return http.HttpResponsePermanentRedirect(
81+
reverse("oauth2_provider:device-confirm", kwargs={"device_code": device.device_code}), status=308
82+
)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!-- device_verification.html -->
2+
<!DOCTYPE html>
3+
<html>
4+
<head>
5+
<title>Device code</title>
6+
</head>
7+
<body>
8+
<h1>Enter code displayed on device</h1>
9+
<form method="post">
10+
{% csrf_token %}
11+
{{ form.as_p }}
12+
<button type="submit">Verify</button>
13+
</form>
14+
</body>
15+
</html>

0 commit comments

Comments
 (0)