$ cat /posts/drf-authentication-token-jwt-and-oauth2.md

DRF Authentication: Token, JWT, and OAuth2

drwxr-xr-x2026-01-235 min0 views
DRF Authentication: Token, JWT, and OAuth2

Authentication verifies user identity in REST APIs ensuring only authorized clients access protected endpoints preventing unauthorized data access, modification, or deletion. Without proper authentication, APIs remain vulnerable to abuse, data breaches, and security exploits as anyone can make requests pretending to be legitimate users. Django REST Framework provides multiple authentication schemes including Session Authentication reusing Django's session system, Token Authentication generating persistent tokens for stateless authentication, JSON Web Tokens (JWT) creating self-contained signed tokens with expiration, and OAuth2 enabling third-party authentication through providers like Google, GitHub, or Facebook. Each authentication method offers different tradeoffs between security, complexity, scalability, and client requirements with stateless token-based approaches preferred for mobile apps and single-page applications while session authentication suits traditional web applications. This comprehensive guide explores DRF authentication including understanding authentication vs authorization concepts, implementing Token Authentication with built-in TokenAuthentication class, configuring JWT authentication with djangorestframework-simplejwt, setting up OAuth2 with social authentication providers, combining multiple authentication methods, securing API endpoints with authentication classes, handling authentication in ViewSets, implementing custom authentication backends, managing token expiration and refresh, and best practices for API security. Mastering DRF authentication enables building secure APIs protecting user data while providing seamless authentication experiences across web and mobile platforms throughout Django REST Framework development integrated with Django security features.

Authentication Fundamentals

Authentication answers the question 'Who are you?' by verifying credentials like usernames and passwords or validating tokens proving user identity. Authorization then answers 'What can you do?' determining which resources authenticated users can access based on permissions and roles. DRF separates these concerns with authentication classes verifying identity and permission classes controlling access enabling flexible security policies. Authentication happens before permission checks in DRF's request processing pipeline with authentication classes populating request.user and request.auth attributes used by subsequent permission checks and view logic. Understanding this flow from credential submission through authentication validation to permission enforcement enables implementing comprehensive API security properly protecting sensitive endpoints while allowing public access where appropriate.

Authentication TypeStateStorageBest ForComplexity
SessionStatefulServer-side sessionsWeb applications, same-domainLow
TokenStatelessDatabase tokensMobile apps, SPAsLow
JWTStatelessClient-side onlyMicroservices, distributed systemsMedium
OAuth2StatelessProvider tokensThird-party login, social authHigh
pythonauthentication_setup.py
# settings.py - Global authentication configuration
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'rest_framework',
    'rest_framework.authtoken',  # For Token Authentication
]

REST_FRAMEWORK = {
    # Default authentication classes for all views
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    ],
    
    # Require authentication by default
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
}

# Run migrations to create token table
# python manage.py migrate
Authentication verifies identity (login), while authorization determines permissions (access control). Always implement both layers - authentication alone doesn't protect resources without proper authorization checks.

Token Authentication

Token Authentication generates unique tokens for authenticated users storing them in the database and requiring clients to include tokens in request headers for subsequent API calls. This stateless approach works perfectly for mobile apps and single-page applications avoiding CSRF complexity while maintaining simplicity. DRF's built-in TokenAuthentication provides one persistent token per user created upon registration or first login remaining valid indefinitely unless explicitly deleted. Tokens authenticate requests through the Authorization header using format 'Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' with DRF automatically associating tokens with user accounts enabling seamless integration with Django's user model.

pythontoken_authentication.py
# Token Authentication Implementation

# 1. Add to settings.py
INSTALLED_APPS = [
    'rest_framework.authtoken',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',
    ],
}

# 2. Create tokens for existing users
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token

# Create token for specific user
user = User.objects.get(username='john')
token = Token.objects.create(user=user)
print(token.key)  # '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'

# Or get existing token
token, created = Token.objects.get_or_create(user=user)

# 3. Login view to obtain token
from rest_framework.authtoken.views import obtain_auth_token
from django.urls import path

urlpatterns = [
    path('api/login/', obtain_auth_token, name='api-login'),
]

# Request:
# POST /api/login/
# {"username": "john", "password": "password123"}

# Response:
# {"token": "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"}

# 4. Custom login view with additional user data
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework import status
from django.contrib.auth import authenticate
from rest_framework.authtoken.models import Token

@api_view(['POST'])
@permission_classes([AllowAny])
def login(request):
    username = request.data.get('username')
    password = request.data.get('password')
    
    user = authenticate(username=username, password=password)
    
    if user:
        token, created = Token.objects.get_or_create(user=user)
        return Response({
            'token': token.key,
            'user_id': user.id,
            'username': user.username,
            'email': user.email
        })
    
    return Response(
        {'error': 'Invalid credentials'},
        status=status.HTTP_401_UNAUTHORIZED
    )

