Adding Authentication to a REST Framework Django API
Tags: django, python, tutorial
Published: 2020-03-09 21:25:45

In this article, we'll be adding user authentication to an already existing REST API. This is a continuation of a previous article where we built a simple Bookstore Django REST API with just one endpoint that sends a response `{"message": "Welcome to the BookStore!"}`. 
Want to [Create a simple REST API with Django](https://dev.to/nobleobioma/create-a-simple-rest-api-with-django-253p)?

At the end of this article, 
- User can register
- User can log in
- TokenBased Authentication
- User can Logout

## Let's get started 😀

First, change directory into the project and spin up our environment:

```bash
$ cd bookstore
$ pipenv shell
```

Now, let's add some packages to our environment that will handle our authentication:

```bash
$ pipenv install django-rest-auth django-allauth
```

We've added some new packages, let's make our Django app aware of these authentication packages and add some functionalities too. To do this,

```python
# ./bookstore_app/settings.py
...
INSTALLED_APPS = [
    ...
    'rest_framework.authtoken',
    'rest_auth',
    'django.contrib.sites',
    'allauth',
    'allauth.account',
    'rest_auth.registration',
]
```

Because Django tries to send an email e.g when a user creates an email account, we need to add a little config `EMAIL_BACKEND` and `SITE_ID` to the `settings.py` file. This prints the email to be sent to the console.

```python
# ./bookstore_app/settings.py
...
INSTALLED_APPS = [
    ...
]

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
SITE_ID = 1

MIDDLEWARE = [
    ...
]

...
```
Next, we'll add the URLs from the `rest_auth` package to the project's `urls.py` file. This gives us access to a host of auth functionalities.

- User Registration - registration/
- User Login - login/
- User Logout - logout/
- User Details - user/
- Change Password - password/change/
- Password Reset - password/reset/
- Confirm Password Reset - password/reset/confirm/

```python
# ./bookstore_app/urls.py

urlpatterns = [
    ...
    path('', include('rest_auth.urls')),
    path('registration/', include('rest_auth.registration.urls')),
]
```

After adding the URLs, let's now set some `Authentication Scheme` and 
`Permission Policies`. What file are we going to be setting these?? 🤔

NICE!!!! That will be the `settings.py` file. Thank you for thinking about it for me. 😊

```python
# ./bookstore_app/settings.py
...

REST_FRAMEWORK = {
    # Authentication Scheme
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    ),
    # Permission Policies
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ]
}

```  
Now, let's run our migrations:

```bash
$ python manage.py migrate
```

Let's add some restrictions to the `welcome` viewset in the `views.py` file so that unauthorized users would not have access.

```python
# ./bookstore_app/api/views.py

from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt

@api_view(["GET"])
@csrf_exempt
@permission_classes([IsAuthenticated])  
def welcome(request):
    content = {"message": "Welcome to the BookStore!"}
    return JsonResponse(content)
```

Finally, the restriction is set and an unauthenticated user cannot have access.

Now, let's test our endpoints. We'll create a user, log in and access the `welcome` endpoint. You can use [Postman](https://www.postman.com/) to test with the same JSON properties, but I'll be using [curl](https://curl.haxx.se/).

##### Registration
```bash
> Request

$ curl -X POST -H "Content-Type: application/json" -d '{"username":"testuser", "password1":"testpassword", "password2":"testpassword"}' localhost:8000/registration/
```
```bash
> Response:
   {"key":"1565c60a136420bc733b10c4a165e07698014acb"}
```

Creating a user and Logging in returns a token that can be used to uniquely identify a user so we'll skip login and use the token to access the welcome endpoint that we added the restriction to.

##### Welcome endpoint with Token
```bash
> Request
$ curl -X GET -H 'Authorization: Token 1565c60a136420bc733b10c4a165e07698014acb' localhost:8000/api/welcome
```
```bash
> Response
   {"message": "Welcome AH_Bookstore App!"}
```
##### Welcome endpoint without Token
```bash
> Request
$ curl -X GET -H '' localhost:8000/api/welcome
```
```bash
> Response
   {"detail":"Authentication credentials were not provided."}
```

Finally, our authentication works fine. Next, we'll be adding CRUD functionality to our app and creating some models. 

Thank you for reading. 😊