Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 37

API Token Authentication with DRF

Recommended resources

Hello! Welcome back to your journey into backend engineering.

In our last lesson, we contrasted stateful session-based authentication with stateless token-based authentication. We discussed the architectural trade-offs between them, noting that sessions offer easy server-side control while tokens provide superior scalability for modern APIs and microservices.

Today, we'll take our first practical step into the world of token authentication. As promised, we're going to implement Django REST Framework's built-in TokenAuthentication. This system is an excellent starting point because it uses a token that is stored in the database, making it a "stateful token" system. This bridges the gap between the session-based model you've learned about and the fully stateless JWT model we'll cover next.

By the end of this lesson, you will be able to implement this token system from scratch, create an endpoint for users to get their token, protect another endpoint that requires this token, and understand the complete client-server flow.

1. How DRF's TokenAuthentication Works

Before we write any code, let's understand the mechanism. Your extensive experience with data modeling in AI will make this straightforward.

At its core, DRF's TokenAuthentication relies on a simple database model:

  1. The Token Model: When you enable this feature, DRF creates a new table in your database (named authtoken_token). This table stores the authentication tokens.
  2. One-to-One Relationship: The Token model has a one-to-one relationship with your User model. This means each user can have exactly one authentication token associated with them. The token itself is just a long, randomly generated string.
  3. The Authentication Flow:
    • A client first obtains a token, typically by sending their username and password to a login endpoint.
    • The server validates the credentials and returns the user's unique token.
    • For all subsequent requests to protected parts of the API, the client includes this token in an HTTP header: Authorization: Token <your_token_string>.
    • When the server receives the request, DRF extracts the token string from the header and performs a database lookup in the authtoken_token table.
    • If a matching token is found, DRF identifies the associated user and grants access. If not, it denies access.

The crucial part here is the database lookup. This is what makes this system stateful, much like session-based authentication. The server must maintain a state (the list of valid tokens) in its database.

For an official overview of this mechanism, you can refer to the Django REST Framework documentation.

Authentication - TokenAuthentication

The official documentation provides the definitive explanation for TokenAuthentication. It covers the setup, how clients should format the Authorization header, and what credentials become available on the request object upon success.

Read the section titled 'TokenAuthentication'. Focus on the initial setup steps (INSTALLED_APPS), the example of creating a token programmatically, and the format of the Authorization HTTP header.


2. Step-by-Step Implementation

Now, let's build this system. We'll follow a three-part process: configuration, token generation, and endpoint protection.

Part 1: Setup and Configuration

First, we need to tell our Django project to use the token authentication app and set it as our default authentication method.

How to Implement Token Authentication using Django ... - Implementing the Token Authentication

This tutorial provides a clear, step-by-step guide to the initial setup. It's a great practical walkthrough of the configuration process.

Read the section 'Implementing the Token Authentication'. Follow the steps for modifying settings.py (adding 'rest_framework.authtoken' and configuring REST_FRAMEWORK) and running the python manage.py migrate command. You can stop before the 'createsuperuser' part.

To summarize the key steps from the article:

  1. Add rest_framework.authtoken to INSTALLED_APPS:
    In your settings.py, add 'rest_framework.authtoken' to the INSTALLED_APPS list. This app contains the Token model and other utilities we need.

    # settings.py
    INSTALLED_APPS = [
        ...
        'rest_framework',
        'rest_framework.authtoken', # <-- Add this
        ...
    ]
    
  2. Run Migrations:
    Execute python manage.py migrate. This command will see the new app and create the necessary authtoken_token table in your database.

  3. Set Default Authentication Class:
    In settings.py, configure DRF to use TokenAuthentication as the default scheme for your API.

    # settings.py
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'rest_framework.authentication.TokenAuthentication',
        ],
        # You might also have permission classes configured here
        'DEFAULT_PERMISSION_CLASSES': [
            'rest_framework.permissions.IsAuthenticated',
        ]
    }
    

With this configuration, DRF will now inspect every incoming request for a valid token in the Authorization header.

Part 2: Generating and Obtaining Tokens

A user needs a way to get their token. There are two primary, automated ways to handle this.

A) The Login Endpoint: obtain_auth_token

DRF provides a built-in view that you can easily add to your urls.py. This view accepts a POST request with a username and password and, if they are valid, returns the user's token.

Generating Authentication Tokens (Django Rest framework TokenAuthentication)

Let's see how to expose an API endpoint for users to request a token. This video demonstrates how to use DRF's built-in obtain_auth_token view.

Watch from 13:40 to 16:28. The key is adding obtain_auth_token to your urls.py. Notice how a POST request with the correct credentials results in a JSON response containing the token.

In your project's urls.py, you would add:

# urls.py
from django.urls import path
from rest_framework.authtoken.views import obtain_auth_token

urlpatterns = [
    ...
    path('api-token-auth/', obtain_auth_token, name='api_token_auth'),
]

A client can now send a POST request to /api-token-auth/ with their username and password to log in and receive their token.

B) Automatic Generation on User Creation

A very robust pattern is to automatically create a token for a user the moment their account is created (e.g., upon registration). This is elegantly handled using Django's signals. A signal allows certain actions (our "receivers") to be notified when specific events happen elsewhere in the application. In this case, we'll listen for the post_save signal on the User model.

Generating Authentication Tokens (Django Rest framework TokenAuthentication)

To make our system more seamless, we can automatically generate a token for every new user. This video explains how to use a post_save signal to achieve this. This is a common and powerful backend pattern.

Watch from 06:48 to 11:30. Focus on the code that defines the @receiver function. Understand that this function runs automatically every time a new User object is saved to the database.

You would add the following code, typically in your user management app's models.py file or a dedicated signals.py file:

# models.py or signals.py
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token

@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
    if created:
        Token.objects.create(user=instance)

This ensures every new user gets a token from the moment they sign up.

Part 3: Protecting an Endpoint and Using the Token

Now for the payoff. We have a way for users to get a token; let's create a protected API endpoint that requires it.

This is done by adding a permission class, IsAuthenticated, to a view. This class simply checks if the request has an authenticated user associated with it. Because we configured TokenAuthentication as our default, DRF will handle the token verification process for us.

Django Rest Framework Authentication 🔒 ✅ Scalable Auth in 27 minutes [2023]

Finally, let's see how to secure an endpoint. This segment shows how to use decorators to declare that a view requires authentication and how the client must present the token to gain access.

Watch from 24:07 to 27:07. Pay close attention to the @permission_classes([IsAuthenticated]) decorator. Observe how the Authorization: Token <key> header is constructed in the request and how the server code can access the authenticated user's details via request.user.

Here is an example of a protected view using function-based views with decorators:

# views.py
from rest_framework.decorators import api_view, permission_classes, authentication_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import TokenAuthentication
from rest_framework.response import Response

@api_view(['GET'])
@authentication_classes([TokenAuthentication]) # Explicitly state the auth method
@permission_classes([IsAuthenticated])       # State the required permission
def protected_view(request):
    content = {
        'message': f"Hello, {request.user.username}! You are seeing a protected view."
    }
    return Response(content)

If you set TokenAuthentication as the default in settings.py, you don't need the @authentication_classes decorator, but being explicit can improve clarity.

The complete flow for a client:

  1. POST to /api-token-auth/ with username and password.
  2. Receive a response: { "token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b" }.
  3. Store this token.
  4. To access /protected-view/, make a GET request and include the header:
    Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
  5. Receive a successful response from the protected view. If the header is missing or the token is invalid, the server will respond with a 401 Unauthorized error.
Test your understanding!

Imagine you have a user with the username test_dev and a valid token tok_a1b2c3d4. You have an endpoint at /api/status/ that is protected with IsAuthenticated.

You try to access it with the following curl command, but it fails with a 401 Unauthorized error:
curl -X GET http://127.0.0.1:8000/api/status/ -H 'Authorization: tok_a1b2c3d4'

What is wrong with this command, and how would you fix it?

Show answer

The problem is the format of the Authorization header's value. DRF's TokenAuthentication expects the value to be prefixed with the string literal "Token" followed by a space.

The corrected command is:

curl -X GET http://127.0.0.1:8000/api/status/ -H 'Authorization: Token tok_a1b2c3d4'

This specific format—Token <key>—is the standard for this authentication scheme.


Conclusion

Congratulations! You have now learned how to implement a complete, database-backed token authentication system in Django REST Framework. This is a fundamental skill for building secure APIs.

Key Takeaways:

  • DRF's TokenAuthentication is a stateful system that links a unique token stored in the database to each user.
  • Setup requires adding rest_framework.authtoken to INSTALLED_APPS, running migrate, and configuring TokenAuthentication as an authentication class.
  • Clients obtain a token by posting credentials to an endpoint, which can be easily created using the built-in obtain_auth_token view.
  • You can automate token creation for new users with Django signals (post_save).
  • API endpoints are protected by adding the IsAuthenticated permission class, and clients must send their token in the Authorization: Token <key> header to gain access.

Preview of the Next Lesson:
While TokenAuthentication is robust and simple, its reliance on a database lookup for every request can be a bottleneck in very high-traffic systems. In our next lesson, we will move to a truly stateless authentication mechanism by exploring JSON Web Tokens (JWT). You will learn about the structure of a JWT and why its self-contained, verifiable nature makes it so powerful for scalable, modern architectures.