# 5. Register view creating user and token
@api_view(['POST'])
@permission_classes([AllowAny])
def register(request):
    username = request.data.get('username')
    password = request.data.get('password')
    email = request.data.get('email')
    
    if User.objects.filter(username=username).exists():
        return Response(
            {'error': 'Username already exists'},
            status=status.HTTP_400_BAD_REQUEST
        )
    
    user = User.objects.create_user(
        username=username,
        password=password,
        email=email
    )
    
    token = Token.objects.create(user=user)
    
    return Response({
        'token': token.key,
        'user_id': user.id,
        'username': user.username
    }, status=status.HTTP_201_CREATED)

# 6. Logout view (delete token)
@api_view(['POST'])
def logout(request):
    # Delete the user's token
    request.user.auth_token.delete()
    return Response({'message': 'Logged out successfully'})

# 7. Using token in client requests
"""
// JavaScript example
fetch('http://api.example.com/api/articles/', {
    method: 'GET',
    headers: {
        'Authorization': 'Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b',
        'Content-Type': 'application/json'
    }
});

# Python requests example
import requests

headers = {
    'Authorization': 'Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'
}

response = requests.get(
    'http://api.example.com/api/articles/',
    headers=headers
)
"""

# 8. Protected view using token authentication
from rest_framework.decorators import api_view, authentication_classes, permission_classes
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated

@api_view(['GET'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def protected_view(request):
    return Response({
        'message': f'Hello {request.user.username}!',
        'user_id': request.user.id
    })

JWT Authentication

JSON Web Tokens (JWT) are self-contained cryptographically signed tokens encoding user information, expiration times, and claims eliminating database lookups for authentication. Unlike basic Token Authentication requiring database queries to validate tokens, JWT validation happens through signature verification using secret keys enabling truly stateless authentication ideal for microservices and distributed systems. JWT tokens consist of three parts: header specifying algorithm, payload containing user data and claims, and signature ensuring token integrity. The djangorestframework-simplejwt package provides production-ready JWT implementation with access tokens for API requests, refresh tokens for obtaining new access tokens, and configurable expiration times. JWT integrates seamlessly with DRF views and serializers providing drop-in authentication for existing APIs.

pythonjwt_authentication.py
# JWT Authentication with djangorestframework-simplejwt

# 1. Installation
# pip install djangorestframework-simplejwt

# 2. Configure settings.py
from datetime import timedelta

INSTALLED_APPS = [
    'rest_framework',
    'rest_framework_simplejwt',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
}

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
    'ROTATE_REFRESH_TOKENS': True,
    'BLACKLIST_AFTER_ROTATION': True,
    'UPDATE_LAST_LOGIN': True,
    
    'ALGORITHM': 'HS256',
    'SIGNING_KEY': SECRET_KEY,
    'VERIFYING_KEY': None,
    
    'AUTH_HEADER_TYPES': ('Bearer',),
    'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
    'USER_ID_FIELD': 'id',
    'USER_ID_CLAIM': 'user_id',
    
    'TOKEN_TYPE_CLAIM': 'token_type',
}

# 3. URL configuration
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
    TokenVerifyView,
)
from django.urls import path

urlpatterns = [
    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'),
]

# 4. Obtaining JWT tokens
"""
# Request: POST /api/token/
{
    "username": "john",
    "password": "password123"
}

# Response:
{
    "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
    "refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}
"""

# 5. Using access token in requests
"""
// JavaScript
fetch('http://api.example.com/api/articles/', {
    headers: {
        'Authorization': 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...'
    }
});
"""

# 6. Refreshing access token
"""
# Request: POST /api/token/refresh/
{
    "refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}

# Response:
{
    "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."  # New access token
}
"""

# 7. Custom JWT claims
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView

class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user):
        token = super().get_token(user)
        
        # Add custom claims
        token['username'] = user.username
        token['email'] = user.email
        token['is_staff'] = user.is_staff
        
        return token

class CustomTokenObtainPairView(TokenObtainPairView):
    serializer_class = CustomTokenObtainPairSerializer

# Update urls.py
urlpatterns = [
    path('api/token/', CustomTokenObtainPairView.as_view(), name='token_obtain_pair'),
]

# 8. Protected view with JWT
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def protected_view(request):
    # request.user automatically populated from JWT
    return Response({
        'message': f'Hello {request.user.username}!',
        'user_id': request.user.id
    })

# 9. Token blacklisting (for logout)
INSTALLED_APPS = [
    'rest_framework_simplejwt.token_blacklist',
]

from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework.decorators import api_view
from rest_framework.response import Response

