Skip to content

Commit b505d2d

Browse files
duzumakidopry
authored andcommitted
Add the device user code form
1 parent 6623cf7 commit b505d2d

File tree

4 files changed

+84
-3
lines changed

4 files changed

+84
-3
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{% extends "oauth2_provider/base.html" %}
2+
{% block content %}
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>
16+
{% endblock content %}

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: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
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
47
from django.utils.decorators import method_decorator
58
from django.views.decorators.csrf import csrf_exempt
69
from django.views.generic import View
710
from oauthlib.oauth2 import DeviceApplicationServer
11+
from oauthlib.oauth2.rfc8628.errors import (
12+
AccessDenied,
13+
ExpiredTokenError,
14+
)
815

916
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
1118
from oauth2_provider.views.mixins import OAuthLibMixin
1219

1320

@@ -28,3 +35,44 @@ def post(self, request, *args, **kwargs):
2835
create_device(device_request, device_response)
2936

3037
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+
)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{% extends "oauth2_provider/base.html" %}
2+
{% block content %}
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>
16+
{% endblock content %}

0 commit comments

Comments
 (0)