Image Credit: Django REST Framework

Let’s build an API with Django REST Framework — Part 2

Emmanuel King Kasulani
Backticks & Tildes

--

Welcome back! In this second part, I will show you how to secure an API using two features that come with Django REST Framework out of the box; Authentication and Permissions. In case you missed the first part, click here to read it before proceeding on, because this is a continuation of what was discussed in that part.

In this part you are going to add restrictions to your simple music API. Let’s assume, for security reasons your API was no longer going to be public, and therefore you require consumers of your API to be registered users, then you are going to need to add mechanisms to ensure this is done. Those mechanisms, dear reader is none other than; Authentication, Authorization and Permissions.

So what’s Authentication?

Authentication is the mechanism of associating an incoming request with a set of identifying credentials, such as the user the request came from, or the token that it was signed with. Authentication by itself won’t allow or disallow an incoming request, it simply identifies the credentials that the request was made with. After the user is authenticated, DRF will check if the user is authorized to access the resource they are requesting for. I will be discussing more about authorization when am covering permissions later on.

Authentication is always run at the very start of the view, before the authorization checks occur, and before any other code is allowed to proceed.

For the music service API we developed in the first part, i will show you how to secure it using a token based authentication scheme. I will be using JSON Web Tokens (JWT) in for this scheme. The beauty of using JWT, is that you don’t need to save the token in a database.

Furthermore, JWTs provide a simple, yet powerful mechanism for securely transferring authentication information between a client and server. It’s also a compact URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature (JWS).

When using JWTs, make sure to expose your endpoints using the HTTPS scheme to protect the integrity of the data exchanged using JWT from man-in-the-middle attacks.

Let’s add JWT to the API

You start off by installing a third party package, that provides a JWT interface for the API. Open your CLI and enter the following command;

(venv)music_service$ pip install djangorestframework-jwt

After installing the djangorestframework-jwt package, you can now go ahead and open the api/settings.py file and add the JSONWebTokenAuthentication to Django REST Framework's DEFAULT_AUTHENTICATION_CLASSES.

Still inside the api/settings.py file, also add a dictionary with JWT settings. This assists in defining the defaults to use for JWT. Copy and paste the code below in your api/settings.py file before proceeding further.

At this point, you are done setting up JWT in your project and with this, you have also set the global authentication settings. These authentication settings will be applied on all views in your API, unless you over ride them on a view level.

Let’s now look at permissions in DRF.

Why do you need to set Permissions?

Together with authentication and throttling, permissions determine whether a request should be granted or denied access.

Permission checks are always run at the very start of the view, before any other code is allowed to proceed. Permission checks will typically use the authentication information in the request.user and request.auth properties to determine if the incoming request should be permitted.

Permissions are used to grant or deny access different classes of users to different parts of the API.

The simplest style of permission would be to allow access to any authenticated user, and deny access to any unauthenticated user. This corresponds the IsAuthenticated class in DRF.

Now let’s add permissions to the API. Open the api/settings.py file and add DjangoPermissionsOrnonReadOnly to Django REST framework’s DEFAULT_PERMISSION_CLASSES .

The DjangoModelPermissionsOrAnonReadOnly class not only applies the Django Permissions but also allows unauthenticated users to have read-only access to the API.

With this, you have set the global permissions settings. Global permissions will be applied to the details views in your API that correspond to endpoints such as songs/:id. You can also over ride the global settings on a view level. We shall see how this can later on when we set the permissions on the GET songs/ endpoint.

Let’s now code the view that will be used to login to allow users to use your API.

User login view

For your API, users get tokens only when they login successfully. You now need to add a view to handle authentication of the user when they attempt to login to use the API. This view will respond with a token to the client.

Write a test first

Before you can proceed to code the view, add a test as always.

Open the music/tests.py file and add the following lines of code;

First you need to update the BaseViewTest class as shown in the code snippet below;

Then add the AuthLoginUserTest that extends the new BaseViewTest as shown in the code snippet below;