@api_view(['POST'])
def logout(request):
    try:
        refresh_token = request.data.get('refresh')
        token = RefreshToken(refresh_token)
        token.blacklist()  # Add to blacklist
        return Response({'message': 'Logged out successfully'})
    except Exception as e:
        return Response({'error': str(e)}, status=400)
JWT PartContentEncodingPurpose
Header{"alg": "HS256", "typ": "JWT"}Base64 URLAlgorithm and token type
Payload{"user_id": 1, "exp": 1234567890}Base64 URLClaims and user data
SignatureHMACSHA256(header + payload + secret)Base64 URLToken verification
JWT tokens cannot be invalidated before expiration unless using token blacklisting. Keep ACCESS_TOKEN_LIFETIME short (15-60 minutes) and use refresh tokens for extended sessions. Never store sensitive data in JWT payload as it's only encoded, not encrypted.

OAuth2 and Social Authentication

OAuth2 enables users to authenticate through third-party providers like Google, Facebook, GitHub, or Twitter eliminating password management while leveraging trusted identity providers. Social authentication improves user experience reducing registration friction and providing seamless sign-in experiences familiar to users. The django-allauth package integrates OAuth2 providers with Django and DRF handling authentication flows, token management, and user profile creation automatically. OAuth2 involves complex multi-step flows where users authorize your application to access their provider account with providers issuing access tokens your API validates for authentication. This approach works excellently for consumer-facing applications where users already have provider accounts integrated with Django's session management.

pythonoauth2_authentication.py
# OAuth2 with django-allauth and dj-rest-auth

# 1. Installation
# pip install django-allauth dj-rest-auth

# 2. Configure settings.py
INSTALLED_APPS = [
    'django.contrib.sites',
    'allauth',
    'allauth.account',
    'allauth.socialaccount',
    'allauth.socialaccount.providers.google',
    'allauth.socialaccount.providers.github',
    'allauth.socialaccount.providers.facebook',
    'dj_rest_auth',
    'dj_rest_auth.registration',
]

SITE_ID = 1

# AllAuth settings
AUTHENTICATION_BACKENDS = [
    'django.contrib.auth.backends.ModelBackend',
    'allauth.account.auth_backends.AuthenticationBackend',
]

ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_AUTHENTICATION_METHOD = 'email'

# Social provider settings
SOCIALACCOUNT_PROVIDERS = {
    'google': {
        'SCOPE': [
            'profile',
            'email',
        ],
        'AUTH_PARAMS': {
            'access_type': 'online',
        }
    },
    'github': {
        'SCOPE': [
            'user',
            'repo',
            'read:org',
        ],
    }
}

# 3. URL configuration
from django.urls import path, include

urlpatterns = [
    path('api/auth/', include('dj_rest_auth.urls')),
    path('api/auth/registration/', include('dj_rest_auth.registration.urls')),
    path('api/auth/social/', include('allauth.socialaccount.urls')),
]

# 4. Configure OAuth providers in Django admin
"""
1. Run migrations: python manage.py migrate
2. Create superuser: python manage.py createsuperuser
3. Access admin: http://localhost:8000/admin/
4. Add Social Applications:
   - Navigate to: Social applications
   - Add application for each provider:
     * Provider: Google/GitHub/Facebook
     * Name: Your App Name
     * Client ID: from provider console
     * Secret key: from provider console
     * Sites: Select your site
"""

# 5. Google OAuth2 setup
"""
Google Cloud Console (console.cloud.google.com):
1. Create project
2. Enable Google+ API
3. Create OAuth2 credentials
4. Set authorized redirect URIs:
   - http://localhost:8000/api/auth/social/google/callback/
   - https://yourdomain.com/api/auth/social/google/callback/
5. Copy Client ID and Client Secret
"""

# 6. Frontend OAuth flow (JavaScript)
"""
// 1. Redirect user to provider
window.location.href = 'http://localhost:8000/api/auth/social/google/';

// 2. User authorizes on Google
// 3. Redirect back to your callback URL with code
// 4. Exchange code for tokens
fetch('http://localhost:8000/api/auth/social/google/', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({
        code: authorizationCode,
        redirect_uri: 'http://localhost:3000/callback/'
    })
})
.then(response => response.json())
.then(data => {
    // Store access token
    localStorage.setItem('token', data.access_token);
});
"""

# 7. Custom social authentication view
from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter
from dj_rest_auth.registration.views import SocialLoginView

class GoogleLogin(SocialLoginView):
    adapter_class = GoogleOAuth2Adapter

# urls.py
urlpatterns = [
    path('api/auth/google/', GoogleLogin.as_view(), name='google_login'),
]

Combining Authentication Methods

