Django Rest Framework with React : JWT Authentication part 1

Django and React together? Can i have another plate please.

Keshav Vinayak Jha
The Startup
7 min readSep 4, 2019

--

Introduction

In this article we will learn about what JWT is and see a simple example of using this token to authenticate users. We will be using Django as our back-end and absorb our APIs with fetch API using React. This blog assumes basic working knowledge of both React and Django.

What is a JSON Web Token (JWT)

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object.

Simply put, JWT is an authentication token that can be divided into three subparts : Header, payload, Signature. It is represented as :

hhhh.pppp.ssss

The header contains information about the signing algorithm being used, the payload contains information about the entity or some additional statements. The encoded header, payload, underlying algorithm all come together with a signature to form the Signature part. You can experiment and create your own tokens on jwt.io .

Now that we’re done with the theoretical stuff, let’s jump right into the interesting implementation in Django!

JWT implementation using Django

Let’s initialise a virtualenv to host install fresh packages and then initialise our django project. Boot up a terminal at a preferable location and run

virtualenv --python=python3 djangoenvsource djangoenv/bin/activate

This should create and start a virtual environment configured with python3. Let’s create our django application now (assuming Django==2.1 installed). Also initialise an app, say, social_app.

django-admin startproject simple_rest
cd simple_rest
django-admin startapp social_app

Now let’s install the required packages into out environment.

pip install djangorestframework
pip install django-cors-headers
pip install djangorestframework-jwt

Don’t forget to add your dependencies and your app in your settings. Also, let’s not forget to add our CorsMiddleware, lest it comes back to haunt when dealing with React. The cors framework allows cross origin requests from react to django. This article gives you the required information you may need if you’re hearing about CORS for the very first time.

Let’s deal with our API default permissions and set them up for JWT authentication.

You can chose to either white list all (Definitely not recommended), or white-list just your react host, localhost with port 3000 being the default config.

CORS_ORIGIN_WHITELIST = (‘http://localhost:3000',)CORS_ALLOW_CREDENTIALS = True

While I recommend switching to postgres as your primary DB and straying far away from SQLite ( long post coming up for why ), it does not fall into the scope of this post. The following section is devoted to switching to postgres. Feel welcome to skip it, it’s completely optional and does not affect the rest of the tutorial.

SQLite to Postgres (Optional)

Install postgresql ≥ 11.0 on your machine and run the following commands :

sudo su - postgres
createdb jwt_db
psql

We’ve successfully initialised our Postgres database, leCORS_ALLOW_CREDENTIALS = Truet’s now create a password for it. Run \conninfo to get the connection information.

Install pgcli ( pip install pgcli ) to directly connect to your postgres database.

pgcli postgresql://postgres:example123@localhost:5432/jwt_db

Don’t forget to modify your DATABASES in settings.py

Great ! Now let’s put this database to use in our django app. I’d advise you to use global configurations or environment variables to hide such sensitive information as your database address, but for the sake of simplicity, let’s avoid that.

That concludes setting up postgres as our database. Onto the main article !

Writing APIs for fetching and registering users

Since we’re writing the APIs for the User model, which is predefined in django authentication, we are not required to define a new model, rather we can go ahead and describe our serializers (parsers that define how the APIs are to respond to requests). Let’s go ahead and define two serializers for the User model, one which is used when fetching all the details of the user while logging in, and one which is used while registering a new user. Also, notice that a token is not included in this API, as we do not need it every time we request a user’s data, just when logging in.

Next, create the serializer for handling post request for registering a new user. First, we mention the the password field as a write only field to have it stored in the database but prevent it from showing in the returned response. Afterwards, we override the default create method, which is done to make sure the password is hashed properly before storage.

class UserSerializerWithToken(sz.ModelSerializer):    password = sz.CharField(write_only=True)
token = sz.SerializerMethodField()
def get_token(self, object):
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(object)
token = jwt_encode_handler(payload)
return token def create(self, validated_data):
user = User.objects.create(
username = validated_data['username'],
first_name = validated_data['first_name'],
last_name = validated_data['last_name']
)
user.set_password(validated_data['password'])
user.save()
return user

class Meta:
model = User
fields = ('token', 'username', 'password', 'first_name',
'last_name')

Now, we needed a token to be created every time we request a login. By default, the User model does not include such token. So we create a custom serializerField by the name of ‘token’ and make use of get_ field with which we can include custom information in the serializer. the get_token() method makes use of the default jwt settings and encodes the user information (as discussed in the first section) into an equivalent jwt. Let’s move on to defining our views.

We need to define an endpoint from which we can fetch user information, if authenticated. Another view we would define is the Create User view which handles registration.

This is a functional view that only responds to a GET request. This view authenticates the incoming request from a specific user and fetches his/her information. Let’s use an APIView inherited class to define our Registration view.

class CreateUserView(APIView):
permission_classes = (permissions.AllowAny, )
def post(self,request):
user = request.data.get('user')
if not user:
return Response({'response' : 'error', 'message' : 'No data found'})
serializer = UserSerializerWithToken(data = user)
if serializer.is_valid():
saved_user = serializer.save()
else:
return Response({"response" : "error", "message" : serializer.errors})
return Response({"response" : "success", "message" : "user created succesfully"})

Our UserSerializerWithToken is used as a serializer for incoming requests at this endpoint. The response is taken, the details is extracted, the registration details is checked for validation errors, and then finally a user is created and a response message of success is returned.

You might be wondering about the exact urls these endpoints point to, let’s get to defining our api urls now, but before that, we’re missing an important feature.

Right now a user, when logged in, can request the authentication token, and fetch his own details separately. We need to return both his details and token when he requests a login to ease our definitions.

Create a utils.py file in your social_app directory, and define it as follows :

Next, we need to inform the jwt framework to use this response handler by default, add the following lines to your settings.py

JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER' : 'simple_rest.utils.custom_jwt_response_handler'
}

