DJANGO Multi-Authentication PowerUser Guide (Python)
Last updated: December 09, 2025
Author: Paul Namalomba
- SESKA Computational Engineer
- Software Developer
- PhD Candidate (Civil Engineering Spec. Computational and Applied Mechanics)
Contact: kabwenzenamalomba@gmail.com
Website: paulnamalomba.github.io
Overview
This guide focuses on implementing robust multi-authentication in Django using Python: combining username/password, OAuth2/OIDC (Google, Azure AD, GitHub), JWT for APIs, and optional SAML and MFA. It shows how to structure settings, authentication backends, URL routing, and views so that you can safely support multiple identity providers in one project.
Contents
- DJANGO Multi-Authentication PowerUser Guide (Python)
- Overview
- Contents
- Quickstart
- Key Concepts
- Configuration and Best Practices
- Security Considerations
- Examples
- Troubleshooting
- Performance and Tuning
- References and Further Reading
Quickstart
Install and Scaffold Project
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install "Django>=4.2,<5" djangorestframework
django-admin startproject config .
python manage.py startapp accounts
Core Dependencies
For multi-auth, you commonly combine:
pip install \
django-allauth \
djangorestframework-simplejwt \
django-otp django-otp-yubikey \
python3-saml
Key Concepts
Authentication Backends
In settings.py:
AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend", # Username/password
"allauth.account.auth_backends.AuthenticationBackend", # Social auth (Google, GitHub, etc.)
]
- Order matters; Django will try backends sequentially.
- Custom backends can enforce additional constraints (tenant, domain, flags).
Session vs Token Auth
- Session auth: Browser-centric; server-side session and CSRF protection.
- Token/JWT auth: API-centric; stateless bearer tokens with short lifetimes.
- Hybrid: Same user base, multiple auth methods (session for UI, JWT for API).
Configuration and Best Practices
Settings Layout
A typical production layout:
# config/settings/base.py
from pathlib import Path
import os
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
DEBUG = os.environ.get("DJANGO_DEBUG", "False") == "True"
ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "").split(",")
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
# Third-party
"rest_framework",
"rest_framework_simplejwt",
"allauth",
"allauth.account",
"allauth.socialaccount",
"allauth.socialaccount.providers.google",
"otp",
"otp_totp",
# Local
"accounts",
]
REST framework and JWT:
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework_simplejwt.authentication.JWTAuthentication",
"rest_framework.authentication.SessionAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
}
from datetime import timedelta
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=15),
"REFRESH_TOKEN_LIFETIME": timedelta(days=30),
"ROTATE_REFRESH_TOKENS": True,
"BLACKLIST_AFTER_ROTATION": True,
}
Environment Variables
export DJANGO_SECRET_KEY="<long-random-secret>"
export DJANGO_DEBUG="False"
export DJANGO_ALLOWED_HOSTS="localhost,api.example.com"
# Google OAuth2
export SOCIAL_AUTH_GOOGLE_CLIENT_ID="<client-id>"
export SOCIAL_AUTH_GOOGLE_CLIENT_SECRET="<client-secret>"
# Azure AD
export SOCIAL_AUTH_AZUREAD_TENANT_ID="<tenant-id>"
export SOCIAL_AUTH_AZUREAD_CLIENT_ID="<client-id>"
export SOCIAL_AUTH_AZUREAD_CLIENT_SECRET="<client-secret>"
# JWT
export JWT_SIGNING_KEY="<jwt-signing-key>"
Security Considerations
Password Storage and MFA
- Always use Django’s built-in password hashing (
PBKDF2by default) or stronger algorithms (Argon2). - Enforce strong password policies and rate limit login attempts.
- Use MFA for admin and privileged roles (via
django-otpor external IdPs).
Example: enabling Argon2:
PASSWORD_HASHERS = [
"django.contrib.auth.hashers.Argon2PasswordHasher",
"django.contrib.auth.hashers.PBKDF2PasswordHasher",
]
Token Handling
- Keep access tokens short-lived; use refresh tokens for continuity.
- Use HTTPS everywhere; never transmit tokens over HTTP.
- Store JWT signing keys securely (Key Vault, environment variables, or files with restricted permissions).
Examples
Example 1: Base Auth Setup
accounts/models.py:
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
# Example extension: tenant field
tenant_id = models.CharField(max_length=64, blank=True, null=True)
config/settings/base.py:
AUTH_USER_MODEL = "accounts.User"
LOGIN_REDIRECT_URL = "/"
LOGOUT_REDIRECT_URL = "/"
URLs:
# config/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("accounts/", include("allauth.urls")),
path("api/auth/", include("accounts.api_urls")),
]
Example 2: OAuth2/OIDC with Social Login
config/settings/base.py (allauth configuration):
SITE_ID = 1
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
ACCOUNT_AUTHENTICATION_METHOD = "username_email"
SOCIALACCOUNT_PROVIDERS = {
"google": {
"APP": {
"client_id": os.environ.get("SOCIAL_AUTH_GOOGLE_CLIENT_ID"),
"secret": os.environ.get("SOCIAL_AUTH_GOOGLE_CLIENT_SECRET"),
"key": "",
}
}
}
Template snippet for login options:
<a href="{% provider_login_url 'google' %}">Sign in with Google</a>
Example 3: JWT for APIs
accounts/api_urls.py:
from django.urls import path
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
urlpatterns = [
path("token/", TokenObtainPairView.as_view(), name="token_obtain_pair"),
path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),
]
Example API view:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
class ProfileView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
user = request.user
return Response({
"username": user.username,
"email": user.email,
"auth": request.auth.__class__.__name__ if request.auth else "session",
})
Example 4: MFA with django-otp
config/settings/base.py:
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"otp.middleware.OTPMiddleware",
]
Simple view enforcing OTP:
from django_otp.decorators import otp_required
from django.http import HttpResponse
@otp_required
def sensitive_view(request):
return HttpResponse("You passed MFA and can access this sensitive view.")
Troubleshooting
Common Configuration Errors
- Improper redirect URIs: Ensure configured redirect URIs at the IdP match Django’s URLs.
- CSRF token missing: For session-based auth, ensure CSRF middleware and template tags are correctly set up.
- JWT not accepted: Check audience (
aud) and issuer (iss) claims, and that signing keys match.
Debugging Tips
- Enable Django’s debug toolbar in non-production environments.
- Log authentication attempts and failures with enough context (user, provider, IP).
- Use
python -m http.serveror tools likehttpieto simulate callback requests.
Performance and Tuning
Scaling Auth-heavy APIs
- Cache frequently accessed user/permission data in Redis.
- Use database connection pooling for high login volumes.
- Offload authentication to an external gateway (e.g. API Gateway or identity proxy) where appropriate.
References and Further Reading
- Django Authentication: https://docs.djangoproject.com/en/4.2/topics/auth/
- Django REST Framework: https://www.django-rest-framework.org/
- django-allauth: https://django-allauth.readthedocs.io/
- django-otp: https://django-otp-official.readthedocs.io/
- SimpleJWT: https://django-rest-framework-simplejwt.readthedocs.io/