Real-world APIs often support multiple authentication methods simultaneously allowing different clients to authenticate using their preferred mechanism. DRF evaluates authentication classes in order returning the first successful authentication or anonymous user if all fail. Combining Session Authentication for web browsing, Token or JWT for mobile apps, and OAuth2 for social login creates flexible APIs supporting diverse client types. This multi-authentication approach requires careful consideration of CSRF protection disabling it for token-based authentication while maintaining it for session-based requests ensuring security across authentication types.

pythonmultiple_authentication.py
# Multiple authentication methods
from rest_framework.authentication import SessionAuthentication, TokenAuthentication
from rest_framework_simplejwt.authentication import JWTAuthentication

# settings.py - Global configuration
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',  # For browsable API
        'rest_framework_simplejwt.authentication.JWTAuthentication',  # For mobile/SPA
        'rest_framework.authentication.TokenAuthentication',  # Fallback
    ],
}

# Per-view authentication
from rest_framework.decorators import api_view, authentication_classes
from rest_framework.response import Response

@api_view(['GET'])
@authentication_classes([JWTAuthentication, TokenAuthentication])
def api_view_example(request):
    # Accepts JWT or Token authentication only
    return Response({'user': request.user.username})

# ViewSet authentication
from rest_framework import viewsets
from rest_framework.authentication import SessionAuthentication, TokenAuthentication

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    authentication_classes = [SessionAuthentication, TokenAuthentication]

# CSRF exemption for token authentication
from rest_framework.authentication import SessionAuthentication

class CsrfExemptSessionAuthentication(SessionAuthentication):
    def enforce_csrf(self, request):
        return  # Disable CSRF for this authentication

# Use in views that need session auth without CSRF
class MyView(APIView):
    authentication_classes = [CsrfExemptSessionAuthentication, TokenAuthentication]

Authentication Best Practices

  • Use HTTPS everywhere: Always enforce HTTPS in production preventing token interception through man-in-the-middle attacks
  • Choose appropriate token lifetime: Keep access tokens short-lived (15-60 minutes) using refresh tokens for extended sessions
  • Implement token refresh: Use refresh tokens enabling users to stay authenticated without re-login while maintaining security
  • Store tokens securely: Use httpOnly cookies or secure storage preventing XSS attacks from stealing tokens
  • Validate token signatures: Always verify JWT signatures preventing token tampering and unauthorized access
  • Implement logout properly: Delete tokens server-side or use blacklisting preventing reuse of invalidated tokens
  • Rate limit authentication endpoints: Protect login endpoints with throttling preventing brute force attacks
  • Return generic error messages: Avoid revealing whether username or password was incorrect preventing user enumeration
  • Monitor authentication failures: Log failed authentication attempts detecting suspicious activity and potential attacks
  • Test authentication thoroughly: Verify authentication works correctly testing token validation, expiration, and edge cases
Authentication is only one layer of security. Combine it with proper permissions, input validation, HTTPS, rate limiting, and monitoring for comprehensive API security following security best practices.

Conclusion

Django REST Framework provides comprehensive authentication options from Session Authentication reusing Django's built-in session system to Token Authentication generating persistent database-stored tokens ideal for mobile applications. JWT authentication offers truly stateless authentication through self-contained cryptographically signed tokens enabling distributed systems and microservices architectures without database lookups. OAuth2 integration through django-allauth enables social authentication leveraging trusted providers like Google, GitHub, and Facebook improving user experience while eliminating password management complexities. Each authentication method offers distinct tradeoffs with Session Authentication best for traditional web applications, Token Authentication perfect for mobile apps and SPAs, JWT excelling in distributed systems, and OAuth2 providing seamless third-party login experiences. Multiple authentication methods can coexist in single APIs with DRF evaluating authentication classes in order supporting diverse client types from web browsers to mobile applications. Proper authentication implementation requires HTTPS enforcement, appropriate token lifetimes with refresh mechanisms, secure token storage preventing XSS attacks, signature validation preventing tampering, proper logout with token deletion or blacklisting, rate limiting protecting against brute force, generic error messages preventing user enumeration, authentication failure monitoring detecting attacks, and thorough testing verifying correct behavior. Understanding authentication fundamentals distinguishing authentication verifying identity from authorization controlling access enables implementing layered security protecting API resources. Mastering DRF authentication enables building secure APIs protecting user data while providing seamless authentication experiences across platforms integrated with ViewSets, serializers, and core DRF concepts throughout API development maintaining security standards from development through production deployment.

$ cat /comments/ (0)

new_comment.sh

// Email hidden from public

>_

$ cat /comments/

// No comments found. Be the first!

[session] guest@{codershandbook}[timestamp] 2026

Navigation

Connect

Subscribe

// 2026 {Coders Handbook}. EOF.