JSON Web Token based authentication for Django REST framework
—
Essential functions for JWT token creation, validation, and payload management. These utilities handle the core JWT operations including encoding, decoding, payload generation, and secret key management.
Core functions for JWT token creation and validation.
def jwt_encode_handler(payload):
"""
Encodes a payload dictionary into a JWT token string.
Args:
payload (dict): JWT payload containing claims and user data
Returns:
str: Encoded JWT token
Note:
Uses configured algorithm, secret key, and signing options from settings.
"""
def jwt_decode_handler(token):
"""
Decodes and validates a JWT token string.
Args:
token (str): JWT token string to decode
Returns:
dict: Decoded payload containing claims and user data
Raises:
jwt.ExpiredSignature: Token has expired
jwt.DecodeError: Token is malformed or invalid
jwt.InvalidTokenError: Token signature is invalid
Note:
Validates expiration, signature, audience, and issuer based on settings.
"""Functions for creating and processing JWT payloads with user information.
def jwt_payload_handler(user):
"""
Creates a JWT payload from a Django user instance.
Args:
user: Django user model instance
Returns:
dict: JWT payload containing user claims and metadata
Payload Structure:
- user_id: User primary key (deprecated, use username field)
- username: User's username
- exp: Expiration timestamp
- email: User's email (if available, deprecated)
- orig_iat: Original issued-at time (if refresh enabled)
- aud: Audience claim (if configured)
- iss: Issuer claim (if configured)
- {username_field}: Username value using configured field name
Deprecation Warnings:
Issues warnings about future removal of 'email' and 'user_id' fields.
These fields will be removed in future versions.
Special Handling:
- UUID primary keys are converted to strings
- Username field is dynamically determined from User model
- Expiration calculated from JWT_EXPIRATION_DELTA setting
"""
def jwt_get_username_from_payload_handler(payload):
"""
Extracts username from JWT payload.
Args:
payload (dict): Decoded JWT payload
Returns:
str: Username from payload
"""
def jwt_get_user_id_from_payload_handler(payload):
"""
Extracts user ID from JWT payload (deprecated).
Args:
payload (dict): Decoded JWT payload
Returns:
Any: User ID from payload
Deprecated:
This function is deprecated and will be removed in future versions.
Use JWT_PAYLOAD_GET_USERNAME_HANDLER instead. Issues deprecation
warning when called.
"""Functions for managing JWT signing keys, including user-specific secret keys.
def jwt_get_secret_key(payload=None):
"""
Retrieves the secret key for JWT signing/verification.
Args:
payload (dict, optional): JWT payload for user-specific keys
Returns:
str: Secret key for JWT operations
Note:
- Returns user-specific key if JWT_GET_USER_SECRET_KEY is configured
- Falls back to JWT_SECRET_KEY setting
- Enables per-user token invalidation for enhanced security
"""Function for customizing JWT response payloads.
def jwt_response_payload_handler(token, user=None, request=None):
"""
Formats the response payload for JWT authentication endpoints.
Args:
token (str): Generated JWT token
user (User, optional): Authenticated user instance
request (HttpRequest, optional): Current request object
Returns:
dict: Response data sent to client
Default Response:
{'token': token}
Customization Example:
return {
'token': token,
'user': UserSerializer(user).data,
'expires': payload['exp']
}
"""from rest_framework_jwt.utils import jwt_payload_handler
from datetime import datetime, timedelta
def custom_jwt_payload_handler(user):
"""Add custom claims to JWT payload."""
payload = jwt_payload_handler(user)
# Add custom claims
payload.update({
'role': user.role,
'permissions': list(user.permissions.values_list('name', flat=True)),
'organization': user.organization.id if user.organization else None,
})
return payload
# Configure in settings
JWT_AUTH = {
'JWT_PAYLOAD_HANDLER': 'myapp.utils.custom_jwt_payload_handler',
}def custom_jwt_response_handler(token, user=None, request=None):
"""Include user data in JWT response."""
return {
'token': token,
'user': {
'id': user.id,
'username': user.username,
'email': user.email,
'full_name': user.get_full_name(),
},
'expires_in': 300, # Token lifetime in seconds
}
# Configure in settings
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER': 'myapp.utils.custom_jwt_response_handler',
}def get_user_secret_key(user):
"""Generate user-specific secret key for token invalidation."""
# Use user's password hash as part of secret
# Tokens become invalid when password changes
return f"{settings.SECRET_KEY}:{user.password}"
# Configure in settings
JWT_AUTH = {
'JWT_GET_USER_SECRET_KEY': 'myapp.utils.get_user_secret_key',
}from rest_framework_jwt.utils import (
jwt_payload_handler, jwt_encode_handler, jwt_decode_handler
)
from django.contrib.auth import get_user_model
User = get_user_model()
# Create token manually
user = User.objects.get(username='testuser')
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
# Validate token manually
try:
decoded_payload = jwt_decode_handler(token)
username = decoded_payload['username']
print(f"Token valid for user: {username}")
except Exception as e:
print(f"Token validation failed: {e}")These utilities are configured through the JWT_AUTH settings dictionary:
JWT_AUTH = {
# Handler Functions
'JWT_PAYLOAD_HANDLER': 'rest_framework_jwt.utils.jwt_payload_handler',
'JWT_ENCODE_HANDLER': 'rest_framework_jwt.utils.jwt_encode_handler',
'JWT_DECODE_HANDLER': 'rest_framework_jwt.utils.jwt_decode_handler',
'JWT_RESPONSE_PAYLOAD_HANDLER': 'rest_framework_jwt.utils.jwt_response_payload_handler',
# Secret Key Options
'JWT_SECRET_KEY': settings.SECRET_KEY,
'JWT_GET_USER_SECRET_KEY': None, # For user-specific keys
# Token Settings
'JWT_ALGORITHM': 'HS256',
'JWT_EXPIRATION_DELTA': timedelta(seconds=300),
'JWT_ALLOW_REFRESH': False,
# Verification Options
'JWT_VERIFY': True,
'JWT_VERIFY_EXPIRATION': True,
'JWT_LEEWAY': 0,
# Claims
'JWT_AUDIENCE': None,
'JWT_ISSUER': None,
}The utility functions handle various JWT-related errors:
import jwt
from rest_framework.exceptions import AuthenticationFailed
# Common errors from jwt_decode_handler:
try:
payload = jwt_decode_handler(token)
except jwt.ExpiredSignature:
# Token has expired
pass
except jwt.DecodeError:
# Token is malformed
pass
except jwt.InvalidTokenError:
# Invalid signature or claims
passThese errors are typically caught by authentication classes and converted to appropriate HTTP responses.
Install with Tessl CLI
npx tessl i tessl/pypi-djangorestframework-jwt