I'm doing backend project in which I need to do an API without any UI. In this API there's no registration (only by admin UI), but I need a way to log in, because I need some "pages" to be only for logged in users.
I set up token authentication, each user has their token created.
Is there a simple way to make some login "form" with serializer? I mean "page" in which there is only two fields (for username and password) and ability to POST this to get authenticated and then go back to "login only pages"?
I recommend to try DjangoREST browsable API: https://www.django-rest-framework.org/topics/browsable-api/. It is included into DRF, so you do not need to install any extras. In order to use the browsable API, just type the endpoint url into your browser. If you are using ModelSerializers, then forms for data input will be generated automatically, otherwise you will have to enter data as a JSON.
add to settings.py
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
),
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
),
}
add to urls.py
urlpatterns = [
...
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
...
]
I'm having problem with Django REST Swagger. I've created a simple viewset for users using DRF (just for showing my problem), where AppUser is my custom user model and it is not showing the POST method in my documentation, but I can call it with Postman and create a new resource.
I'm using:
Django 2.1
Django-rest-swagger 2.2.0
Djangorestframework 3.9.1
Here is my code:
views.py
class UserViewSet(viewsets.ModelViewSet):
queryset = AppUser.objects.all()
serializer_class = UserSerializer
serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = AppUser
fields = '__all__'
urls.py
from django.conf.urls import url, include
from rest_framework.routers import SimpleRouter
from rest_framework_swagger.views import get_swagger_view
import app.views as app
# creating router
router = SimpleRouter()
router.register(r'users', app.UserViewSet)
schema_view = get_swagger_view(title='My app API')
# register urls
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^docs', schema_view)
]
Here you can see what my app documentation looks like:
I would like to get something like this:
I've tried multiple tutorials on creating Swagger documentation and I was trying it on User model, but I still get only the GET request. What am I doing wrong?
Thank you for your help.
I've figured it out. I haven't been logged in properly so I haven't been authenticated against permissions listed in DEFAULT_PERMISSION_CLASSES setting for DRF in settings.py.
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES':
('rest_framework.permissions.IsAuthenticatedOrReadOnly',),
}
HTTP methods POST, PUT, PATCH, etc. are checked using has_permission() against list of permissions defined there.
After logging in it works well.
EDIT: Problem with login was, that Django-rest-swagger 2.2.0 is not working correctly with JWT authentication, so I downgraded to 2.1.2.
I've used create-react-app combined with Django Rest Framework to make a small site. Everything works perfectly when I use npm start and hit the Django API from one port to another. When I npm build the static react files and serve them from within the Django app, however, I get 403 forbidden when hitting a number of endpoints. I think it has something to do with CSRF, but I can't figure out what.
I serve the static files from views.py:
#api_view(['GET'])
def serve_html(request):
try:
with open(os.path.join(REACT_APP_DIR, 'build', 'index.html')) as f:
return HttpResponse(f.read())
except FileNotFoundError:
return HttpResponse(
"""
This URL is only used when you have built the production
version of the app. Visit http://localhost:3000/ instead, or
run `yarn run build` to test the production version.
""",
status=501,
)
My urls.py contains the catchall for the static files at the bottom:
urlpatterns = [
# ..all api paths
url(r'^', serve_html, name='serve_html'),
]
I use TokenAuthentication and SessionAuthentication:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
And the api requests that require users to be logged in have the following decorators:
#authentication_classes((SessionAuthentication, TokenAuthentication))
#permission_classes((IsAuthenticated,))
Addendum: I notice that when running it cross-origin, there is no sessionid cookie (and things work), but when I try to host react files on same-origin, there is a sessionid cookie (and 403 Forbidden is returned).
Sorry for the open-endedquestion, but I'm at my wit's end here; I really don't know why it's not working.
You do not require the SessionAuthentication class since you are using a token-based HTTP Authentication scheme. This should do the trick:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
)
}
Hope this helps.
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)
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!
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"))]
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
How can I add IsAdminUser permissions to /o/applications/* views in Django OAuth Toolkit?
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAdminUser',
)
}
in my settings.py doesn't work with Django OAuth Toolkit views, also I modify ApplicationList in my view.py doesn't work:
class ApplicationList:
permission_classes = (permissions.IsAdminUser,)
I'm kind of newby to Django and Python so I will be glad for your help