7

We're looking to implement Django OAuth on our backend in order to integrate Alexa and other 3rd party APIs. We've been following the tutorials on their site (http://django-oauth-toolkit.readthedocs.io/en/latest/tutorial/tutorial.html), but have run into a security question that has so far escaped us:

Is there a security concern that any user can access https://<oursite.com>/o/applications? If so, what steps need to be taken to prevent users from accessing these views?

The only relevant questions on SO weren't particularly helpful:

Secure creation of new applications in Django OAuth Toolkit

Disable or restrict /o/applications (django rest framework, oauth2)

Community
  • 1
  • 1
Nat Homer
  • 400
  • 3
  • 15

3 Answers3

6

I'm doing a similar thing, and I believe it is a security concern that anyone can see /o/applications - from what I can tell, that page is meant to be a development utility, not a production page. In fact, in the django-oauth-toolkit documentation, they have a code example with more restricted access to views.

from django.conf.urls import url
import oauth2_provider.views as oauth2_views
from django.conf import settings
from .views import ApiEndpoint

# OAuth2 provider endpoints
oauth2_endpoint_views = [
    url(r'^authorize/$', oauth2_views.AuthorizationView.as_view(), name="authorize"),
    url(r'^token/$', oauth2_views.TokenView.as_view(), name="token"),
    url(r'^revoke-token/$', oauth2_views.RevokeTokenView.as_view(), name="revoke-token"),
]

if settings.DEBUG:
    # OAuth2 Application Management endpoints
    oauth2_endpoint_views += [
        url(r'^applications/$', oauth2_views.ApplicationList.as_view(), name="list"),
        url(r'^applications/register/$', oauth2_views.ApplicationRegistration.as_view(), name="register"),
        url(r'^applications/(?P<pk>\d+)/$', oauth2_views.ApplicationDetail.as_view(), name="detail"),
        url(r'^applications/(?P<pk>\d+)/delete/$', oauth2_views.ApplicationDelete.as_view(), name="delete"),
        url(r'^applications/(?P<pk>\d+)/update/$', oauth2_views.ApplicationUpdate.as_view(), name="update"),
    ]

    # OAuth2 Token Management endpoints
    oauth2_endpoint_views += [
        url(r'^authorized-tokens/$', oauth2_views.AuthorizedTokensListView.as_view(), name="authorized-token-list"),
        url(r'^authorized-tokens/(?P<pk>\d+)/delete/$', oauth2_views.AuthorizedTokenDeleteView.as_view(),
            name="authorized-token-delete"),
    ]

urlpatterns = [
    # OAuth 2 endpoints:
    url(r'^o/', include(oauth2_endpoint_views, namespace="oauth2_provider")),

    url(r'^admin/', include(admin.site.urls)),
    url(r'^api/hello', ApiEndpoint.as_view()),  # an example resource endpoint
]

The revoke token view is part of the RFC, so that one is needed. I took a similar approach in my app of only including AuthorizationView, TokenView, and RevokeTokenView.

Hope that helps!

Noah Gilmore
  • 978
  • 2
  • 12
  • 23
  • https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ says "You must never enable debug in production." Therefore I believe they're giving us a hint that we should implement our own access controls to these sensitive endpoints. – David Chandler Mar 13 '18 at 15:18
  • Yep, the sensitive endpoints are entirely optional and should not be used in production ever. In fact, in my case it was easier to just leave them out entirely and manage the authorized applications through Django Admin. – Noah Gilmore Mar 15 '18 at 07:22
5

It is a security concern, and I suggest restricting access only to superusers with active accounts as in the following code from urls.py:

from django.contrib.auth.decorators import user_passes_test
import oauth2_provider.views as oauth2_views

def is_super(user):
    return user.is_superuser and user.is_active

oauth2_endpoint_views = [
    url(r'^authorize/$', oauth2_views.AuthorizationView.as_view(), name="authorize"),
    url(r'^token/$', oauth2_views.TokenView.as_view(), name="token"),
    url(r'^revoke-token/$', oauth2_views.RevokeTokenView.as_view(), name="revoke-token"),
    # the above are public but we restrict the following:
    url(r'^applications/$', user_passes_test(is_super)(oauth2_views.ApplicationList.as_view()), name="list"),
    ...
]
urlpatterns = [url(r'^o/', include(oauth2_endpoint_views, namespace="oauth2_provider"))]
David Chandler
  • 323
  • 2
  • 7
1

To exclude 'applications/' endpoint, import just required urls instead of using whole oauth2_provider.urls:

from oauth2_provider.urls import app_name, base_urlpatterns, management_urlpatterns

urlpatterns = [
    ...
    # oauth2
    path('oauth2/', include((base_urlpatterns, app_name), namespace='oauth2_provider'))
]

Only urls, required for the client app authorization will be added:

oauth2/ ^authorize/$ [name='authorize']
oauth2/ ^token/$ [name='token']
oauth2/ ^revoke_token/$ [name='revoke-token']
oauth2/ ^introspect/$ [name='introspect'] 

To add/remove applications, you can either use Django admin site, or allow management_urlpatterns for admin users, as in @David Chander answer: https://stackoverflow.com/a/49210935/7709003

Dmytro Gierman
  • 152
  • 1
  • 6