While we’re at it, let’s define our default login and login redirect urls as well.

LOGIN_URL = 'login'
LOGIN_REDIRECT_URL = 'home'

Let’s do the url definition in the following manner : Define the main url files in the core project folder, then create a url file within our main app(social_app), and define the registration url there.

Now we can finally move onto defining our api urls. Let’s start with the urls.py file in the main project directory ( simple_rest.urls )

The ‘token-auth/’ endpoint has basically become our login API, it returns in response relevant user information and the jwt token. The predefined obtain_jwt_token view by default returns only the token, but we had modified it to return user details as well. Create a views.py file within the main project directory and define it as follows :

from django.shortcuts import renderdef home(request):
return render(request,'home.html')

To make sure our templates are rendered correctly, do add the following line in your settings.py file

Now, define the urls.py file within the social_app directory,

Good job if you made it till here ! Let’s test out our APIs now.

First start up the DRF server using the following command within the directory of the manage.py file.

python manage.py runserver

Hopefully you do not face any errors while booting up the server.

Great ! Let’s send a request to register a new user.

We recieve the corresponding response as :

Let’s try to login with this user :

The corresponding response is :

Our API endpoints for registering a new user and obtaining the jwt token are working absolutely fine ! Moving forward, if you were to create more models and APIs which required authentication, you would want to store this token in the browser localStorage for further authentication. Anyway, the back-end setup is more or less done with for now.

What’s next ?

In this blog we explored how we can use the django rest framework along with JSON web tokens to create an authentication system. You can use any front-end framework to absorb these endpoints and create a web app for your own. In the next continuation of this blog, I’ll create a simple React front-end which will consist of basic registration and login forms. I hope I was able to help you understand the simplicity of using Django Rest Framework as a server. Thanks for the read !

The next part : https://medium.com/@keshavvinayakjha/django-rest-framework-with-react-jwt-authentication-part-2-8e272e866150

--

--