I ran into the same problem and it was very frustrating. I ended up coming up with an admittedly less than ideal solution that involves re-defining the built-in views from oauth2_provider
for the routes you want protected from regular users.
- Create a new directory in your application called
oauth
and create a views.py
and urls.py
.
- In
views.py
:
a. Create two new mixins StaffRequiredMixin
and ApplicationOwnerIsStaffMixin
. Both require user.is_staff
to be True
.
b. Redefine the views found in oauth2_provider.views.application.py
by copy/pasting and update them to use the new mixins.
from django.contrib.auth.mixins import LoginRequiredMixin
from django.forms.models import modelform_factory
from django.urls import reverse_lazy
from django.views.generic import CreateView, DeleteView, DetailView, ListView, UpdateView
from oauth2_provider import models as oauth2_provider_models
class StaffRequiredMixin(LoginRequiredMixin):
"""Verify that the current user is staff."""
def dispatch(self, request, *args, **kwargs):
if not request.user.is_staff:
return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs)
class ApplicationOwnerIsStaffMixin(StaffRequiredMixin):
"""
This mixin is used to provide an Application queryset filtered by request.user.is_staff.
"""
fields = "__all__"
def get_queryset(self):
return oauth2_provider_models.get_application_model().objects.filter(user=self.request.user.is_staff)
class ApplicationRegistration(StaffRequiredMixin, CreateView):
"""
View used to register a new Application for the request.user
"""
template_name = "oauth2_provider/application_registration_form.html"
def get_form_class(self):
"""
Returns the form class for the application model
"""
return modelform_factory(
oauth2_provider_models.get_application_model(),
fields=(
"name",
"client_id",
"client_secret",
"client_type",
"authorization_grant_type",
"redirect_uris",
"algorithm",
),
)
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
class ApplicationDetail(ApplicationOwnerIsStaffMixin, DetailView):
"""
Detail view for an application instance owned by the request.user
"""
context_object_name = "application"
template_name = "oauth2_provider/application_detail.html"
class ApplicationList(ApplicationOwnerIsStaffMixin, ListView):
"""
List view for all the applications owned by the request.user
"""
context_object_name = "applications"
template_name = "oauth2_provider/application_list.html"
class ApplicationDelete(ApplicationOwnerIsStaffMixin, DeleteView):
"""
View used to delete an application owned by the request.user
"""
context_object_name = "application"
success_url = reverse_lazy("oauth2_provider:list")
template_name = "oauth2_provider/application_confirm_delete.html"
class ApplicationUpdate(ApplicationOwnerIsStaffMixin, UpdateView):
"""
View used to update an application owned by the request.user
"""
context_object_name = "application"
template_name = "oauth2_provider/application_form.html"
def get_form_class(self):
"""
Returns the form class for the application model
"""
return modelform_factory(
oauth2_provider_models.get_application_model(),
fields=(
"name",
"client_id",
"client_secret",
"client_type",
"authorization_grant_type",
"redirect_uris",
"algorithm",
),
)
- In
urls.py
:
a. Redefine the urlpatterns to match that of oauth2_provider.urls.py
and substitute in the new views for all the application/
and authorized_tokens/
based routes.
b. Notice the last two lines of the file. In order to make all the other routes accessible for authorization requests and granting tokens, etc. we need to include the default views for those here as well.
from django.urls import re_path
from . import views
from oauth2_provider import views as oauth2_provider_views
from oauth2_provider import urls as oauth2_provider_urls
app_name = "oauth"
urlpatterns = [
# Application management views
re_path(r"^applications/$", views.ApplicationList.as_view(), name="list"),
re_path(r"^applications/register/$", views.ApplicationRegistration.as_view(), name="register"),
re_path(r"^applications/(?P<pk>[\w-]+)/$", views.ApplicationDetail.as_view(), name="detail"),
re_path(r"^applications/(?P<pk>[\w-]+)/delete/$", views.ApplicationDelete.as_view(), name="delete"),
re_path(r"^applications/(?P<pk>[\w-]+)/update/$", views.ApplicationUpdate.as_view(), name="update"),
# Token management views
re_path(r"^authorized_tokens/$", oauth2_provider_views.AuthorizedTokensListView.as_view(), name="authorized-token-list"),
re_path(r"^authorized_tokens/(?P<pk>[\w-]+)/delete/$", oauth2_provider_views.AuthorizedTokenDeleteView.as_view(), name="authorized-token-delete")
]
urlpatterns += oauth2_provider_urls.base_urlpatterns
urlpatterns += oauth2_provider_urls.oidc_urlpatterns
- In your main applications configuration
urls.py
a. Update it to use your new oauth
routes and views.
b. If you had the one from the django_oauth_toolkit
example, replace it with this one.
urlpatterns = [
path("", ..., name="home"),
...
# Remove this one
# path("o/", include("oauth2_provider.urls", namespace="oauth2_provider")),
# Add this one
path("o/", include("your_application.oauth.urls", namespace="oauth2_provider")),
...
]