

Django Rest Framework authentication: the easy way
source link: https://www.guguweb.com/2022/01/23/django-rest-framework-authentication-the-easy-way/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

In this tutorial you'll learn how to implement Django Rest Framework authentication in your web application by leveraging the built-in Django session framework.
This approach is way simpler (and secure) than other popular methods such as JWT, and has only one requirement: your frontend (think Vue.js, React, ...) and your backend should be served by the same domain.
I created an example project on GitHub you can use to follow along with this tutorial.
Why you should avoid JWT for Django Rest Framework authentication
JWT (Json Web Token) is a very popular method to provide authentication in APIs. If you are developing a modern web application with Vue.js or React as the frontend and Django Rest Framework as the backend, there is an high probability that you are considering JWT as the best method to implement authentication.
The reality is that JWT is just one method, and unfortunately not the simpler, nor the most reliable. JWT is not supported out-of-the-box in Django Rest Framework and requires additional libraries and additional configuration for your project.
Also, implementing JWT in a secure way is quite challenging, and this is due to its complex design. Quoting the words of James Bennet, a long time Django project contributor:
"JWT is over-complex, puts too much power in the attacker's hands, has too many configuration knobs, and makes poor cryptographic choices. This is why we see vulnerabilities in JWT libraries and JWT-using systems again and again and again."
Here is some examples of JWT vulnerabilities found in the wild.
The good news is that if you can control the domain of both your backend and your frontend, you can use a much simpler method: Django sessions.
Django Rest Framework settings
Django Rest Framework comes with built-in session based authentication. To use it you have to add this in your Django settings module:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}
Remember that authentication deals with recognizing the users that are connecting to your API, while permissions deals with giving access to some resources to the users.
In the DEFAULT_AUTHENTICATION_CLASSES
list you are configuring only the good old Django sessions to authenticate users. In the DEFAULT_PERMISSION_CLASSES
list you are requiring that only authenticated users will access your endpoints.
Where is my token?
Django sessions are based by default on a session cookie stored on the client. There's no need for a "Token", an "Authorization" header or something like that.
If you can store that session cookie on the client and send it on every request to your API you will authenticate the user.
Django Rest Framework authentication endpoint
Now it's time to write a very simple view to let the users authenticate with a username/password.
We'll need a serializer that will take the username and password from the request and will perform the actual authentication using Django authenication framework.
Create a serializers.py
file in your app and add this code to it:
from django.contrib.auth import authenticate
from rest_framework import serializers
class LoginSerializer(serializers.Serializer):
"""
This serializer defines two fields for authentication:
* username
* password.
It will try to authenticate the user with when validated.
"""
username = serializers.CharField(
label="Username",
write_only=True
)
password = serializers.CharField(
label="Password",
# This will be used when the DRF browsable API is enabled
style={'input_type': 'password'},
trim_whitespace=False,
write_only=True
)
def validate(self, attrs):
# Take username and password from request
username = attrs.get('username')
password = attrs.get('password')
if username and password:
# Try to authenticate the user using Django auth framework.
user = authenticate(request=self.context.get('request'),
username=username, password=password)
if not user:
# If we don't have a regular user, raise a ValidationError
msg = 'Access denied: wrong username or password.'
raise serializers.ValidationError(msg, code='authorization')
else:
msg = 'Both "username" and "password" are required.'
raise serializers.ValidationError(msg, code='authorization')
# We have a valid user, put it in the serializer's validated_data.
# It will be used in the view.
attrs['user'] = user
return attrs
Then we can use this serializer in a login view. Add this to your views.py
file:
from rest_framework import permissions
from rest_framework import views
from rest_framework.response import Response
from . import serializers
class LoginView(views.APIView):
# This view should be accessible also for unauthenticated users.
permission_classes = (permissions.AllowAny,)
def post(self, request, format=None):
serializer = serializers.LoginSerializer(data=self.request.data,
context={ 'request': self.request })
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
login(request, user)
return Response(None, status=status.HTTP_202_ACCEPTED)
Mount your view in the project urls.py
:
from django.contrib import admin
from django.urls import path
from users import views
urlpatterns = [
path('admin/', admin.site.urls),
path('login/', views.LoginView.as_view()),
]
Finally you can migrate the database, create a test user and launch the Django development server:
$ ./manage.py migrate
$ ./manage.py createsuperuser
(... follow instructions ...)
$ ./manage.py runserver
Test authentication using HTTPie
I really like HTTPie command line HTTP client. Maybe because it is written in Python and based on Python requests. :)
You can install it using
$ pip install httpie
It is one of the few packages that I like to install system wide.
After installing HTTPie, let's test a wrong login:
$ http POST :8000/login/ username=guguweb password=wrongpassword
HTTP/1.1 400 Bad Request
( ... some other response headers ... )
{
"non_field_errors": [
"Access denied: wrong username or password."
]
}
As you can see the error message is returned and the user is not logged in.
Now let's try with the right credentials:
$ http POST :8000/login/ username=guguweb password=mysecret
HTTP/1.1 202 Accepted
( ... some other response headers ... )
Set-Cookie: csrftoken=ALo4uq(...)lLoayts; expires=Tue, 24 Jan 2023 11:00:26 GMT; Max-Age=31449600; Path=/; SameSite=Lax
Set-Cookie: sessionid=l5p5zqjkhuijhjdb84; expires=Tue, 08 Feb 2022 11:00:26 GMT; HttpOnly; Max-Age=1209600; Path=/;
The user is correctly logged in and a session cookie named sessionid
has been returned to our client.
If we will persist that session cookie in each request, our user will be persistently authenticated.
A new endpoint to retrieve the user profile
Let's write a view that will let the user to retrieve his user profile. Of course this view will require an already authenticated user.
First of all we need a new serializer that will be used to return the profile information. Add this to the serializers.py
file introduced earlier:
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
'username',
'email',
'first_name',
'last_name',
]
Now you can add a view to retrieve the user profile. Remember that we added rest_framework.permissions.IsAuthenticated
in our DEFAULT_PERMISSION_CLASSES
setting, so each view will require that the user is authenticated, unless otherwise specified.
Add this to your views.py
file:
class ProfileView(generics.RetrieveAPIView):
serializer_class = serializers.UserSerializer
def get_object(self):
return self.request.user
And mount the view to an endpoint in your urls.py
file:
urlpatterns = [
...
path('profile/', views.ProfileView.as_view()),
]
Test the user profile endpoint using HTTPie
Let's test a unauthenticated call to our new endpoint:
$ http GET :8000/profile/
HTTP/1.1 403 Forbidden
( ... some other response headers ... )
{
"detail": "Authentication credentials were not provided."
}
The endpoint is correctly protected from unauthenticated requests. Now let's try to add an header with our session cookie.
Where I can find the session cookie value?
You can copy the sessionid value from the previous login request, it was something like this Set-Cookie: sessionid=l5p5zqjkhuijhjdb84;
$ http GET :8000/profile/ Cookie:sessionid=l5p5zqjkhuijhjdb84
HTTP/1.1 200 OK
( ... some other response headers ... )
{
"email": "[email protected]",
"first_name": "Augusto",
"last_name": "Destrero",
"username": "guguweb"
}
Conclusions
In this tutorial you learned how to implement Django Rest Framework authentication in your web application by leveraging the built-in Django session framework.
This approach assumes that you can control the domain of both your frontend and your backend, and the two layers of the web application can be served from the same top level domain. If this is not the case, you cannot rely on browser cookies to persist the user session between subsequent requests.
In many cases this assumption is reasonable and this approach is way simper to configure and implement with respect to other token based approaches.
Don't forget to clone/fork my example project on GitHub to actually see this project running.
In a follow up post I will show you how to implement a simple Vue.js based application that will connect to the REST API implemented in this tutorial.
Have fun with Django Rest Framework!
Recommend
-
15
Django REST Framework中把DatetimeField转换为时间戳 2019-07-21 15:32:11 +08 字数:870 标签: Django ...
-
10
Live Coding: Learning Django REST FrameworkSchedule time with mepowered by Calendly
-
9
Building With Django REST Framework REST is a loosely defined protocol for listing, creating, changing, and deleting data on your server over HTTP. The Django REST framework (DRF) is a toolkit built on top of the Django web framewor...
-
9
...
-
9
自动生成接口文档 2020-09-23 2 1807 大多数情况下,开发的接口都不是给开发这个接口的人用的,所以...
-
7
A web developer worthy of the name must be able to create a REST API. This article will help you understand everything there is to know to build your first API using the Python language and the Django REST Framework. To not miss anyth...
-
5
In this tutorial we are going to build API as a data source for a DataTable jQuery plugin. Introduction There is this awesome plugin I have used recently for displaying and sorting data
-
6
Integrate Axios with Django Rest Framework Do you need to integrate the Axios HTTP client with Django Rest Framework? Then make sure to correctly configure the Django built-in Cross Site Request Forgery protection. TLDR: add th...
-
3
Beginner's Guide to the Django Rest Framework So you're learning to use the Django Web Framework and you're loving it. But you want an attractive, easy to use API for your application? Look no further than the
-
6
Django-REST-framework中文文档教程 2016-06-27编程技术 目前网上的关于Django-REST-framew...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK