Django REST Framework Authentication: JWT, OAuth2, and Session

by Didin J. on Sep 20, 2025 Django REST Framework Authentication: JWT, OAuth2, and Session

Learn Django REST Framework authentication with Session, JWT, and OAuth2. Step-by-step guide to secure APIs for web, SPA, and mobile apps.

Authentication is one of the most important aspects of any web application or REST API. Without a secure authentication mechanism, your API is vulnerable to unauthorized access, data leaks, and potential security breaches. Django REST Framework (DRF) provides flexible tools to implement authentication, and depending on your use case, you may choose different strategies.

In this tutorial, we’ll explore three common authentication methods in DRF:

  • Session Authentication – Uses Django’s built-in session management and cookies. Great for traditional web apps or when your frontend is tightly coupled with Django.

  • JWT (JSON Web Token) Authentication – A stateless token-based approach. Popular in modern single-page applications (React, Angular, Vue) and mobile apps.

  • OAuth2 Authentication – An industry-standard protocol used when integrating with third-party apps or when you need fine-grained token scopes.

By the end of this tutorial, you’ll be able to:

  • Set up a Django REST Framework project from scratch

  • Implement Session Authentication for browser and API clients

  • Configure JWT Authentication using djangorestframework-simplejwt

  • Add OAuth2 Authentication using django-oauth-toolkit

  • Secure API endpoints so only authenticated users can access them

  • Test each authentication method with tools like Postman or curl

💡 Choosing the right authentication method depends on your project’s needs:

  • Use Session Auth if your frontend is Django/Template-based.

  • Use JWT Auth for stateless APIs with modern SPAs or mobile apps.

  • Use OAuth2 for integrations, microservices, or when exposing APIs to external clients.


Prerequisites

Before diving into the code, make sure you have the following tools and knowledge in place:

Technical Requirements

  • Python 3.12+ installed on your system

  • Pip for installing Python packages

  • Virtual environment (recommended: venv or virtualenv) to keep dependencies isolated

  • Django 5+ and Django REST Framework (DRF) 3.16+

  • djangorestframework-simplejwt for JWT authentication

  • django-oauth-toolkit for OAuth2 authentication

Basic Knowledge

  • Familiarity with Python programming

  • Basic understanding of Django (models, views, settings)

  • Understanding of REST APIs (endpoints, HTTP methods, status codes)

  • Some exposure to authentication concepts (tokens, sessions, headers)

Tools You’ll Use

  • Postman or curl for testing API requests

  • Text Editor/IDE such as VS Code, PyCharm, or Sublime Text

  • SQLite (default in Django) – no additional database setup required

With these prerequisites covered, we’re ready to set up our Django project and start building authentication step by step.


Setting Up the Django Project

In this section, we’ll create a new Django project, install the required dependencies, and prepare our environment.

Step 1: Create and Activate a Virtual Environment

It’s always best to work in an isolated Python environment.

# Create a new project folder
mkdir drf_auth_demo && cd drf_auth_demo

# Create virtual environment
python3 -m venv venv

# Activate it
# On macOS/Linux:
source venv/bin/activate

# On Windows:
venv\Scripts\activate

Step 2: Install Django and Required Packages

Now, install Django REST Framework and the authentication libraries we’ll use.

pip install django djangorestframework djangorestframework-simplejwt django-oauth-toolkit

Optional but recommended: install drf-yasg or drf-spectacular if you want auto-generated API docs later.

Step 3: Create a New Django Project

Let’s create a new project named drf_auth_demo.

django-admin startproject drf_auth_demo .

This creates the Django project structure in the current folder.

Step 4: Create an App for Authentication

We’ll use a dedicated app called accounts to handle users and authentication logic.

python manage.py startapp accounts

Step 5: Update settings.py

Open drf_auth_demo/settings.py and add the following changes:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # Third-party
    'rest_framework',
    'oauth2_provider',  # for OAuth2

    # Local
    'accounts',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',  # Session
        'rest_framework_simplejwt.authentication.JWTAuthentication',  # JWT
        'oauth2_provider.contrib.rest_framework.OAuth2Authentication',  # OAuth2
    ],
}

Step 6: Run Initial Migrations

Apply initial migrations to set up the database.

python manage.py migrate

Now your project is ready, with DRF and authentication backends configured.


User Model and Serializer

Django already comes with a powerful built-in User model, which we can use directly for authentication. For this tutorial, we’ll stick with Django’s default User, but you could extend it later if your project requires custom fields.

Step 1: Create a Serializer for the User

Inside your accounts app, create a new file accounts/serializers.py and add the following:

from django.contrib.auth.models import User
from rest_framework import serializers


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        # We only expose basic user fields for now
        fields = ['id', 'username', 'email', 'is_staff']

This serializer will be useful when returning user details via our API.

Step 2: Create Views for User Registration and Profile

Inside accounts/views.py, let’s add a simple registration endpoint and a profile view.

from django.contrib.auth.models import User
from rest_framework import generics, permissions
from rest_framework.response import Response
from .serializers import UserSerializer


# User Registration View
class RegisterView(generics.CreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [permissions.AllowAny]

    def perform_create(self, serializer):
        # Ensure password is hashed
        password = self.request.data.get("password")
        user = serializer.save()
        if password:
            user.set_password(password)
            user.save()


# User Profile (requires authentication)
class ProfileView(generics.RetrieveAPIView):
    serializer_class = UserSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_object(self):
        return self.request.user

Here’s what’s happening:

  • RegisterView: Allows anyone to create a new user account. Passwords are hashed properly.

  • ProfileView: Returns the authenticated user’s details.

Step 3: Define URLs for the Endpoints

Inside accounts/urls.py (create this file if it doesn’t exist):

from django.urls import path
from .views import RegisterView, ProfileView

urlpatterns = [
    path('register/', RegisterView.as_view(), name='register'),
    path('profile/', ProfileView.as_view(), name='profile'),
]

Now, include this in the main project drf_auth_demo/urls.py:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/accounts/', include('accounts.urls')),
]

Step 4: Test the User Endpoints

Run the server:

python manage.py runserver

Test with Postman or curl:

1. Register a new user

curl -X POST http://127.0.0.1:8000/api/accounts/register/ \
  -H "Content-Type: application/json" \
  -d '{"username":"testuser","email":"[email protected]","password":"testpass123"}'

2. Check profile (requires login/session, will be fully tested later)

curl -X GET http://127.0.0.1:8000/api/accounts/profile/

At this stage, the user registration and profile endpoints are ready, which will integrate seamlessly with Session, JWT, and OAuth 2.0 authentication methods.


Session Authentication

Django’s Session Authentication relies on cookies. When a user logs in, Django stores the session ID in the database and sends a cookie to the client. For subsequent requests, the client includes this cookie, and Django uses it to identify the logged-in user.

This works seamlessly for browser-based apps but can also be used for APIs.

Step 1: Enable Session Authentication in Settings

We already added SessionAuthentication in settings.py under REST_FRAMEWORK. Django’s session middleware is enabled by default, so no further setup is needed here.

Step 2: Add Login and Logout Views

Inside accounts/views.py, add login and logout endpoints.

from django.contrib.auth import authenticate, login, logout
from rest_framework.views import APIView
from rest_framework import status


class LoginView(APIView):
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        username = request.data.get("username")
        password = request.data.get("password")

        user = authenticate(request, username=username, password=password)
        if user is not None:
            login(request, user)
            return Response({"message": "Login successful"}, status=status.HTTP_200_OK)
        return Response({"error": "Invalid credentials"}, status=status.HTTP_400_BAD_REQUEST)


class LogoutView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request):
        logout(request)
        return Response({"message": "Logged out successfully"}, status=status.HTTP_200_OK)

Step 3: Add URLs for Login and Logout

Update accounts/urls.py:

from .views import RegisterView, ProfileView, LoginView, LogoutView

urlpatterns = [
    path('register/', RegisterView.as_view(), name='register'),
    path('profile/', ProfileView.as_view(), name='profile'),
    path('login/', LoginView.as_view(), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
]

Step 4: Test Session Authentication

Run the server:

python manage.py runserver

1. Log in and create a session

curl -X POST http://127.0.0.1:8000/api/accounts/login/ \
  -H "Content-Type: application/json" \
  -d '{"username":"testuser","password":"testpass123"}' \
  -c cookies.txt

2. Access profile with session cookie

curl -X GET http://127.0.0.1:8000/api/accounts/profile/ \
  -b cookies.txt

3. Log out and invalidate the session

curl -X POST http://127.0.0.1:8000/api/accounts/logout/ \
  -b cookies.txt

✅ At this point, you’ve implemented Session Authentication for your DRF API. This is a solid choice if Django templates render your frontend or if cookies are acceptable in your client environment.


JWT Authentication

JWT (JSON Web Token) is a compact and self-contained way of securely transmitting information between parties as a JSON object. With djangorestframework-simplejwt, we can easily integrate JWT into our DRF project.

Step 1: Install and Configure SimpleJWT

We already installed djangorestframework-simplejwt earlier. If not, install it now:

pip install --upgrade pip

Update settings.py to configure JWT:

from datetime import timedelta

SIMPLE_JWT = {
    "ACCESS_TOKEN_LIFETIME": timedelta(minutes=30),
    "REFRESH_TOKEN_LIFETIME": timedelta(days=1),
    "ROTATE_REFRESH_TOKENS": True,
    "BLACKLIST_AFTER_ROTATION": True,
    "AUTH_HEADER_TYPES": ("Bearer",),
}

Step 2: Add JWT Views

simplejwt come with built-in views for obtaining, refreshing, and verifying tokens.
In drf_auth_demo/urls.py, add:

from django.urls import path, include
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
    TokenVerifyView,
)

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/accounts/', include('accounts.urls')),

    # JWT endpoints
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
]

Step 3: Test JWT Authentication

1. Obtain an Access and Refresh Token

curl -X POST http://127.0.0.1:8000/api/token/ \
  -H "Content-Type: application/json" \
  -d '{"username":"testuser","password":"testpass123"}'

Response:

{
  "refresh": "eyJ0eXAiOiJKV1QiLCJh... (long string)",
  "access": "eyJ0eXAiOiJKV1QiLCJh... (long string)"
}

2. Access Protected Endpoint with Bearer Token

curl -X GET http://127.0.0.1:8000/api/accounts/profile/ \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

3. Refresh the Token

curl -X POST http://127.0.0.1:8000/api/token/refresh/ \
  -H "Content-Type: application/json" \
  -d '{"refresh":"<REFRESH_TOKEN>"}'

4. Verify Token

curl -X POST http://127.0.0.1:8000/api/token/verify/ \
  -H "Content-Type: application/json" \
  -d '{"token":"<ACCESS_TOKEN>"}'

✅ Now your API supports JWT Authentication. Clients can log in once, get a token, and then use that token in the Authorization header for all future requests.


OAuth2 Authentication

We’ll use django-oauth-toolkit (DOT), which is the go-to package for adding OAuth2 support to Django REST Framework.

Step 1: Install and Add to INSTALLED_APPS

If you haven’t already, install it:

pip install django-oauth-toolkit

In settings.py, we already added it earlier, but just to confirm:

INSTALLED_APPS += [
    'oauth2_provider',
]

Also update REST Framework authentication classes (we already did in Section 3, but here’s the relevant part):

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework_simplejwt.authentication.JWTAuthentication',
        'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
    ],
}

Step 2: Add OAuth2 URLs

In drf_auth_demo/urls.py, include the OAuth2 endpoints:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/accounts/', include('accounts.urls')),

    # JWT
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),

    # OAuth2
    path('o/', include('oauth2_provider.urls', namespace='oauth2_provider')),
]

This automatically adds endpoints like:

  • /o/applications/ – manage OAuth2 clients

  • /o/token/ – obtain tokens

  • /o/revoke_token/ – revoke tokens

Step 3: Apply Migrations

Run:

python manage.py migrate

Step 4: Create an OAuth2 Application

Run the dev server and log in to the admin panel:

python manage.py createsuperuser
python manage.py runserver

Go to http://127.0.0.1:8000/adminApplications (under OAuth2 Provider) → Add Application.

Fill in the fields:

  • Name: MyAppClient

  • Client Type: Confidential

  • Authorization Grant Type: Resource owner password-based

  • Redirect URIs: (leave blank for password grant)

Save it, and copy the Client ID and Client Secret.

Step 5: Obtain an OAuth2 Token

Now, request a token using the Password Grant:

curl -X POST http://127.0.0.1:8000/o/token/ \
  -d "grant_type=password" \
  -d "username=testuser" \
  -d "password=testpass123" \
  -d "client_id=lDu3WFYW10NOc1AdG3L2Mmh6JJtCZu41cTSx82LR" \
  -d "client_secret=7yvXRV4NKGly4qOTiBptMmLCDQSzzmTKjnY0CGv3NX6ucpA58pxBTqzLSFHCmbJLqMVaa12mqFzxdGJCEaZpBKINo8nd63FZlZ7fPFxFS2nAsjaVVA9cUceiAmT0TVse"

Response:

{
  "access_token": "2YotnFZFEjr1zCsicMWpAA...",
  "expires_in": 36000,
  "token_type": "Bearer",
  "scope": "read write",
  "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA..."
}

Step 6: Access Protected Endpoint with OAuth2 Token

curl -X GET http://127.0.0.1:8000/api/accounts/profile/ \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

✅ Your API now supports OAuth2 Authentication! 🎉


Protecting API Endpoints

At this point, we have three authentication methods configured. Now, let’s see how to restrict access to certain endpoints so only authenticated users can use them.

Step 1: Apply DRF Permissions

Django REST Framework provides several permission classes. The most common is IsAuthenticated, which ensures the request is made by a logged-in or valid-token user.

In accounts/views.py, we already used:

from rest_framework import permissions

class ProfileView(generics.RetrieveAPIView):
    serializer_class = UserSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_object(self):
        return self.request.user

This means:

  • With Session Auth → user must be logged in via /login/.

  • With JWT Auth → client must provide Authorization: Bearer <ACCESS_TOKEN>.

  • With OAuth2 Auth → client must provide Authorization: Bearer <OAUTH2_ACCESS_TOKEN>.

Step 2: Custom Permissions (Optional)

You can create more fine-grained permissions. For example, only staff users can view all users:

from rest_framework.permissions import BasePermission

class IsAdminUser(BasePermission):
    def has_permission(self, request, view):
        return request.user and request.user.is_staff

Then apply it:

class UserListView(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsAdminUser]

Step 3: Test Each Authentication Method

  1. Session Authentication

    • Log in via /api/accounts/login/ (cookie stored).

    • Access /api/accounts/profile/ with saved session cookie → ✅ Success.

  2. JWT Authentication

    • Obtain a token from /api/token/.

    • Access /api/accounts/profile/ with Authorization: Bearer <ACCESS_TOKEN> → ✅ Success.

  3. OAuth2 Authentication

    • Obtain a token from /o/token/.

    • Access /api/accounts/profile/ with Authorization: Bearer <ACCESS_TOKEN> → ✅ Success.

If you try without authentication, DRF will return:

{"detail": "Authentication credentials were not provided."}

✅ Now your API endpoints are protected by all three authentication methods. You can choose which one fits your project best or support multiple at the same time.


Testing the Authentication Methods

In this section, we’ll simulate API requests for Session, JWT, and OAuth2 authentication using curl. You can also test these with Postman.

1. Session Authentication

  1. Log in and store the session cookie

 
curl -X POST http://127.0.0.1:8000/api/accounts/login/ \
  -H "Content-Type: application/json" \
  -d '{"username":"testuser","password":"testpass123"}' \
  -c cookies.txt

 

  • The -c cookies.txt option saves the session cookie.

  1. Access the protected endpoint using the session

 
curl -X GET http://127.0.0.1:8000/api/accounts/profile/ \
  -b cookies.txt

 

  • You should see your user details returned.

  1. Logout and invalidate session

 
curl -X POST http://127.0.0.1:8000/api/accounts/logout/ \
  -b cookies.txt

 

2. JWT Authentication

  1. Obtain JWT token

 
curl -X POST http://127.0.0.1:8000/api/token/ \
  -H "Content-Type: application/json" \
  -d '{"username":"testuser","password":"testpass123"}'

 

  • Response includes access and refresh tokens.

  1. Access protected endpoint using JWT

 
curl -X GET http://127.0.0.1:8000/api/accounts/profile/ \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

 

  1. Refresh the JWT token

 
curl -X POST http://127.0.0.1:8000/api/token/refresh/ \
  -H "Content-Type: application/json" \
  -d '{"refresh":"<REFRESH_TOKEN>"}'

 

3. OAuth2 Authentication

  1. Obtain an OAuth2 token (Password Grant)

 
curl -X POST http://127.0.0.1:8000/o/token/ \
  -d "grant_type=password" \
  -d "username=testuser" \
  -d "password=testpass123" \
  -d "client_id=<CLIENT_ID>" \
  -d "client_secret=<CLIENT_SECRET>"

 

  • Response includes access_token and refresh_token.

  1. Access protected endpoint using an OAuth2 token

 
curl -X GET http://127.0.0.1:8000/api/accounts/profile/ \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

 

4. Observations

Method Storage Token Expiration Ideal Use Case
Session Cookies Server-controlled Traditional web apps
JWT Client-side Configurable (SIMPLE_JWT) SPAs, mobile apps
OAuth2 Client-side Configurable

Third-party integrations, API marketplaces

💡 Tip: You can support multiple authentication methods simultaneously, as DRF will try them in order until one succeeds.

✅ With these tests, your readers can verify each authentication method works as intended and understand the differences in flow.


Conclusion + Best Practices

In this tutorial, we explored three authentication methods for Django REST Framework: Session Authentication, JWT Authentication, and OAuth2 Authentication. You now know how to implement, test, and secure your APIs using each method.

Key Takeaways

  • Session Authentication

    • Uses Django’s built-in session and cookie mechanisms.

    • Best for traditional server-rendered web apps.

    • Automatically handles CSRF protection.

  • JWT Authentication

    • Stateless and works well for SPAs and mobile clients.

    • Tokens contain user info and can be verified without querying the database.

    • Requires careful handling of token expiration and storage on the client.

  • OAuth2 Authentication

    • Industry-standard for secure delegated access.

    • Supports multiple grant types (Password, Client Credentials, Authorization Code).

    • Ideal for third-party integrations and microservices.

Best Practices

  1. Always use HTTPS – Never transmit tokens or credentials over plain HTTP.

  2. Set appropriate token lifetimes – Short-lived access tokens reduce risk if leaked.

  3. Use refresh tokens securely – Store them safely and rotate them if possible.

  4. Apply proper permissions – Always protect endpoints with IsAuthenticated or custom permissions.

  5. Combine authentication methods wisely – You can support multiple methods, but be clear about which clients use which method.

  6. Log out users properly – Invalidate sessions or tokens when necessary.

By following this guide, you now have a flexible, secure, and fully tested authentication system for your Django REST Framework APIs, ready for production or integration with SPAs, mobile apps, or third-party clients.

✅ Next steps: Consider integrating frontend frameworks (React, Angular, Vue) or mobile clients to see how these authentication methods work in real-world scenarios.

You can find the full source code on our GitHub.

=====

That's just the basics. If you need more deep learning about Python and the frameworks, you can take the following cheap course:

Thanks!