Skip to main content

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/ or password-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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_auth_system-0.1.2.tar.gz (26.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_auth_system-0.1.2-py3-none-any.whl (33.6 kB view details)

Uploaded Python 3

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

Hashes for django_auth_system-0.1.2.tar.gz
Algorithm Hash digest
SHA256 5c6079eb2d1bd155b2b0b95c87897934e2cc3a48adb2e49b693e8d9f966f5502
MD5 d069fa038c8230f11fd0b6abda539af5
BLAKE2b-256 ad32dbae020b0229b43566f35d0e2aba4e43f627b9311f7f9af770d875a00211

See more details on using hashes here.

File details

Details for the file django_auth_system-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for django_auth_system-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 a2e70a5dafe5c379202721dfb2653eb0236516a8f1e7079197ac73a367ce65b6
MD5 fac5ee4526f93d2159b7071e7f885474
BLAKE2b-256 df7e7fe84b1f3f727cb967fce145dba91f76b10abf3f6e30fe4aa8b63fe0347a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page