Setting up token authentication in React Native and Django

January 12, 2025

Prequisites

  • A React Native project
  • A Django project

Which authentication method should you use?

Every application is different, in general DRF states that SessionAuthentication should be used if the client is running in the same session context as your website. However, for mobile applications, TokenAuthentication is a better option for applications.

Once the user logs in, the server generates a token that the client stores securely. For every request to the backend, the client sends this token in the Authorization header. It’s stateless and works well for APIs, but you’ll need to handle token expiration and refresh mechanisms. Therefore, this is what we’re going to use.

Installing Django REST framework

First of all we need to install DRF, which can be done by running:

pip install djangorestframework

Remember that this should be run within your Python environment, in which case you may need to run source venv/bin/activate first. We also need to update our settings.py by adding:

INSTALLED_APPS = [
    ...
    'rest_framework',
    'rest_framework.authtoken',
]
 
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
}

We’re setting the both the default authentication class and permission classes, since we want each request that is sent to the server to be authenticated, with the exception for the login view.

Creating a LoginView

Now that we have the basics set up, we need to create a new LoginView. For this, we’ll be using a DRF GenericAPIView.

from django.contrib.auth import authenticate, login
from rest_framework import status
from rest_framework.authtoken.models import Token
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response

from account.serializers import LoginUserSerializer


class LoginView(GenericAPIView):
    serializer_class = LoginUserSerializer
    # We need to ensure that the default permission classes are not used
    authentication_classes = []
    permission_classes = []

    def post(self, request, *args, **kwargs):
        data = request.data
        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        authenticated_user = authenticate(
            email=data['email'],
            password=data['password'],
        )
        if not authenticated_user:
            return Response(status=401)
        login(request, authenticated_user)
        token, _ = Token.objects.get_or_create(user=authenticated_user) 
        return Response(
            status=200,
            data={
                **serializer.data,
                'token': token.key,
            },
        )

The LoginUserSerializer in this case is just a simple serializer for what the request data should contain:

from rest_framework import serializers


class LoginUserSerializer(serializers.Serializer):
    email = serializers.EmailField(required=True)
    password = serializers.CharField(write_only=True, required=True)

Now we’ve got everything set for logging in and getting a token.

Setting up React Native for success

I’m going to skip the frontend parts of this and focus on the few functions of importance.

To ensure that every request contains the header Authorization with the token, one way to make it simple is to modify the axios interceptors.

I’d suggest creating a new file, which you import every time you need to request anything from the backend.

Mine looks like this:

import axios, { AxiosInstance,InternalAxiosRequestConfig } from 'axios'
import { store } from '../store'

const ensureToken = (config: InternalAxiosRequestConfig) => {
    const state = store.getState()
    config.headers['Authorization'] = `Token ${state.user?.user?.token}`
    return config
}

const addInterceptors = (instance: AxiosInstance) => {
    instance.interceptors.request.use(ensureToken)
    return instance
}

const api = axios.create({
    baseURL: process.env.EXPO_PUBLIC_BASE_API_URL,
})

export default addInterceptors(api)

With this setup, the file can be imported whenever a backend request is required.

Wrap-up

By following this guide, you’ve established a solid foundation for integrating token-based authentication in your Django and React Native application. Here’s what you’ve achieved:

  1. Selected an Authentication Method: You chose TokenAuthentication as the ideal approach for mobile applications due to its stateless nature and compatibility with APIs.

  2. Configured Django REST Framework: You installed DRF and set up authentication and permissions in settings.py to secure your API endpoints effectively.

  3. Created a Login API: You implemented a LoginView using DRF’s GenericAPIView and crafted a simple serializer to handle user login and token generation.

  4. Enhanced the React Native Client: By setting up an Axios instance with interceptors, you ensured that every request from the client includes the user’s authentication token seamlessly.

With these components in place, your application is ready to securely handle user authentication and API interactions. Remember to implement additional features such as token expiration handling and refresh mechanisms to further enhance security and user experience. Happy coding!


Profile picture

Written by An anonymous coder who lives and works building useful things. Mostly focusing on Django, Vue and overall Python examples.