Create the Login view

Since your view will respond with a token to the client, you will need a serializer to serialize this response for the client. In part 1, i explained why you need serializers for your views that respond with data.

To create a serializer for the token, Open the music/serializers.py file and add the following lines of code;

After creating the serializer, you can now proceed and create the login view;

Open the music/views.py file, and add the following lines of code;

Permissions: Like I mentioned before, In DRF, you can set the permission class at the view level. In the code snippet above, we set the permission_classes attribute of the LoginView to override the global settings. The AllowAny class permits any one to access this view publicly, since it’s a login view, you definitely want it to be accessed publicly.

Lastly, you need to set the permissions of the ListSongsView from part 1. Since you don’t want any one who’s not logged in to view the list of songs, you set the permission_classes attribute of the ListSongsView to IsAuthenticated. Your ListSongsView should now look like this;

Wire up the view

Before you can run the test, you will have to link the LoginView by configuring the urls.

Open the music/urls.py file and add the following line of code to the existing urlpatterns list.

Now let’s test!

First, let’s run automated tests. Run the command;

(venv)music_service$ python manage.py test

The out put in your shell should be similar to this;

Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.F
===================================================================
FAIL: test_get_all_songs (music.tests.GetAllSongsTest)-------------------------------------------------------------------Traceback (most recent call last):File "/your/dir/to/music_service/music/tests.py", line xxx, in test_get_all_songsself.assertEqual(response.data, serialized.data)AssertionError: {'detail': 'Authentication credentials we[13 chars]ed.'} != [OrderedDict([('title', 'like glue'), ('a[224 chars]')])]--------------------------------------------------------------------Ran 2 tests in 0.010sFAILED (failures=1)
Destroying test database for alias 'default'...

Why is the test failing?

Don’t panic! The devil is in the detail — Me :)

This is a good sign. The test we wrote in part 1 will fail. The reason is because you added permissions to the ListSongsView.

The response in the shell is always not the best to see the reason for failure. You fire up your browser and navigate to http://127.0.0.1:8000/api/v1/songs/ , you will see some thing like this below;

Look carefully at the text in red, it’s telling you something; Authentication credentials were not provided. This tells you that login is required now to view all songs, which was the goal of this part of the tutorial.

Let’s fix the test

Now, to fix your test from part 1, follow the comments in the code snippet below and do as they instruct.

Once you have updated you test code as shown above, your tests should now all be passing.

Bonus: User Register view

Now that you have view to login users, I think you will also need a view to register the users. As a bonus, I will show you how to add a view to be used by the API users to register.

This view will be open to the public as you don’t need to put restrictions on it, so you will set the permission_classes of the view to AllowAny .

Add a Test

Oh yes, as you usual, you have to write the test first.

Open the music/tests.py and add the following lines of code;

Create the register view

Now open the music/views.py and add the following lines of code;

Wire up the view

As always, before you can run the test, you will have to link the new view by configuring the urls.

Open the music/urls.py file and add the following line of code to the existing urlpatterns list.

With the urls setup, you can now run the test with python manage.py test command.

Take Away

As you can see, setting up security in DRF is very easy. You need minimum lines of code to do that.

You setup security in your API, by setting global settings to use for Authorization and Permissions.

You also setup security on a per view level.

For practice, you can go ahead and implement the remaining auth endpoints for the simple music service API listed in this table below;

For a complete implementation of the music service API, you can always go to my git hub repo, fork it and play around with it as you learn.

Additional resources

I recommend you read this resource to familiarize your self with other HTTP authentication schemes. It’s important that you understand them and get a feel of how security has evolved.

There’s also a great tool you can use to manually test your API endpoints called postman. You can install it on your development machine and experiment with it now.

I hope this was helpful to you and I want to thank you for reading this post. Hit clap in case it was helpful so that others may find it too. You can also suggest in the comment section how I can improve on this post to help you understand the Django REST Framework.

--

--