A pluggable Django authentication system with JWT cookies, email verification, password reset, and TOTP-based 2FA with backup codes.
Project description
django-auth-system
A pluggable Django authentication system with JWT cookies, email verification, password reset, and TOTP-based 2FA with backup codes.
Built on Django REST Framework + SimpleJWT — designed for cookie-based SPA / mobile clients.
Features
- Signup with email + password, auto-deactivate until email is verified
- Email verification via token link
- Login — returns JWT in HttpOnly cookies (or hands off to 2FA)
- Logout — blacklists the refresh token, clears cookies
- Token refresh — silent refresh via the refresh-token cookie
- Password reset — request + confirm (token-based)
- Change password — authenticated user changes own password
- Change email — request + confirm (token-based)
- TOTP 2FA — setup via QR code, enable/disable, backup codes
- Backup codes — generate, list, consume, regenerate (10 codes by default)
- Pending session — Redis-backed 2FA handoff (5-minute TTL)
- Django admin integration — manage 2FA per user
- OpenAPI schema — drf-spectacular extension for cookie-JWT auth
- Email authentication backend — authenticate by email, not username
- Rate limiting — login, signup, password-reset, and 2FA-verify throttles
- Session invalidation — password/email change invalidates all existing JWT tokens
- Secure email change — pending email stored in Redis, not in URL params
- Configurable session hash — optional
update_session_auth_hash()support
Requirements
| Dependency | Version |
|---|---|
| Python | 3.10+ |
| Django | 4.2+ |
| djangorestframework | 3.14+ |
| djangorestframework-simplejwt | 5.3+ |
| pyotp | 2.8+ |
| qrcode | 7.4+ |
| redis | 5.0+ |
| drf-spectacular | 0.27+ |
Installation
pip install django-auth-system
Add to INSTALLED_APPS:
INSTALLED_APPS = [
...
"rest_framework",
"rest_framework_simplejwt",
"drf_spectacular",
"auth_system",
...
]
Include the URLs:
# urls.py
urlpatterns = [
path("api/auth/", include("auth_system.urls")),
]
Run migrations:
python manage.py migrate auth_system
Configuration
All settings live under the AUTH_SYSTEM dict in your settings.py:
AUTH_SYSTEM = {
# JWT
"JWT_ACCESS_TOKEN_LIFETIME": timedelta(minutes=15),
"JWT_REFRESH_TOKEN_LIFETIME": timedelta(days=7),
"JWT_ACCESS_COOKIE_NAME": "access_token",
"JWT_REFRESH_COOKIE_NAME": "refresh_token",
"JWT_COOKIE_SECURE": True, # set False for local dev over HTTP
"JWT_COOKIE_SAMESITE": "Lax",
"JWT_COOKIE_HTTPONLY": True,
# Redis
"REDIS_URL": "redis://127.0.0.1:6379/0",
"PENDING_SESSION_TTL": 300, # seconds (2FA handoff window)
"PENDING_SESSION_PREFIX": "auth:pending:",
"PENDING_EMAIL_TTL": 3600, # seconds (email change window)
# 2FA
"TOTP_ISSUER_NAME": "AuthSystem",
"BACKUP_CODES_COUNT": 10,
# Session
"UPDATE_SESSION_AUTH_HASH": False, # set True for session-based auth clients
# Rate limiting — set None to disable
"LOGIN_THROTTLE_RATES": {
"login": "10/min",
"signup": "5/min",
"password_reset": "5/min",
"verify_2fa": "10/min",
},
}
You also need standard Django settings:
AUTH_USER_MODEL = "your_app.User" # or django's default
FRONTEND_URL = "https://your-frontend.com"
DEFAULT_FROM_EMAIL = "noreply@your-domain.com"
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
Authentication backends
Add the custom email-based authentication backend (if your User model uses email as the unique identifier):
AUTHENTICATION_BACKENDS = [
"auth_system.authentication.EmailAuthBackend",
"django.contrib.auth.backends.ModelBackend",
]
DRF settings
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"auth_system.authentication.CookieJWTAuthentication",
],
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
}
SimpleJWT (required for token blacklisting)
INSTALLED_APPS += ["rest_framework_simplejwt.token_blacklist"]
API Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/auth/signup/ |
public | Create account |
| GET | /api/auth/verify-account/?uid=&token= |
public | Verify email |
| POST | /api/auth/login/ |
public | Login (password) |
| POST | /api/auth/logout/ |
cookie JWT | Logout, blacklist token |
| POST | /api/auth/refresh/ |
refresh cookie | Refresh access token |
| POST | /api/auth/password-reset/ |
public | Request password reset |
| POST | /api/auth/password-reset-confirm/ |
public | Confirm password reset |
| POST | /api/auth/change-password/ |
cookie JWT | Change password |
| POST | /api/auth/change-email/ |
cookie JWT | Request email change |
| GET | /api/auth/change-email-confirm/ |
public | Confirm email change |
| POST | /api/auth/2fa/setup/ |
cookie JWT | Generate TOTP secret + QR |
| POST | /api/auth/2fa/enable/ |
cookie JWT | Enable 2FA (verify TOTP) |
| POST | /api/auth/2fa/disable/ |
cookie JWT | Disable 2FA (password + TOTP) |
| POST | /api/auth/2fa/verify-login/ |
public | Complete 2FA login step |
| GET | /api/auth/2fa/backup-codes/ |
cookie JWT | List backup codes |
| POST | /api/auth/2fa/backup-codes/ |
cookie JWT | Regenerate backup codes |
Session Invalidation
Each user's TwoFactor record has a token_version field (default 0). Every JWT token embeds this version. When any of these events occur, the version is incremented and all existing JWT tokens become invalid:
- Password changed (via
change-password/orpassword-reset-confirm/) - Email changed (via
change-email-confirm/)
This protects against token replay after sensitive account changes.
2FA Login Flow
┌──────────┐ POST /login/ ┌──────────┐
│ │ ──────────────────────► │ │
│ Client │ {email, password} │ Server │
│ │ ◄────────────────────── │ │
└──────────┘ {requires_2fa: true, └──────────┘
session_token: uuid}
┌──────────┐ POST /2fa/verify-login/ ┌──────────┐
│ │ ──────────────────────────► │ │
│ Client │ {session_token, code} │ Server │
│ │ ◄────────────────────────── │ │
└──────────┘ Set-Cookie: access_token └──────────┘
Set-Cookie: refresh_token
Development
git clone https://github.com/your-username/django-auth-system
cd django-auth-system
python -m venv .venv && .venv\Scripts\activate
pip install -e ".[dev]"
Running tests
pytest tests/ -v
Requires a local Redis instance on 127.0.0.1:6379 or override REDIS_URL in tests/settings.py.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_auth_system-0.1.2.tar.gz.
File metadata
- Download URL: django_auth_system-0.1.2.tar.gz
- Upload date:
- Size: 26.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5c6079eb2d1bd155b2b0b95c87897934e2cc3a48adb2e49b693e8d9f966f5502
|
|
| MD5 |
d069fa038c8230f11fd0b6abda539af5
|
|
| BLAKE2b-256 |
ad32dbae020b0229b43566f35d0e2aba4e43f627b9311f7f9af770d875a00211
|
File details
Details for the file django_auth_system-0.1.2-py3-none-any.whl.
File metadata
- Download URL: django_auth_system-0.1.2-py3-none-any.whl
- Upload date:
- Size: 33.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a2e70a5dafe5c379202721dfb2653eb0236516a8f1e7079197ac73a367ce65b6
|
|
| MD5 |
fac5ee4526f93d2159b7071e7f885474
|
|
| BLAKE2b-256 |
df7e7fe84b1f3f727cb967fce145dba91f76b10abf3f6e30fe4aa8b63fe0347a
|