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 Type | State | Storage | Best For | Complexity |
|---|---|---|---|---|
| Session | Stateful | Server-side sessions | Web applications, same-domain | Low |
| Token | Stateless | Database tokens | Mobile apps, SPAs | Low |
| JWT | Stateless | Client-side only | Microservices, distributed systems | Medium |
| OAuth2 | Stateless | Provider tokens | Third-party login, social auth | High |
# 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 migrateToken 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.
# 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.
# 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 Part | Content | Encoding | Purpose |
|---|---|---|---|
| Header | {"alg": "HS256", "typ": "JWT"} | Base64 URL | Algorithm and token type |
| Payload | {"user_id": 1, "exp": 1234567890} | Base64 URL | Claims and user data |
| Signature | HMACSHA256(header + payload + secret) | Base64 URL | Token verification |
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.
# 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.
# 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
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.
$ share --platform
$ cat /comments/ (0)
$ cat /comments/
// No comments found. Be the first!


